sync0rw.c 22.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/******************************************************
The read-write lock (for thread synchronization)

(c) 1995 Innobase Oy

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

#include "sync0rw.h"
#ifdef UNIV_NONINL
#include "sync0rw.ic"
#endif

#include "os0thread.h"
#include "mem0mem.h"
#include "srv0srv.h"

ulint	rw_s_system_call_count	= 0;
ulint	rw_s_spin_wait_count	= 0;
20
ulint	rw_s_os_wait_count	= 0;
21 22 23 24 25

ulint	rw_s_exit_count		= 0;

ulint	rw_x_system_call_count	= 0;
ulint	rw_x_spin_wait_count	= 0;
26
ulint	rw_x_os_wait_count	= 0;
27 28 29 30 31 32 33

ulint	rw_x_exit_count		= 0;

/* The global list of rw-locks */
rw_lock_list_t	rw_lock_list;
mutex_t		rw_lock_list_mutex;

34
#ifdef UNIV_SYNC_DEBUG
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
/* The global mutex which protects debug info lists of all rw-locks.
To modify the debug info list of an rw-lock, this mutex has to be
acquired in addition to the mutex protecting the lock. */

mutex_t		rw_lock_debug_mutex;
os_event_t	rw_lock_debug_event;	/* If deadlock detection does not
					get immediately the mutex, it may
					wait for this event */
ibool		rw_lock_debug_waiters;	/* This is set to TRUE, if there may
					be waiters for the event */

/**********************************************************************
Creates a debug info struct. */
static
rw_lock_debug_t*
rw_lock_debug_create(void);
/*======================*/
/**********************************************************************
Frees a debug info struct. */
static
void
rw_lock_debug_free(
/*===============*/
	rw_lock_debug_t* info);

/**********************************************************************
Creates a debug info struct. */
static
rw_lock_debug_t*
rw_lock_debug_create(void)
/*======================*/
{
	return((rw_lock_debug_t*) mem_alloc(sizeof(rw_lock_debug_t)));
}

/**********************************************************************
Frees a debug info struct. */
static
void
rw_lock_debug_free(
/*===============*/
	rw_lock_debug_t* info)
{
	mem_free(info);
}
80
#endif /* UNIV_SYNC_DEBUG */
81 82 83 84 85 86 87 88 89 90 91

/**********************************************************************
Creates, or rather, initializes an rw-lock object in a specified memory
location (which must be appropriately aligned). The rw-lock is initialized
to the non-locked state. Explicit freeing of the rw-lock with rw_lock_free
is necessary only if the memory block containing it is freed. */

void
rw_lock_create_func(
/*================*/
	rw_lock_t*	lock,		/* in: pointer to memory */
92
	const char*	cfile_name,	/* in: file name where created */
93 94 95 96 97 98 99 100 101
	ulint		cline)		/* in: file line where created */
{
	/* If this is the very first time a synchronization
	object is created, then the following call initializes
	the sync system. */

	mutex_create(rw_lock_get_mutex(lock));
	mutex_set_level(rw_lock_get_mutex(lock), SYNC_NO_ORDER_CHECK);

102
	lock->mutex.cfile_name = cfile_name;
103 104 105 106 107 108 109 110 111
	lock->mutex.cline = cline;

	rw_lock_set_waiters(lock, 0);
	rw_lock_set_writer(lock, RW_LOCK_NOT_LOCKED);
	lock->writer_count = 0;
	rw_lock_set_reader_count(lock, 0);

	lock->writer_is_wait_ex = FALSE;

112
#ifdef UNIV_SYNC_DEBUG
113 114 115
	UT_LIST_INIT(lock->debug_list);

	lock->level = SYNC_LEVEL_NONE;
116 117
#endif /* UNIV_SYNC_DEBUG */
	lock->magic_n = RW_LOCK_MAGIC_N;
118
	
119
	lock->cfile_name = cfile_name;
120 121
	lock->cline = cline;

unknown's avatar
unknown committed
122 123
	lock->last_s_file_name = "not yet reserved";
	lock->last_x_file_name = "not yet reserved";
124 125
	lock->last_s_line = 0;
	lock->last_x_line = 0;
unknown's avatar
unknown committed
126
	lock->event = os_event_create(NULL);
127

128
	mutex_enter(&rw_lock_list_mutex);
unknown's avatar
unknown committed
129 130 131 132 133
	
	if (UT_LIST_GET_LEN(rw_lock_list) > 0) {
		ut_a(UT_LIST_GET_FIRST(rw_lock_list)->magic_n
							== RW_LOCK_MAGIC_N);
	}
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149

	UT_LIST_ADD_FIRST(list, rw_lock_list, lock);

	mutex_exit(&rw_lock_list_mutex);
}

