Imported Debian patch 0.12.7-3
[bowtie.git] / spinlock.h
1 #ifndef SPINLOCK_H_
2 #define SPINLOCK_H_
3
4 /**
5  * This non-reentrant spinlock implementation for i386 and x86_64 is
6  * based on free code by Gert Boddaert:
7  *
8  * http://www.codeproject.com/KB/threads/spinlocks.aspx
9  *
10  * Using spinlocks instead of the heavier pthreads mutexes can, in some
11  * cases, help Bowtie perform better for large numbers of threads.
12  */
13
14 // (If the user hasn't specified this, then there's no need for any
15 // kind of locking, so we skip this header)
16 #ifdef BOWTIE_PTHREADS
17
18 #if defined(__GNUC__)
19 #if defined(__x86_64__) || defined(__i386__)
20 #define USE_SPINLOCK
21 #endif
22 #endif
23
24 #ifdef USE_SPINLOCK
25
26 #if defined(__x86_64__)
27 #define SPINLOCK_WORD long
28 #else
29 #define SPINLOCK_WORD int
30 #endif
31
32 class SpinLock {
33   public:    // inlined constructor
34
35     // inlined NON-virtual destructor
36     inline SpinLock() : m_s(1) {}
37     inline ~SpinLock() {}
38
39     // enter the lock, spinlocks (with/without Sleep)
40     // when mutex is already locked
41     inline void Enter(void)
42     {
43         SPINLOCK_WORD prev_s;
44         do
45         {
46             prev_s = TestAndSet(&m_s, 0);
47             if (m_s == 0 && prev_s == 1)
48             {
49                 // The lock and was unlocked and we grabbed it
50                 break;
51             }
52             // reluinquish current timeslice (can only
53             // be used when OS available and
54             // we do NOT want to 'spin')
55             // HWSleep(0);
56         }
57         while (true);
58     }
59     // Tries to enter the lock, returns 0
60     // when mutex is already locked,
61     // returns != 0 when success
62     inline int TryEnter(void)
63     {
64         SPINLOCK_WORD prev_s = TestAndSet(&m_s, 0);
65         if (m_s == 0 && prev_s == 1)
66         {
67             return 1;
68         }
69         return 0;
70     }
71
72     // Leaves or unlocks the mutex
73     // (should only be called by lock owner)
74     inline void Leave(void)
75     {
76         TestAndSet(&m_s, 1);
77     }
78
79   protected:
80     // sets BIT value and returns previous
81     // value.in 1 atomic un-interruptable operation
82           SPINLOCK_WORD TestAndSet(SPINLOCK_WORD* pTargetAddress, SPINLOCK_WORD nValue);
83
84   private:
85         SPINLOCK_WORD m_s;
86 };
87
88 // This part is Platform dependent!
89
90 /* The following piece of code can be found
91  in function AtomicExchange of file atomicops-internals-x86.h
92  under http://google-perftools.googlecode.com/svn/trunk/src/base/
93 */
94 #if defined(__x86_64__)
95 #define TAS(_lw, _res) \
96  asm volatile("xchgq %1,%0":"=r"(_res):"m"(*_lw),"0"(_res):"memory")
97 #elif defined(__i386__)
98 #define TAS(_lw, _res) \
99  asm volatile("xchgl %1,%0":"=r"(_res):"m"(*_lw),"0"(_res):"memory")
100 #else
101 #error "Architecture is neither x86_64 nor i386 (see spinlock.h)"
102 #endif
103 /* TAS is only defined for GNUC on x86_64 and i386,
104  that is where GNUC x86 inline assembly can be used */
105
106 inline SPINLOCK_WORD SpinLock::TestAndSet(SPINLOCK_WORD* pTargetAddress, SPINLOCK_WORD nValue) {
107
108 #if 0
109     __asm
110     {
111         mov edx, dword ptr [pTargetAddress]
112         mov eax, nValue
113         lock xchg eax, dword ptr [edx]
114     }
115 #else
116     TAS(pTargetAddress, nValue);
117     return nValue;
118 #endif
119     // mov = 1 CPU cycle
120     // lock = 1 CPU cycle
121     // xchg = 3 CPU cycles
122 }
123 #endif /*USE_SPINLOCK*/
124
125 #endif /*BOWTIE_PTHREADS*/
126
127 #endif /*SPINLOCK_H_*/