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 = 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 10 seconds
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);