/**********************************************************************
Calling this function is obligatory only if the memory buffer containing
the rw-lock is freed. Removes an rw-lock object from the global list. The
rw-lock is checked to be in the non-locked state. */

void
rw_lock_free(
/*=========*/
	rw_lock_t*	lock)	/* in: rw-lock */
{
unknown's avatar
unknown committed
150
#ifdef UNIV_DEBUG
unknown's avatar
unknown committed
151
	ut_a(rw_lock_validate(lock));
unknown's avatar
unknown committed
152
#endif /* UNIV_DEBUG */
153 154 155 156 157 158 159 160 161
	ut_a(rw_lock_get_writer(lock) == RW_LOCK_NOT_LOCKED);
	ut_a(rw_lock_get_waiters(lock) == 0);
	ut_a(rw_lock_get_reader_count(lock) == 0);
	
	lock->magic_n = 0;

	mutex_free(rw_lock_get_mutex(lock));

	mutex_enter(&rw_lock_list_mutex);
unknown's avatar
unknown committed
162
	os_event_free(lock->event);
163

unknown's avatar
unknown committed
164 165 166 167 168 169 170
	if (UT_LIST_GET_PREV(list, lock)) {
		ut_a(UT_LIST_GET_PREV(list, lock)->magic_n == RW_LOCK_MAGIC_N);
	}
	if (UT_LIST_GET_NEXT(list, lock)) {
		ut_a(UT_LIST_GET_NEXT(list, lock)->magic_n == RW_LOCK_MAGIC_N);
	}

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
	UT_LIST_REMOVE(list, rw_lock_list, lock);

	mutex_exit(&rw_lock_list_mutex);
}

/**********************************************************************
Checks that the rw-lock has been initialized and that there are no
simultaneous shared and exclusive locks. */

ibool
rw_lock_validate(
/*=============*/
	rw_lock_t*	lock)
{
	ut_a(lock);

	mutex_enter(rw_lock_get_mutex(lock));

	ut_a(lock->magic_n == RW_LOCK_MAGIC_N);
	ut_a((rw_lock_get_reader_count(lock) == 0)
	     || (rw_lock_get_writer(lock) != RW_LOCK_EX));
	ut_a((rw_lock_get_writer(lock) == RW_LOCK_EX)
	     || (rw_lock_get_writer(lock) == RW_LOCK_WAIT_EX)
	     || (rw_lock_get_writer(lock) == RW_LOCK_NOT_LOCKED));
	ut_a((rw_lock_get_waiters(lock) == 0)
	     || (rw_lock_get_waiters(lock) == 1));
	ut_a((lock->writer != RW_LOCK_EX) || (lock->writer_count > 0));
	     
	mutex_exit(rw_lock_get_mutex(lock));

	return(TRUE);
}

/**********************************************************************
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(
/*================*/
213 214
        rw_lock_t*   	lock,  	/* in: pointer to rw-lock */
	ulint		pass,	/* in: pass value; != 0, if the lock
215
				will be passed to another thread to unlock */
216
	const char*	file_name, /* in: file name where lock requested */
217
	ulint		line)	/* in: line where requested */
