1 /******************************************************
2 Mutex, the basic synchronization primitive
6 Created 9/5/1995 Heikki Tuuri
7 *******************************************************/
9 #if defined(not_defined) && defined(__GNUC__) && defined(UNIV_INTEL_X86)
10 /* %z0: Use the size of operand %0 which in our case is *m to determine
11 instruction size, it should end up as xchgl. "1" in the input constraint,
12 says that "in" has to go in the same place as "out".*/
13 #define TAS(m, in, out) \
14 asm volatile ("xchg%z0 %2, %0" \
15 : "=g" (*(m)), "=r" (out) \
16 : "1" (in)) /* Note: "1" here refers to "=r" (out) */
19 /**********************************************************************
20 Sets the waiters field in a mutex. */
25 mutex_t* mutex, /* in: mutex */
26 ulint n); /* in: value to set */
27 /**********************************************************************
28 Reserves a mutex for the current thread. If the mutex is reserved, the
29 function spins a preset time (controlled by SYNC_SPIN_ROUNDS) waiting
30 for the mutex before suspending the thread. */
35 mutex_t* mutex, /* in: pointer to mutex */
36 const char* file_name, /* in: file name where mutex
38 ulint line); /* in: line where requested */
39 #ifdef UNIV_SYNC_DEBUG
40 /**********************************************************************
41 Sets the debug information for a reserved mutex. */
46 mutex_t* mutex, /* in: mutex */
47 const char* file_name, /* in: file where requested */
48 ulint line); /* in: line where requested */
49 #endif /* UNIV_SYNC_DEBUG */
50 /**********************************************************************
51 Releases the threads waiting in the primary wait array for this mutex. */
56 mutex_t* mutex); /* in: mutex */
58 /**********************************************************************
59 Performs an atomic test-and-set instruction to the lock_word field of a
65 /* out: the previous value of lock_word: 0 or
67 mutex_t* mutex) /* in: mutex */
69 #if defined(_WIN32) && defined(UNIV_CAN_USE_X86_ASSEMBLER)
71 ulint* lw; /* assembler code is used to ensure that
72 lock_word is loaded from memory */
74 ut_ad(sizeof(ulint) == 4);
76 lw = &(mutex->lock_word);
80 __asm XCHG EDX, DWORD PTR [ECX]
83 /* The fence below would prevent this thread from
84 reading the data structure protected by the mutex
85 before the test-and-set operation is committed, but
86 the fence is apparently not needed:
88 In a posting to comp.arch newsgroup (August 10, 1997)
89 Andy Glew said that in P6 a LOCKed instruction like
90 XCHG establishes a fence with respect to memory reads
91 and writes and thus an explicit fence is not
92 needed. In P5 he seemed to agree with a previous
93 newsgroup poster that LOCKed instructions serialize
94 all instruction execution, and, consequently, also
95 memory operations. This is confirmed in Intel Software
96 Dev. Manual, Vol. 3. */
101 #elif defined(not_defined) && defined(__GNUC__) && defined(UNIV_INTEL_X86)
104 TAS(&mutex->lock_word, 1, res);
110 ret = os_fast_mutex_trylock(&(mutex->os_fast_mutex));
113 /* We check that os_fast_mutex_trylock does not leak
114 and allow race conditions */
115 ut_a(mutex->lock_word == 0);
117 mutex->lock_word = 1;
124 /**********************************************************************
125 Performs a reset instruction to the lock_word field of a mutex. This
126 instruction also serializes memory operations to the program order. */
129 mutex_reset_lock_word(
130 /*==================*/
131 mutex_t* mutex) /* in: mutex */
133 #if defined(_WIN32) && defined(UNIV_CAN_USE_X86_ASSEMBLER)
134 ulint* lw; /* assembler code is used to ensure that
135 lock_word is loaded from memory */
138 lw = &(mutex->lock_word);
142 __asm XCHG EDX, DWORD PTR [ECX]
143 #elif defined(not_defined) && defined(__GNUC__) && defined(UNIV_INTEL_X86)
146 TAS(&mutex->lock_word, 0, res);
148 mutex->lock_word = 0;
150 os_fast_mutex_unlock(&(mutex->os_fast_mutex));
154 /**********************************************************************
155 Gets the value of the lock word. */
160 const mutex_t* mutex) /* in: mutex */
162 const volatile ulint* ptr; /* declared volatile to ensure that
163 lock_word is loaded from memory */
166 ptr = &(mutex->lock_word);
171 /**********************************************************************
172 Gets the waiters field in a mutex. */
177 /* out: value to set */
178 const mutex_t* mutex) /* in: mutex */
180 const volatile ulint* ptr; /* declared volatile to ensure that
181 the value is read from memory */
184 ptr = &(mutex->waiters);
186 return(*ptr); /* Here we assume that the read of a single
187 word from memory is atomic */
190 /**********************************************************************
191 Unlocks a mutex owned by the current thread. */
196 mutex_t* mutex) /* in: pointer to mutex */
198 ut_ad(mutex_own(mutex));
200 ut_d(mutex->thread_id = (os_thread_id_t) ULINT_UNDEFINED);
202 #ifdef UNIV_SYNC_DEBUG
203 sync_thread_reset_level(mutex);
205 mutex_reset_lock_word(mutex);
207 /* A problem: we assume that mutex_reset_lock word
208 is a memory barrier, that is when we read the waiters
209 field next, the read must be serialized in memory
210 after the reset. A speculative processor might
211 perform the read first, which could leave a waiting
212 thread hanging indefinitely.
214 Our current solution call every second
215 sync_arr_wake_threads_if_sema_free()
216 to wake up possible hanging threads if
217 they are missed in mutex_signal_object. */
219 if (mutex_get_waiters(mutex) != 0) {
221 mutex_signal_object(mutex);
224 #ifdef UNIV_SYNC_PERF_STAT
229 /**********************************************************************
230 Locks a mutex for the current thread. If the mutex is reserved, the function
231 spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the mutex
232 before suspending the thread. */
237 mutex_t* mutex, /* in: pointer to mutex */
238 const char* file_name, /* in: file name where locked */
239 ulint line) /* in: line where locked */
241 ut_ad(mutex_validate(mutex));
242 ut_ad(!mutex_own(mutex));
244 /* Note that we do not peek at the value of lock_word before trying
245 the atomic test_and_set; we could peek, and possibly save time. */
247 #if defined UNIV_DEBUG && !defined UNIV_HOTBACKUP
248 mutex->count_using++;
249 #endif /* UNIV_DEBUG && !UNIV_HOTBACKUP */
251 if (!mutex_test_and_set(mutex)) {
252 ut_d(mutex->thread_id = os_thread_get_curr_id());
253 #ifdef UNIV_SYNC_DEBUG
254 mutex_set_debug_info(mutex, file_name, line);
256 return; /* Succeeded! */
259 mutex_spin_wait(mutex, file_name, line);