1 /*---------------------------------------------------------------------------*
2 Project: MP library
3 File: MPSync.h
4
5 Copyright (C) 2010-2011 Nintendo. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12 *---------------------------------------------------------------------------*/
13
14 /*
15 MPSpinLock is an inline smp spinlock implementation, which has very low
16 overhead. This basic spinlock must be used carefully. The intended use is to
17 guard access to shared memory between equal priority threads each running on a
18 different core.
19
20 1) It is possible to get a deadlock condition. If thread holding a spinlock
21 gets preempted by a higher priority thread on the same core who wants the same
22 spinlock.
23
24 2) It is possible to get slower performance than expected. If a thread holding
25 a spinlock gets preempted other cores will have to wait until the holding
26 thread resumes and releases the lock.
27
28 Important usage notes while holding a spinlock:
29 - Do not cause a thread to sleep (for example, OSSleep)
30 - Do not cause a thread rescheduling
31 - Do not share a spinlock between threads of different priorities
32 - Do not do any I/O while holding the spinlock
33
34 OSSpinlock is designed to handle thread priority inversion issues. However,
35 OSSpinlock still busy-waits, if a thread wants to sleep one needs to use an
36 OSMutex.
37 */
38
39 #ifndef __MPSYNC_H__
40 #define __MPSYNC_H__
41
42 #include <types.h>
43 #include <cafe/os/OSAtomic.h>
44 #include <cafe/os/OSTime.h>
45
46 #ifdef __cplusplus
47 extern "C" {
48 #endif
49
50 typedef struct
51 {
52 OSAtomicVar var;
53 } MPSpinLock;
54
MPInitSpinLock(MPSpinLock * spinlock)55 inline void MPInitSpinLock(MPSpinLock *spinlock)
56 {
57 spinlock->var.u.u32 = 0;
58 }
59
MPAcquireSpinLock(MPSpinLock * spinlock)60 inline void MPAcquireSpinLock(MPSpinLock *spinlock)
61 {
62 while(1)
63 {
64 if(OSCompareAndSwapAtomic(&spinlock->var, 0, 1))
65 {
66 __ISYNC();
67 break;
68 }
69 }
70 }
71
MPTryAcquireSpinLock(MPSpinLock * spinlock)72 inline BOOL MPTryAcquireSpinLock(MPSpinLock *spinlock)
73 {
74 if(OSCompareAndSwapAtomic(&spinlock->var, 0, 1))
75 {
76 __ISYNC();
77 return TRUE;
78 }
79
80 return FALSE;
81 }
82
MPReleaseSpinLock(MPSpinLock * spinlock)83 inline void MPReleaseSpinLock(MPSpinLock *spinlock)
84 {
85 // sync is used to ensure any memory changed under the lock is coherent
86 // before the lock is released.
87 __SYNC();
88 spinlock->var.u.u32 = 0;
89 }
90
91 #ifdef __cplusplus
92 }
93 #endif
94
95 #endif // __MPSYNC_H__
96