218 219 220 221 222 223 224 225 226
{
        ulint    index;	/* index of the reserved wait cell */
        ulint    i;   	/* spin round count */
        
        ut_ad(rw_lock_validate(lock));

lock_loop:
	rw_s_spin_wait_count++;

227
	/* Spin waiting for the writer field to become free */
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
        i = 0;

        while (rw_lock_get_writer(lock) != RW_LOCK_NOT_LOCKED
						&& i < SYNC_SPIN_ROUNDS) {
        	if (srv_spin_wait_delay) {
        		ut_delay(ut_rnd_interval(0, srv_spin_wait_delay));
        	}

        	i++;
        }

	if (i == SYNC_SPIN_ROUNDS) {
		os_thread_yield();
	}

	if (srv_print_latch_waits) {
244 245
		fprintf(stderr,
	"Thread %lu spin wait rw-s-lock at %p cfile %s cline %lu rnds %lu\n",
unknown's avatar
unknown committed
246
		(ulong) os_thread_pf(os_thread_get_curr_id()), lock,
247
		lock->cfile_name, (ulong) lock->cline, (ulong) i);
248 249 250 251 252 253
	}

	mutex_enter(rw_lock_get_mutex(lock));

        /* We try once again to obtain the lock */

254
	if (TRUE == rw_lock_s_lock_low(lock, pass, file_name, line)) {
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
		mutex_exit(rw_lock_get_mutex(lock));

		return; /* Success */
	} else {
		/* If we get here, locking did not succeed, we may
		suspend the thread to wait in the wait array */

		rw_s_system_call_count++;

        	sync_array_reserve_cell(sync_primary_wait_array,
				lock, RW_LOCK_SHARED,
				file_name, line,
				&index);

		rw_lock_set_waiters(lock, 1);

		mutex_exit(rw_lock_get_mutex(lock));

		if (srv_print_latch_waits) {
274 275
			fprintf(stderr,
		"Thread %lu OS wait rw-s-lock at %p cfile %s cline %lu\n",
unknown's avatar
unknown committed
276 277
				os_thread_pf(os_thread_get_curr_id()),
		        lock, lock->cfile_name, (ulong) lock->cline);
278 279 280
		}

		rw_s_system_call_count++;
281
		rw_s_os_wait_count++;
282

283
       	 	sync_array_wait_event(sync_primary_wait_array, index);
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

        	goto lock_loop;
	}        
}

/**********************************************************************
This function is used in the insert buffer to move the ownership of an
x-latch on a buffer frame to the current thread. The x-latch was set by
the buffer read operation and it protected the buffer frame while the
read was done. The ownership is moved because we want that the current
thread is able to acquire a second x-latch which is stored in an mtr.
This, in turn, is needed to pass the debug checks of index page
operations. */

void
rw_lock_x_lock_move_ownership(
/*==========================*/
	rw_lock_t*	lock)	/* in: lock which was x-locked in the
				buffer read */
{
	ut_ad(rw_lock_is_locked(lock, RW_LOCK_EX));

	mutex_enter(&(lock->mutex));

	lock->writer_thread = os_thread_get_curr_id();

	lock->pass = 0;

	mutex_exit(&(lock->mutex));
}

/**********************************************************************
Low-level function for acquiring an exclusive lock. */
UNIV_INLINE
ulint
rw_lock_x_lock_low(
/*===============*/
				/* out: RW_LOCK_NOT_LOCKED if did
				not succeed, RW_LOCK_EX if success,
				RW_LOCK_WAIT_EX, if got wait reservation */
        rw_lock_t*   	lock,  	/* in: pointer to rw-lock */
325
	ulint		pass,	/* in: pass value; != 0, if the lock will
326
				be passed to another thread to unlock */
327
	const char*	file_name,/* in: file name where lock requested */
328
	ulint		line)	/* in: line where requested */
