summaryrefslogtreecommitdiff
path: root/storage/innobase/include/sync0rw.ic
blob: 2a674f00262092c19c759c1bac004ca8bf341d47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
/******************************************************
The read-write lock (for threads)

(c) 1995 Innobase Oy

Created 9/11/1995 Heikki Tuuri
*******************************************************/

/**********************************************************************
Lock an rw-lock in shared mode for the current thread. If the rw-lock is
locked in exclusive mode, or there is an exclusive lock request waiting,
the function spins a preset time (controlled by SYNC_SPIN_ROUNDS),
waiting for the lock before suspending the thread. */

void
rw_lock_s_lock_spin(
/*================*/
	rw_lock_t*	lock,	/* in: pointer to rw-lock */
	ulint		pass,	/* in: pass value; != 0, if the lock will
				be passed to another thread to unlock */
	const char*	file_name,/* in: file name where lock requested */
	ulint		line);	/* in: line where requested */
#ifdef UNIV_SYNC_DEBUG
/**********************************************************************
Inserts the debug information for an rw-lock. */

void
rw_lock_add_debug_info(
/*===================*/
	rw_lock_t*	lock,		/* in: rw-lock */
	ulint		pass,		/* in: pass value */
	ulint		lock_type,	/* in: lock type */
	const char*	file_name,	/* in: file where requested */
	ulint		line);		/* in: line where requested */
/**********************************************************************
Removes a debug information struct for an rw-lock. */

void
rw_lock_remove_debug_info(
/*======================*/
	rw_lock_t*	lock,		/* in: rw-lock */
	ulint		pass,		/* in: pass value */
	ulint		lock_type);	/* in: lock type */
#endif /* UNIV_SYNC_DEBUG */

/************************************************************************
Accessor functions for rw lock. */
UNIV_INLINE
ulint
rw_lock_get_waiters(
/*================*/
	rw_lock_t*	lock)
{
	return(lock->waiters);
}
UNIV_INLINE
void
rw_lock_set_waiters(
/*================*/
	rw_lock_t*	lock,
	ulint		flag)
{
	lock->waiters = flag;
}
UNIV_INLINE
ulint
rw_lock_get_writer(
/*===============*/
	rw_lock_t*	lock)
{
	return(lock->writer);
}
UNIV_INLINE
void
rw_lock_set_writer(
/*===============*/
	rw_lock_t*	lock,
	ulint		flag)
{
	lock->writer = flag;
}
UNIV_INLINE
ulint
rw_lock_get_reader_count(
/*=====================*/
	rw_lock_t*	lock)
{
	return(lock->reader_count);
}
UNIV_INLINE
void
rw_lock_set_reader_count(
/*=====================*/
	rw_lock_t*	lock,
	ulint		count)
{
	lock->reader_count = count;
}
UNIV_INLINE
mutex_t*
rw_lock_get_mutex(
/*==============*/
	rw_lock_t*	lock)
{
	return(&(lock->mutex));
}

/**********************************************************************
Returns the value of writer_count for the lock. Does not reserve the lock
mutex, so the caller must be sure it is not changed during the call. */
UNIV_INLINE
ulint
rw_lock_get_x_lock_count(
/*=====================*/
				/* out: value of writer_count */
	rw_lock_t*	lock)	/* in: rw-lock */
{
	return(lock->writer_count);
}

/**********************************************************************
Low-level function which tries to lock an rw-lock in s-mode. Performs no
spinning. */
UNIV_INLINE
ibool
rw_lock_s_lock_low(
/*===============*/
				/* out: TRUE if success */
	rw_lock_t*	lock,	/* in: pointer to rw-lock */
	ulint		pass __attribute__((unused)),
				/* in: pass value; != 0, if the lock will be
				passed to another thread to unlock */
	const char*	file_name, /* in: file name where lock requested */
	ulint		line)	/* in: line where requested */
{
	ut_ad(mutex_own(rw_lock_get_mutex(lock)));

	/* Check if the writer field is free */

	if (UNIV_LIKELY(lock->writer == RW_LOCK_NOT_LOCKED)) {
		/* Set the shared lock by incrementing the reader count */
		lock->reader_count++;

#ifdef UNIV_SYNC_DEBUG
		rw_lock_add_debug_info(lock, pass, RW_LOCK_SHARED, file_name,
				       line);
#endif
		lock->last_s_file_name = file_name;
		lock->last_s_line = line;

		return(TRUE);	/* locking succeeded */
	}

	return(FALSE);	/* locking did not succeed */
}

