1/*****************************************************************************
2
3Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2008, Google Inc.
5
6Portions of this file contain modifications contributed and copyrighted by
7Google, Inc. Those modifications are gratefully acknowledged and are described
8briefly in the InnoDB documentation. The contributions by Google are
9incorporated with their permission, and subject to the conditions contained in
10the file COPYING.Google.
11
12This program is free software; you can redistribute it and/or modify
13it under the terms of the GNU General Public License, version 2.0,
14as published by the Free Software Foundation.
15
16This program is also distributed with certain software (including
17but not limited to OpenSSL) that is licensed under separate terms,
18as designated in a particular file or component or in included license
19documentation.  The authors of MySQL hereby grant you an additional
20permission to link the program and your derivative works with the
21separately licensed software that they have included with MySQL.
22
23This program is distributed in the hope that it will be useful,
24but WITHOUT ANY WARRANTY; without even the implied warranty of
25MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26GNU General Public License, version 2.0, for more details.
27
28You should have received a copy of the GNU General Public License along with
29this program; if not, write to the Free Software Foundation, Inc.,
3051 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
31
32*****************************************************************************/
33
34/**************************************************//**
35@file include/sync0sync.ic
36Mutex, the basic synchronization primitive
37
38Created 9/5/1995 Heikki Tuuri
39*******************************************************/
40
41/******************************************************************//**
42Sets the waiters field in a mutex. */
43UNIV_INTERN
44void
45mutex_set_waiters(
46/*==============*/
47	ib_mutex_t*	mutex,	/*!< in: mutex */
48	ulint		n);	/*!< in: value to set */
49/******************************************************************//**
50Reserves a mutex for the current thread. If the mutex is reserved, the
51function spins a preset time (controlled by SYNC_SPIN_ROUNDS) waiting
52for the mutex before suspending the thread. */
53UNIV_INTERN
54void
55mutex_spin_wait(
56/*============*/
57	ib_mutex_t*	mutex,		/*!< in: pointer to mutex */
58	const char*	file_name,	/*!< in: file name where mutex
59					requested */
60	ulint		line);		/*!< in: line where requested */
61#ifdef UNIV_SYNC_DEBUG
62/******************************************************************//**
63Sets the debug information for a reserved mutex. */
64UNIV_INTERN
65void
66mutex_set_debug_info(
67/*=================*/
68	ib_mutex_t*	mutex,		/*!< in: mutex */
69	const char*	file_name,	/*!< in: file where requested */
70	ulint		line);		/*!< in: line where requested */
71#endif /* UNIV_SYNC_DEBUG */
72/******************************************************************//**
73Releases the threads waiting in the primary wait array for this mutex. */
74UNIV_INTERN
75void
76mutex_signal_object(
77/*================*/
78	ib_mutex_t*	mutex);	/*!< in: mutex */
79
80/******************************************************************//**
81Performs an atomic test-and-set instruction to the lock_word field of a
82mutex.
83@return	the previous value of lock_word: 0 or 1 */
84UNIV_INLINE
85lock_word_t
86ib_mutex_test_and_set(
87/*==================*/
88	ib_mutex_t*	mutex)	/*!< in: mutex */
89{
90#if defined(HAVE_ATOMIC_BUILTINS)
91	return(os_atomic_test_and_set(&mutex->lock_word));
92#else
93	ibool	ret;
94
95	ret = os_fast_mutex_trylock(&(mutex->os_fast_mutex));
96
97	if (ret == 0) {
98		/* We check that os_fast_mutex_trylock does not leak
99		and allow race conditions */
100		ut_a(mutex->lock_word == 0);
101
102		mutex->lock_word = 1;
103		os_wmb;
104	}
105
106	return((byte) ret);
107#endif /* HAVE_ATOMIC_BUILTINS */
108}
109
110/******************************************************************//**
111Performs a reset instruction to the lock_word field of a mutex. This
112instruction also serializes memory operations to the program order. */
113UNIV_INLINE
114void
115mutex_reset_lock_word(
116/*==================*/
117	ib_mutex_t*	mutex)	/*!< in: mutex */
118{
119#if defined(HAVE_ATOMIC_BUILTINS)
120	os_atomic_clear(&mutex->lock_word);
121#else
122	mutex->lock_word = 0;
123
124	os_fast_mutex_unlock(&(mutex->os_fast_mutex));
125#endif /* HAVE_ATOMIC_BUILTINS */
126}
127
128/******************************************************************//**
129Gets the value of the lock word. */
130UNIV_INLINE
131lock_word_t
132mutex_get_lock_word(
133/*================*/
134	const ib_mutex_t*	mutex)	/*!< in: mutex */
135{
136	ut_ad(mutex);
137
138	return(mutex->lock_word);
139}
140
141/******************************************************************//**
142Gets the waiters field in a mutex.
143@return	value to set */
144UNIV_INLINE
145ulint
146mutex_get_waiters(
147/*==============*/
148	const ib_mutex_t*	mutex)	/*!< in: mutex */
149{
150	const volatile ulint*	ptr;	/*!< declared volatile to ensure that
151					the value is read from memory */
152	ut_ad(mutex);
153
154	ptr = &(mutex->waiters);
155
156	return(*ptr);		/* Here we assume that the read of a single
157				word from memory is atomic */
158}
159
160/******************************************************************//**
161NOTE! Use the corresponding macro mutex_exit(), not directly this function!
162Unlocks a mutex owned by the current thread. */
163UNIV_INLINE
164void
165mutex_exit_func(
166/*============*/
167	ib_mutex_t*	mutex)	/*!< in: pointer to mutex */
168{
169	ut_ad(mutex_own(mutex));
170
171	ut_d(mutex->thread_id = (os_thread_id_t) ULINT_UNDEFINED);
172
173#ifdef UNIV_SYNC_DEBUG
174	sync_thread_reset_level(mutex);
175#endif
176	mutex_reset_lock_word(mutex);
177
178	/* A problem: we assume that mutex_reset_lock word
179	is a memory barrier, that is when we read the waiters
180	field next, the read must be serialized in memory
181	after the reset. A speculative processor might
182	perform the read first, which could leave a waiting
183	thread hanging indefinitely.
184
185	Our current solution call every second
186	sync_arr_wake_threads_if_sema_free()
187	to wake up possible hanging threads if
188	they are missed in mutex_signal_object. */
189
190	if (mutex_get_waiters(mutex) != 0) {
191
192		mutex_signal_object(mutex);
193	}
194
195#ifdef UNIV_SYNC_PERF_STAT
196	mutex_exit_count++;
197#endif
198}
199
200/******************************************************************//**
201Locks a mutex for the current thread. If the mutex is reserved, the function
202spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the mutex
203before suspending the thread. */
204UNIV_INLINE
205void
206mutex_enter_func(
207/*=============*/
208	ib_mutex_t*	mutex,		/*!< in: pointer to mutex */
209	const char*	file_name,	/*!< in: file name where locked */
210	ulint		line)		/*!< in: line where locked */
211{
212	ut_ad(mutex_validate(mutex));
213	ut_ad(!mutex_own(mutex));
214
215	/* Note that we do not peek at the value of lock_word before trying
216	the atomic test_and_set; we could peek, and possibly save time. */
217
218	if (!ib_mutex_test_and_set(mutex)) {
219		ut_d(mutex->thread_id = os_thread_get_curr_id());
220#ifdef UNIV_SYNC_DEBUG
221		mutex_set_debug_info(mutex, file_name, line);
222#endif
223		return;	/* Succeeded! */
224	}
225
226	mutex_spin_wait(mutex, file_name, line);
227}
228
229#ifdef UNIV_PFS_MUTEX
230/******************************************************************//**
231NOTE! Please use the corresponding macro mutex_enter(), not directly
232this function!
233This is a performance schema instrumented wrapper function for
234mutex_enter_func(). */
235UNIV_INLINE
236void
237pfs_mutex_enter_func(
238/*=================*/
239	ib_mutex_t*	mutex,	/*!< in: pointer to mutex */
240	const char*	file_name,	/*!< in: file name where locked */
241	ulint		line)		/*!< in: line where locked */
242{
243	if (mutex->pfs_psi != NULL) {
244		PSI_mutex_locker*	locker;
245		PSI_mutex_locker_state	state;
246
247		locker = PSI_MUTEX_CALL(start_mutex_wait)(
248			&state, mutex->pfs_psi,
249			PSI_MUTEX_LOCK, file_name,
250			static_cast<uint>(line));
251
252		mutex_enter_func(mutex, file_name, line);
253
254		if (locker != NULL) {
255			PSI_MUTEX_CALL(end_mutex_wait)(locker, 0);
256		}
257	} else {
258		mutex_enter_func(mutex, file_name, line);
259	}
260}
261
262/********************************************************************//**
263NOTE! Please use the corresponding macro mutex_enter_nowait(), not directly
264this function!
265This is a performance schema instrumented wrapper function for
266mutex_enter_nowait_func.
267@return 0 if succeed, 1 if not */
268UNIV_INLINE
269ulint
270pfs_mutex_enter_nowait_func(
271/*========================*/
272	ib_mutex_t*	mutex,		/*!< in: pointer to mutex */
273	const char*	file_name,	/*!< in: file name where mutex
274					requested */
275	ulint		line)		/*!< in: line where requested */
276{
277	ulint		ret;
278
279	if (mutex->pfs_psi != NULL) {
280		PSI_mutex_locker*	locker;
281		PSI_mutex_locker_state		state;
282
283		locker = PSI_MUTEX_CALL(start_mutex_wait)(
284			&state, mutex->pfs_psi,
285			PSI_MUTEX_TRYLOCK, file_name,
286			static_cast<uint>(line));
287
288		ret = mutex_enter_nowait_func(mutex, file_name, line);
289
290		if (locker != NULL) {
291			PSI_MUTEX_CALL(end_mutex_wait)(locker, (int) ret);
292		}
293	} else {
294		ret = mutex_enter_nowait_func(mutex, file_name, line);
295	}
296
297	return(ret);
298}
299
300/******************************************************************//**
301NOTE! Please use the corresponding macro mutex_exit(), not directly
302this function!
303A wrap function of mutex_exit_func() with performance schema instrumentation.
304Unlocks a mutex owned by the current thread. */
305UNIV_INLINE
306void
307pfs_mutex_exit_func(
308/*================*/
309	ib_mutex_t*	mutex)	/*!< in: pointer to mutex */
310{
311	if (mutex->pfs_psi != NULL) {
312		PSI_MUTEX_CALL(unlock_mutex)(mutex->pfs_psi);
313	}
314
315	mutex_exit_func(mutex);
316}
317
318/******************************************************************//**
319NOTE! Please use the corresponding macro mutex_create(), not directly
320this function!
321A wrapper function for mutex_create_func(), registers the mutex
322with performance schema if "UNIV_PFS_MUTEX" is defined when
323creating the mutex */
324UNIV_INLINE
325void
326pfs_mutex_create_func(
327/*==================*/
328	mysql_pfs_key_t	key,		/*!< in: Performance Schema key */
329	ib_mutex_t*	mutex,		/*!< in: pointer to memory */
330# ifdef UNIV_DEBUG
331	const char*	cmutex_name,	/*!< in: mutex name */
332#  ifdef UNIV_SYNC_DEBUG
333	ulint		level,		/*!< in: level */
334#  endif /* UNIV_SYNC_DEBUG */
335# endif /* UNIV_DEBUG */
336	const char*	cfile_name,	/*!< in: file name where created */
337	ulint		cline)		/*!< in: file line where created */
338{
339	mutex->pfs_psi = PSI_MUTEX_CALL(init_mutex)(key, mutex);
340
341	mutex_create_func(mutex,
342# ifdef UNIV_DEBUG
343			  cmutex_name,
344#  ifdef UNIV_SYNC_DEBUG
345			  level,
346#  endif /* UNIV_SYNC_DEBUG */
347# endif /* UNIV_DEBUG */
348			  cfile_name,
349			  cline);
350}
351
352/******************************************************************//**
353NOTE! Please use the corresponding macro mutex_free(), not directly
354this function!
355Wrapper function for mutex_free_func(). Also destroys the performance
356schema probes when freeing the mutex */
357UNIV_INLINE
358void
359pfs_mutex_free_func(
360/*================*/
361	ib_mutex_t*	mutex)	/*!< in: mutex */
362{
363	if (mutex->pfs_psi != NULL) {
364		PSI_MUTEX_CALL(destroy_mutex)(mutex->pfs_psi);
365		mutex->pfs_psi = NULL;
366	}
367
368	mutex_free_func(mutex);
369}
370
371#endif /* UNIV_PFS_MUTEX */
372
373#ifndef HAVE_ATOMIC_BUILTINS
374/**********************************************************//**
375Function that uses a mutex to decrement a variable atomically */
376UNIV_INLINE
377void
378os_atomic_dec_ulint_func(
379/*=====================*/
380	ib_mutex_t*	mutex,		/*!< in: mutex guarding the dec */
381	volatile ulint*	var,		/*!< in/out: variable to decrement */
382	ulint		delta)		/*!< in: delta to decrement */
383{
384	mutex_enter(mutex);
385
386	/* I don't think we will encounter a situation where
387	this check will not be required. */
388	ut_ad(*var >= delta);
389
390	*var -= delta;
391
392	mutex_exit(mutex);
393}
394
395/**********************************************************//**
396Function that uses a mutex to increment a variable atomically */
397UNIV_INLINE
398void
399os_atomic_inc_ulint_func(
400/*=====================*/
401	ib_mutex_t*	mutex,		/*!< in: mutex guarding the increment */
402	volatile ulint*	var,		/*!< in/out: variable to increment */
403	ulint		delta)		/*!< in: delta to increment */
404{
405	mutex_enter(mutex);
406
407	*var += delta;
408
409	mutex_exit(mutex);
410}
411#endif /* !HAVE_ATOMIC_BUILTINS */
412