329
{
330
#ifdef UNIV_SYNC_DEBUG
331
	ut_ad(mutex_own(rw_lock_get_mutex(lock)));
332
#endif /* UNIV_SYNC_DEBUG */
333 334 335 336 337 338 339 340 341
	if (rw_lock_get_writer(lock) == RW_LOCK_NOT_LOCKED) {

		if (rw_lock_get_reader_count(lock) == 0) {
			
			rw_lock_set_writer(lock, RW_LOCK_EX);
			lock->writer_thread = os_thread_get_curr_id();
			lock->writer_count++;
			lock->pass = pass;
			
unknown's avatar
unknown committed
342
#ifdef UNIV_SYNC_DEBUG
343 344
			rw_lock_add_debug_info(lock, pass, RW_LOCK_EX,
							file_name, line);
unknown's avatar
unknown committed
345
#endif
346 347
			lock->last_x_file_name = file_name;
			lock->last_x_line = line;
348 349 350 351 352 353 354 355 356 357
		
			/* Locking succeeded, we may return */
			return(RW_LOCK_EX);
		} else {
			/* There are readers, we have to wait */
			rw_lock_set_writer(lock, RW_LOCK_WAIT_EX);
			lock->writer_thread = os_thread_get_curr_id();
			lock->pass = pass;
			lock->writer_is_wait_ex = TRUE;

unknown's avatar
unknown committed
358
#ifdef UNIV_SYNC_DEBUG
359 360
			rw_lock_add_debug_info(lock, pass, RW_LOCK_WAIT_EX,
							file_name, line);
unknown's avatar
unknown committed
361
#endif
362 363 364 365 366

			return(RW_LOCK_WAIT_EX);
		}

	} else if ((rw_lock_get_writer(lock) == RW_LOCK_WAIT_EX)
unknown's avatar
unknown committed
367 368
		   && os_thread_eq(lock->writer_thread,
						os_thread_get_curr_id())) {
369 370 371 372 373 374 375 376

		if (rw_lock_get_reader_count(lock) == 0) {

			rw_lock_set_writer(lock, RW_LOCK_EX);
			lock->writer_count++;
			lock->pass = pass;
			lock->writer_is_wait_ex = FALSE;

unknown's avatar
unknown committed
377
#ifdef UNIV_SYNC_DEBUG
378 379 380
			rw_lock_remove_debug_info(lock, pass, RW_LOCK_WAIT_EX);
			rw_lock_add_debug_info(lock, pass, RW_LOCK_EX,
							file_name, line);
unknown's avatar
unknown committed
381
#endif
382
		
383 384 385
			lock->last_x_file_name = file_name;
			lock->last_x_line = line;

386 387 388 389 390 391 392
			/* Locking succeeded, we may return */
			return(RW_LOCK_EX);
		}

		return(RW_LOCK_WAIT_EX);

	} else if ((rw_lock_get_writer(lock) == RW_LOCK_EX)
unknown's avatar
unknown committed
393 394
		   && os_thread_eq(lock->writer_thread,
						os_thread_get_curr_id())
395 396 397 398 399
		   && (lock->pass == 0)
		   && (pass == 0)) {

		lock->writer_count++;

unknown's avatar
unknown committed
400
#ifdef UNIV_SYNC_DEBUG
401 402
		rw_lock_add_debug_info(lock, pass, RW_LOCK_EX, file_name,
									line);
unknown's avatar
unknown committed
403
#endif
404
		
405 406 407
		lock->last_x_file_name = file_name;
		lock->last_x_line = line;

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
		/* Locking succeeded, we may return */
		return(RW_LOCK_EX);
	}

	/* Locking did not succeed */
	return(RW_LOCK_NOT_LOCKED);
}

/**********************************************************************
NOTE! Use the corresponding macro, not directly this function! Lock an
rw-lock in exclusive mode for the current thread. If the rw-lock is locked
in shared or 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. If the same thread has an x-lock
on the rw-lock, locking succeed, with the following exception: if pass != 0,
only a single x-lock may be taken on the lock. NOTE: If the same thread has
an s-lock, locking does not succeed! */

void
rw_lock_x_lock_func(
/*================*/
        rw_lock_t*   	lock,  	/* in: pointer to rw-lock */
430
	ulint		pass,	/* in: pass value; != 0, if the lock will
431
				be passed to another thread to unlock */
432
	const char*	file_name,/* in: file name where lock requested */
433
	ulint		line)	/* in: line where requested */