/**********************************************************************
Low-level function which locks an rw-lock in s-mode when we know that it
is possible and none else is currently accessing the rw-lock structure.
Then we can do the locking without reserving the mutex. */
UNIV_INLINE
void
rw_lock_s_lock_direct(
/*==================*/
	rw_lock_t*	lock,		/* in: pointer to rw-lock */
	const char*	file_name,	/* in: file name where requested */
	ulint		line)		/* in: line where lock requested */
{
	ut_ad(lock->writer == RW_LOCK_NOT_LOCKED);
	ut_ad(rw_lock_get_reader_count(lock) == 0);

	/* Set the shared lock by incrementing the reader count */
	lock->reader_count++;

	lock->last_s_file_name = file_name;
	lock->last_s_line = line;

#ifdef UNIV_SYNC_DEBUG
	rw_lock_add_debug_info(lock, 0, RW_LOCK_SHARED, file_name, line);
#endif
}

/**********************************************************************
Low-level function which locks an rw-lock in x-mode when we know that it
is not locked and none else is currently accessing the rw-lock structure.
Then we can do the locking without reserving the mutex. */
UNIV_INLINE
void
rw_lock_x_lock_direct(
/*==================*/
	rw_lock_t*	lock,		/* in: pointer to rw-lock */
	const char*	file_name,	/* in: file name where requested */
	ulint		line)		/* in: line where lock requested */
{
	ut_ad(rw_lock_validate(lock));
	ut_ad(rw_lock_get_reader_count(lock) == 0);
	ut_ad(rw_lock_get_writer(lock) == RW_LOCK_NOT_LOCKED);

	rw_lock_set_writer(lock, RW_LOCK_EX);
	lock->writer_thread = os_thread_get_curr_id();
	lock->writer_count++;
	lock->pass = 0;

	lock->last_x_file_name = file_name;
	lock->last_x_line = line;

#ifdef UNIV_SYNC_DEBUG
	rw_lock_add_debug_info(lock, 0, RW_LOCK_EX, file_name, line);
#endif
}