434 435 436 437 438 439 440 441 442 443 444
{
        ulint	index;  /* index of the reserved wait cell */
        ulint	state;	/* lock state acquired */
        ulint	i;	/* spin round count */
        
        ut_ad(rw_lock_validate(lock));

lock_loop:
        /* Acquire the mutex protecting the rw-lock fields */
	mutex_enter_fast(&(lock->mutex));

445
	state = rw_lock_x_lock_low(lock, pass, file_name, line);
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
		
	mutex_exit(&(lock->mutex));
        
	if (state == RW_LOCK_EX) {

		return;	/* Locking succeeded */

	} else if (state == RW_LOCK_NOT_LOCKED) {

 		/* Spin waiting for the writer field to become free */
		i = 0;

        	while (rw_lock_get_writer(lock) != RW_LOCK_NOT_LOCKED 
               					&& i < SYNC_SPIN_ROUNDS) {
        		if (srv_spin_wait_delay) {
				ut_delay(ut_rnd_interval(0,
							srv_spin_wait_delay));
        		}
        		
        		i++;
        	}
		if (i == SYNC_SPIN_ROUNDS) {
			os_thread_yield();
		}
        } else if (state == RW_LOCK_WAIT_EX) {

 		/* Spin waiting for the reader count field to become zero */
		i = 0;

        	while (rw_lock_get_reader_count(lock) != 0 
               					&& i < SYNC_SPIN_ROUNDS) {
        		if (srv_spin_wait_delay) {
				ut_delay(ut_rnd_interval(0,
							srv_spin_wait_delay));
        		}

			i++;
        	}
		if (i == SYNC_SPIN_ROUNDS) {
			os_thread_yield();
		}
        } else {
488
		i = 0; /* Eliminate a compiler warning */
489 490 491 492
		ut_error;
	}	

	if (srv_print_latch_waits) {
493 494 495
		fprintf(stderr,
	"Thread %lu spin wait rw-x-lock at %p cfile %s cline %lu rnds %lu\n",
			os_thread_pf(os_thread_get_curr_id()), lock,
496
		lock->cfile_name, (ulong) lock->cline, (ulong) i);
497 498 499 500 501 502 503 504 505
	}

	rw_x_spin_wait_count++;

        /* We try once again to obtain the lock. Acquire the mutex protecting
	the rw-lock fields */

	mutex_enter(rw_lock_get_mutex(lock));

506
	state = rw_lock_x_lock_low(lock, pass, file_name, line);
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525

	if (state == RW_LOCK_EX) {
		mutex_exit(rw_lock_get_mutex(lock));

		return;	/* Locking succeeded */
	}

	rw_x_system_call_count++;

        sync_array_reserve_cell(sync_primary_wait_array,
				lock, RW_LOCK_EX,
				file_name, line,
				&index);

	rw_lock_set_waiters(lock, 1);

	mutex_exit(rw_lock_get_mutex(lock));

	if (srv_print_latch_waits) {
526 527 528
		fprintf(stderr,
		"Thread %lu OS wait for rw-x-lock at %p cfile %s cline %lu\n",
			os_thread_pf(os_thread_get_curr_id()), lock,
529
		lock->cfile_name, (ulong) lock->cline);
530 531 532
	}

	rw_x_system_call_count++;
533
	rw_x_os_wait_count++;
534 535 536 537 538 539

        sync_array_wait_event(sync_primary_wait_array, index);

        goto lock_loop;
}

540
#ifdef UNIV_SYNC_DEBUG
541 542 543 544 545 546 547 548 549 550 551 552
/**********************************************************************
Acquires the debug mutex. We cannot use the mutex defined in sync0sync,
because the debug mutex is also acquired in sync0arr while holding the OS
mutex protecting the sync array, and the ordinary mutex_enter might
recursively call routines in sync0arr, leading to a deadlock on the OS
mutex. */

void
rw_lock_debug_mutex_enter(void)
/*==========================*/
{
loop:
553
	if (0 == mutex_enter_nowait(&rw_lock_debug_mutex,
554
			__FILE__, __LINE__)) {
555 556 557 558 559 560 561
		return;
	}

	os_event_reset(rw_lock_debug_event);

	rw_lock_debug_waiters = TRUE;

562
	if (0 == mutex_enter_nowait(&rw_lock_debug_mutex,
563
			__FILE__, __LINE__)) {
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
		return;
	}

	os_event_wait(rw_lock_debug_event);

	goto loop;	
}

/**********************************************************************
Releases the debug mutex. */

void
rw_lock_debug_mutex_exit(void)
/*==========================*/
{
	mutex_exit(&rw_lock_debug_mutex);

	if (rw_lock_debug_waiters) {
		rw_lock_debug_waiters = FALSE;
		os_event_set(rw_lock_debug_event);
	}
}

/**********************************************************************
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 */
unknown's avatar
unknown committed
596
	const char*	file_name,	/* in: file where requested */
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
	ulint		line)		/* in: line where requested */
{
	rw_lock_debug_t*	info;

	ut_ad(lock);
	ut_ad(file_name);

	info = rw_lock_debug_create();

	rw_lock_debug_mutex_enter();

	info->file_name = file_name;
	info->line 	= line;
	info->lock_type = lock_type;
	info->thread_id = os_thread_get_curr_id();
	info->pass	= pass;

	UT_LIST_ADD_FIRST(list, lock->debug_list, info);	

	rw_lock_debug_mutex_exit();

	if ((pass == 0) && (lock_type != RW_LOCK_WAIT_EX)) {
		sync_thread_add_level(lock, lock->level);
	}
}	