/**********************************************************************
NOTE! Use the corresponding macro, not directly this function! Lock an
rw-lock in shared mode for the current thread. If the rw-lock is locked
in exclusive mode, or there is an exclusive lock request waiting, the
function spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for
the lock, before suspending the thread. */
UNIV_INLINE
void
rw_lock_s_lock_func(
/*================*/
	rw_lock_t*	lock,	/* in: pointer to rw-lock */
	ulint		pass,	/* in: pass value; != 0, if the lock will
				be passed to another thread to unlock */
	const char*	file_name,/* in: file name where lock requested */
	ulint		line)	/* in: line where requested */
{
	/* NOTE: As we do not know the thread ids for threads which have
	s-locked a latch, and s-lockers will be served only after waiting
	x-lock requests have been fulfilled, then if this thread already
	owns an s-lock here, it may end up in a deadlock with another thread
	which requests an x-lock here. Therefore, we will forbid recursive
	s-locking of a latch: the following assert will warn the programmer
	of the possibility of this kind of a deadlock. If we want to implement
	safe recursive s-locking, we should keep in a list the thread ids of
	the threads which have s-locked a latch. This would use some CPU
	time. */

#ifdef UNIV_SYNC_DEBUG
	ut_ad(!rw_lock_own(lock, RW_LOCK_SHARED)); /* see NOTE above */
	ut_ad(!rw_lock_own(lock, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */

	mutex_enter(rw_lock_get_mutex(lock));

	if (UNIV_LIKELY(rw_lock_s_lock_low(lock, pass, file_name, line))) {
		mutex_exit(rw_lock_get_mutex(lock));

		return; /* Success */
	} else {
		/* Did not succeed, try spin wait */
		mutex_exit(rw_lock_get_mutex(lock));

		rw_lock_s_lock_spin(lock, pass, file_name, line);

		return;
	}
}

/**********************************************************************
NOTE! Use the corresponding macro, not directly this function! Lock an
rw-lock in shared mode for the current thread if the lock can be acquired
immediately. */
UNIV_INLINE
ibool
rw_lock_s_lock_func_nowait(
/*=======================*/
				/* out: TRUE if success */
	rw_lock_t*	lock,	/* in: pointer to rw-lock */
	const char*	file_name,/* in: file name where lock requested */
	ulint		line)	/* in: line where requested */
{
	ibool	success	= FALSE;

	mutex_enter(rw_lock_get_mutex(lock));

	if (lock->writer == RW_LOCK_NOT_LOCKED) {
		/* Set the shared lock by incrementing the reader count */
		lock->reader_count++;

#ifdef UNIV_SYNC_DEBUG
		rw_lock_add_debug_info(lock, 0, RW_LOCK_SHARED, file_name,
				       line);
#endif

		lock->last_s_file_name = file_name;
		lock->last_s_line = line;

		success = TRUE;
	}

	mutex_exit(rw_lock_get_mutex(lock));

	return(success);
}

/**********************************************************************
NOTE! Use the corresponding macro, not directly this function! Lock an
rw-lock in exclusive mode for the current thread if the lock can be
obtained immediately. */
UNIV_INLINE
ibool
rw_lock_x_lock_func_nowait(
/*=======================*/
				/* out: TRUE if success */
	rw_lock_t*	lock,	/* in: pointer to rw-lock */
	const char*	file_name,/* in: file name where lock requested */
	ulint		line)	/* in: line where requested */
{
	ibool		success		= FALSE;
	os_thread_id_t	curr_thread	= os_thread_get_curr_id();
	mutex_enter(rw_lock_get_mutex(lock));

	if (UNIV_UNLIKELY(rw_lock_get_reader_count(lock) != 0)) {
	} else if (UNIV_LIKELY(rw_lock_get_writer(lock)
			       == RW_LOCK_NOT_LOCKED)) {
		rw_lock_set_writer(lock, RW_LOCK_EX);
		lock->writer_thread = curr_thread;
		lock->pass = 0;
relock:
		lock->writer_count++;

#ifdef UNIV_SYNC_DEBUG
		rw_lock_add_debug_info(lock, 0, RW_LOCK_EX, file_name, line);
#endif

		lock->last_x_file_name = file_name;
		lock->last_x_line = line;

		success = TRUE;
	} else if (rw_lock_get_writer(lock) == RW_LOCK_EX
		   && lock->pass == 0
		   && os_thread_eq(lock->writer_thread, curr_thread)) {
		goto relock;
	}

	mutex_exit(rw_lock_get_mutex(lock));

	ut_ad(rw_lock_validate(lock));

	return(success);
}

/**********************************************************************
Releases a shared mode lock. */
UNIV_INLINE
void
rw_lock_s_unlock_func(
/*==================*/
	rw_lock_t*	lock	/* in: rw-lock */
#ifdef UNIV_SYNC_DEBUG
	,ulint		pass	/* in: pass value; != 0, if the lock may have
				been passed to another thread to unlock */
#endif
	)
{
	mutex_t*	mutex	= &(lock->mutex);
	ibool		sg	= FALSE;

	/* Acquire the mutex protecting the rw-lock fields */
	mutex_enter(mutex);

	/* Reset the shared lock by decrementing the reader count */

	ut_a(lock->reader_count > 0);
	lock->reader_count--;

#ifdef UNIV_SYNC_DEBUG
	rw_lock_remove_debug_info(lock, pass, RW_LOCK_SHARED);
#endif

	/* If there may be waiters and this was the last s-lock,
	signal the object */

	if (UNIV_UNLIKELY(lock->waiters)
	    && lock->reader_count == 0) {
		sg = TRUE;

		rw_lock_set_waiters(lock, 0);
	}

	mutex_exit(mutex);

	if (UNIV_UNLIKELY(sg)) {
#ifdef __WIN__
		os_event_set(lock->wait_ex_event);
#endif
		os_event_set(lock->event);
		sync_array_object_signalled(sync_primary_wait_array);
	}

	ut_ad(rw_lock_validate(lock));

#ifdef UNIV_SYNC_PERF_STAT
	rw_s_exit_count++;
#endif
}

/**********************************************************************
Releases a shared mode lock when we know there are no waiters and none
else will access the lock during the time this function is executed. */
UNIV_INLINE
void
rw_lock_s_unlock_direct(
/*====================*/
	rw_lock_t*	lock)	/* in: rw-lock */
{
	/* Reset the shared lock by decrementing the reader count */

	ut_ad(lock->reader_count > 0);

	lock->reader_count--;

#ifdef UNIV_SYNC_DEBUG
	rw_lock_remove_debug_info(lock, 0, RW_LOCK_SHARED);
#endif

	ut_ad(!lock->waiters);
	ut_ad(rw_lock_validate(lock));
#ifdef UNIV_SYNC_PERF_STAT
	rw_s_exit_count++;
#endif
}

/**********************************************************************
Releases an exclusive mode lock. */
UNIV_INLINE
void
rw_lock_x_unlock_func(
/*==================*/
	rw_lock_t*	lock	/* in: rw-lock */
#ifdef UNIV_SYNC_DEBUG
	,ulint		pass	/* in: pass value; != 0, if the lock may have
				been passed to another thread to unlock */
#endif
	)
{
	ibool	sg	= FALSE;

	/* Acquire the mutex protecting the rw-lock fields */
	mutex_enter(&(lock->mutex));

	/* Reset the exclusive lock if this thread no longer has an x-mode
	lock */

	ut_ad(lock->writer_count > 0);

	lock->writer_count--;

	if (lock->writer_count == 0) {
		rw_lock_set_writer(lock, RW_LOCK_NOT_LOCKED);
	}

#ifdef UNIV_SYNC_DEBUG
	rw_lock_remove_debug_info(lock, pass, RW_LOCK_EX);
#endif

	/* If there may be waiters, signal the lock */
	if (UNIV_UNLIKELY(lock->waiters)
	    && lock->writer_count == 0) {

		sg = TRUE;
		rw_lock_set_waiters(lock, 0);
	}

	mutex_exit(&(lock->mutex));

	if (UNIV_UNLIKELY(sg)) {
#ifdef __WIN__
		os_event_set(lock->wait_ex_event);
#endif
		os_event_set(lock->event);
		sync_array_object_signalled(sync_primary_wait_array);
	}

	ut_ad(rw_lock_validate(lock));

#ifdef UNIV_SYNC_PERF_STAT
	rw_x_exit_count++;
#endif
}

/**********************************************************************
Releases an exclusive mode lock when we know there are no waiters, and
none else will access the lock durint the time this function is executed. */
UNIV_INLINE
void
rw_lock_x_unlock_direct(
/*====================*/
	rw_lock_t*	lock)	/* in: rw-lock */
{
	/* Reset the exclusive lock if this thread no longer has an x-mode
	lock */

	ut_ad(lock->writer_count > 0);

	lock->writer_count--;

	if (lock->writer_count == 0) {
		rw_lock_set_writer(lock, RW_LOCK_NOT_LOCKED);
	}

#ifdef UNIV_SYNC_DEBUG
	rw_lock_remove_debug_info(lock, 0, RW_LOCK_EX);
#endif

	ut_ad(!lock->waiters);
	ut_ad(rw_lock_validate(lock));

#ifdef UNIV_SYNC_PERF_STAT
	rw_x_exit_count++;
#endif
}