/**********************************************************************
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 */
{
	rw_lock_debug_t*	info;

	ut_ad(lock);

	if ((pass == 0) && (lock_type != RW_LOCK_WAIT_EX)) {
		sync_thread_reset_level(lock);
	}

	rw_lock_debug_mutex_enter();

	info = UT_LIST_GET_FIRST(lock->debug_list);

	while (info != NULL) {
		if ((pass == info->pass)
		    && ((pass != 0)
unknown's avatar
unknown committed
648 649
			|| os_thread_eq(info->thread_id,
						os_thread_get_curr_id()))
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
		    && (info->lock_type == lock_type)) {

		    	/* Found! */
		    	UT_LIST_REMOVE(list, lock->debug_list, info);
			rw_lock_debug_mutex_exit();

		    	rw_lock_debug_free(info);

		    	return;
		}

		info = UT_LIST_GET_NEXT(list, info);
	}

	ut_error;
}
666
#endif /* UNIV_SYNC_DEBUG */
667 668 669 670 671 672 673 674 675 676 677 678 679

/**********************************************************************
Sets the rw-lock latching level field. */

void
rw_lock_set_level(
/*==============*/
	rw_lock_t*	lock,	/* in: rw-lock */
	ulint		level)	/* in: level */
{
	lock->level = level;
}

680
#ifdef UNIV_SYNC_DEBUG
681 682 683 684 685 686 687 688 689
/**********************************************************************
Checks if the thread has locked the rw-lock in the specified mode, with
the pass value == 0. */

ibool
rw_lock_own(
/*========*/
					/* out: TRUE if locked */
	rw_lock_t*	lock,		/* in: rw-lock */
unknown's avatar
unknown committed
690 691
	ulint		lock_type)	/* in: lock type: RW_LOCK_SHARED,
					RW_LOCK_EX */
692 693 694 695 696 697 698 699 700 701 702 703
{
	rw_lock_debug_t*	info;

	ut_ad(lock);
	ut_ad(rw_lock_validate(lock));

	mutex_enter(&(lock->mutex));

	info = UT_LIST_GET_FIRST(lock->debug_list);

	while (info != NULL) {

unknown's avatar
unknown committed
704
		if (os_thread_eq(info->thread_id, os_thread_get_curr_id())
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
		    && (info->pass == 0)
		    && (info->lock_type == lock_type)) {

			mutex_exit(&(lock->mutex));
		    	/* Found! */

		    	return(TRUE);
		}

		info = UT_LIST_GET_NEXT(list, info);
	}
	mutex_exit(&(lock->mutex));

	return(FALSE);
}
720
#endif /* UNIV_SYNC_DEBUG */
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756

/**********************************************************************
Checks if somebody has locked the rw-lock in the specified mode. */

ibool
rw_lock_is_locked(
/*==============*/
					/* out: TRUE if locked */
	rw_lock_t*	lock,		/* in: rw-lock */
	ulint		lock_type)	/* in: lock type: RW_LOCK_SHARED,
					RW_LOCK_EX */
{
	ibool	ret	= FALSE;

	ut_ad(lock);
	ut_ad(rw_lock_validate(lock));
	
	mutex_enter(&(lock->mutex));

	if (lock_type == RW_LOCK_SHARED) {
		if (lock->reader_count > 0) {
			ret = TRUE;
		}
	} else if (lock_type == RW_LOCK_EX) {
		if (lock->writer == RW_LOCK_EX) {
			ret = TRUE;
		}
	} else {
		ut_error;
	}

	mutex_exit(&(lock->mutex));

	return(ret);
}

757
#ifdef UNIV_SYNC_DEBUG
758 759 760 761 762 763 764 765 766 767 768 769 770
/*******************************************************************
Prints debug info of currently locked rw-locks. */

void
rw_lock_list_print_info(void)
/*=========================*/
{
	rw_lock_t*	lock;
	ulint		count		= 0;
	rw_lock_debug_t* info;
	
	mutex_enter(&rw_lock_list_mutex);

771 772 773
	fputs("-------------\n"
		"RW-LATCH INFO\n"
		"-------------\n", stderr);
774 775 776 777 778 779 780 781 782 783 784 785 786

	lock = UT_LIST_GET_FIRST(rw_lock_list);

	while (lock != NULL) {

		count++;

		mutex_enter(&(lock->mutex));

		if ((rw_lock_get_writer(lock) != RW_LOCK_NOT_LOCKED)
		    || (rw_lock_get_reader_count(lock) != 0)
		    || (rw_lock_get_waiters(lock) != 0)) {

787
			fprintf(stderr, "RW-LOCK: %p ", lock);
788 789

			if (rw_lock_get_waiters(lock)) {
790
				fputs(" Waiters for the lock exist\n", stderr);
791
			} else {
792
				putc('\n', stderr);
793 794 795 796 797 798 799 800 801 802 803 804 805
			}
		    
			info = UT_LIST_GET_FIRST(lock->debug_list);
			while (info != NULL) {	
				rw_lock_debug_print(info);
				info = UT_LIST_GET_NEXT(list, info);
			}
		}

		mutex_exit(&(lock->mutex));
		lock = UT_LIST_GET_NEXT(list, lock);
	}

806
	fprintf(stderr, "Total number of rw-locks %ld\n", count);
807 808 809 810 811 812 813 814 815
	mutex_exit(&rw_lock_list_mutex);
}

/*******************************************************************
Prints debug info of an rw-lock. */

void
rw_lock_print(
/*==========*/
816
	rw_lock_t*	lock)	/* in: rw-lock */
817 818 819
{
	rw_lock_debug_t* info;
	
820 821 822 823
	fprintf(stderr,
		"-------------\n"
		"RW-LATCH INFO\n"
		"RW-LATCH: %p ", lock);
824 825 826 827 828 829

	if ((rw_lock_get_writer(lock) != RW_LOCK_NOT_LOCKED)
	    || (rw_lock_get_reader_count(lock) != 0)
	    || (rw_lock_get_waiters(lock) != 0)) {

		if (rw_lock_get_waiters(lock)) {
830
			fputs(" Waiters for the lock exist\n", stderr);
831
		} else {
832
			putc('\n', stderr);
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
		}
		    
		info = UT_LIST_GET_FIRST(lock->debug_list);
		while (info != NULL) {	
			rw_lock_debug_print(info);
			info = UT_LIST_GET_NEXT(list, info);
		}
	}
}

/*************************************************************************
Prints info of a debug struct. */

void
rw_lock_debug_print(
/*================*/
	rw_lock_debug_t*	info)	/* in: debug struct */
{
	ulint	rwt;

	rwt 	  = info->lock_type;	
			
855
	fprintf(stderr, "Locked: thread %ld file %s line %ld  ",
856 857
		(ulong) os_thread_pf(info->thread_id), info->file_name,
	        (ulong) info->line);
858
	if (rwt == RW_LOCK_SHARED) {
859
		fputs("S-LOCK", stderr);
860
	} else if (rwt == RW_LOCK_EX) {
861
		fputs("X-LOCK", stderr);
862
	} else if (rwt == RW_LOCK_WAIT_EX) {
863
		fputs("WAIT X-LOCK", stderr);
864 865 866 867
	} else {
		ut_error;
	}
	if (info->pass != 0) {
unknown's avatar
unknown committed
868
		fprintf(stderr, " pass value %lu", (ulong) info->pass);
869
	}
870
	putc('\n', stderr);
871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
}

/*******************************************************************
Returns the number of currently locked rw-locks. Works only in the debug
version. */

ulint
rw_lock_n_locked(void)
/*==================*/
{
	rw_lock_t*	lock;
	ulint		count		= 0;
	
	mutex_enter(&rw_lock_list_mutex);

	lock = UT_LIST_GET_FIRST(rw_lock_list);

	while (lock != NULL) {
		mutex_enter(rw_lock_get_mutex(lock));

		if ((rw_lock_get_writer(lock) != RW_LOCK_NOT_LOCKED)
				|| (rw_lock_get_reader_count(lock) != 0)) {
			count++;
		}

		mutex_exit(rw_lock_get_mutex(lock));
		lock = UT_LIST_GET_NEXT(list, lock);
	}

	mutex_exit(&rw_lock_list_mutex);

	return(count);
}
904
#endif /* UNIV_SYNC_DEBUG */