Drizzled Public API Documentation

sync0rw.cc
00001 /*****************************************************************************
00002 
00003 Copyright (C) 1995, 2009, Innobase Oy. All Rights Reserved.
00004 Copyright (C) 2008, Google Inc.
00005 
00006 Portions of this file contain modifications contributed and copyrighted by
00007 Google, Inc. Those modifications are gratefully acknowledged and are described
00008 briefly in the InnoDB documentation. The contributions by Google are
00009 incorporated with their permission, and subject to the conditions contained in
00010 the file COPYING.Google.
00011 
00012 This program is free software; you can redistribute it and/or modify it under
00013 the terms of the GNU General Public License as published by the Free Software
00014 Foundation; version 2 of the License.
00015 
00016 This program is distributed in the hope that it will be useful, but WITHOUT
00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00018 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License along with
00021 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
00022 St, Fifth Floor, Boston, MA 02110-1301 USA
00023 
00024 *****************************************************************************/
00025 
00026 /**************************************************/
00033 #include "sync0rw.h"
00034 #ifdef UNIV_NONINL
00035 #include "sync0rw.ic"
00036 #endif
00037 
00038 #include "os0thread.h"
00039 #include "mem0mem.h"
00040 #include "srv0srv.h"
00041 #include "os0sync.h" /* for INNODB_RW_LOCKS_USE_ATOMICS */
00042 
00043 /*
00044   IMPLEMENTATION OF THE RW_LOCK
00045   =============================
00046 The status of a rw_lock is held in lock_word. The initial value of lock_word is
00047 X_LOCK_DECR. lock_word is decremented by 1 for each s-lock and by X_LOCK_DECR
00048 for each x-lock. This describes the lock state for each value of lock_word:
00049 
00050 lock_word == X_LOCK_DECR:      Unlocked.
00051 0 < lock_word < X_LOCK_DECR:   Read locked, no waiting writers.
00052              (X_LOCK_DECR - lock_word) is the
00053              number of readers that hold the lock.
00054 lock_word == 0:          Write locked
00055 -X_LOCK_DECR < lock_word < 0:  Read locked, with a waiting writer.
00056              (-lock_word) is the number of readers
00057              that hold the lock.
00058 lock_word <= -X_LOCK_DECR:     Recursively write locked. lock_word has been
00059              decremented by X_LOCK_DECR once for each lock,
00060              so the number of locks is:
00061              ((-lock_word) / X_LOCK_DECR) + 1
00062 When lock_word <= -X_LOCK_DECR, we also know that lock_word % X_LOCK_DECR == 0:
00063 other values of lock_word are invalid.
00064 
00065 The lock_word is always read and updated atomically and consistently, so that
00066 it always represents the state of the lock, and the state of the lock changes
00067 with a single atomic operation. This lock_word holds all of the information
00068 that a thread needs in order to determine if it is eligible to gain the lock
00069 or if it must spin or sleep. The one exception to this is that writer_thread
00070 must be verified before recursive write locks: to solve this scenario, we make
00071 writer_thread readable by all threads, but only writeable by the x-lock holder.
00072 
00073 The other members of the lock obey the following rules to remain consistent:
00074 
00075 recursive:  This and the writer_thread field together control the
00076     behaviour of recursive x-locking.
00077     lock->recursive must be FALSE in following states:
00078       1) The writer_thread contains garbage i.e.: the
00079       lock has just been initialized.
00080       2) The lock is not x-held and there is no
00081       x-waiter waiting on WAIT_EX event.
00082       3) The lock is x-held or there is an x-waiter
00083       waiting on WAIT_EX event but the 'pass' value
00084       is non-zero.
00085     lock->recursive is TRUE iff:
00086       1) The lock is x-held or there is an x-waiter
00087       waiting on WAIT_EX event and the 'pass' value
00088       is zero.
00089     This flag must be set after the writer_thread field
00090     has been updated with a memory ordering barrier.
00091     It is unset before the lock_word has been incremented.
00092 writer_thread:  Is used only in recursive x-locking. Can only be safely
00093     read iff lock->recursive flag is TRUE.
00094     This field is uninitialized at lock creation time and
00095     is updated atomically when x-lock is acquired or when
00096     move_ownership is called. A thread is only allowed to
00097     set the value of this field to it's thread_id i.e.: a
00098     thread cannot set writer_thread to some other thread's
00099     id.
00100 waiters:  May be set to 1 anytime, but to avoid unnecessary wake-up
00101     signals, it should only be set to 1 when there are threads
00102     waiting on event. Must be 1 when a writer starts waiting to
00103     ensure the current x-locking thread sends a wake-up signal
00104     during unlock. May only be reset to 0 immediately before a
00105     a wake-up signal is sent to event. On most platforms, a
00106     memory barrier is required after waiters is set, and before
00107     verifying lock_word is still held, to ensure some unlocker
00108     really does see the flags new value.
00109 event:    Threads wait on event for read or writer lock when another
00110     thread has an x-lock or an x-lock reservation (wait_ex). A
00111     thread may only wait on event after performing the following
00112     actions in order:
00113        (1) Record the counter value of event (with os_event_reset).
00114        (2) Set waiters to 1.
00115        (3) Verify lock_word <= 0.
00116     (1) must come before (2) to ensure signal is not missed.
00117     (2) must come before (3) to ensure a signal is sent.
00118     These restrictions force the above ordering.
00119     Immediately before sending the wake-up signal, we should:
00120        (1) Verify lock_word == X_LOCK_DECR (unlocked)
00121        (2) Reset waiters to 0.
00122 wait_ex_event:  A thread may only wait on the wait_ex_event after it has
00123     performed the following actions in order:
00124        (1) Decrement lock_word by X_LOCK_DECR.
00125        (2) Record counter value of wait_ex_event (os_event_reset,
00126                        called from sync_array_reserve_cell).
00127        (3) Verify that lock_word < 0.
00128     (1) must come first to ensures no other threads become reader
00129                 or next writer, and notifies unlocker that signal must be sent.
00130                 (2) must come before (3) to ensure the signal is not missed.
00131     These restrictions force the above ordering.
00132     Immediately before sending the wake-up signal, we should:
00133        Verify lock_word == 0 (waiting thread holds x_lock)
00134 */
00135 
00136 
00139 UNIV_INTERN ib_int64_t  rw_s_spin_wait_count  = 0;
00142 UNIV_INTERN ib_int64_t  rw_s_spin_round_count = 0;
00143 
00146 UNIV_INTERN ib_int64_t  rw_s_os_wait_count  = 0;
00147 
00150 UNIV_INTERN ib_int64_t  rw_s_exit_count   = 0;
00151 
00154 UNIV_INTERN ib_int64_t  rw_x_spin_wait_count  = 0;
00157 UNIV_INTERN ib_int64_t  rw_x_spin_round_count = 0;
00158 
00161 UNIV_INTERN ib_int64_t  rw_x_os_wait_count  = 0;
00162 
00165 UNIV_INTERN ib_int64_t  rw_x_exit_count   = 0;
00166 
00167 /* The global list of rw-locks */
00168 UNIV_INTERN rw_lock_list_t  rw_lock_list;
00169 UNIV_INTERN mutex_t   rw_lock_list_mutex;
00170 
00171 #ifdef UNIV_PFS_MUTEX
00172 UNIV_INTERN mysql_pfs_key_t rw_lock_list_mutex_key;
00173 UNIV_INTERN mysql_pfs_key_t rw_lock_mutex_key;
00174 #endif /* UNIV_PFS_MUTEX */
00175 
00176 #ifdef UNIV_SYNC_DEBUG
00177 /* The global mutex which protects debug info lists of all rw-locks.
00178 To modify the debug info list of an rw-lock, this mutex has to be
00179 acquired in addition to the mutex protecting the lock. */
00180 
00181 UNIV_INTERN mutex_t   rw_lock_debug_mutex;
00182 
00183 # ifdef UNIV_PFS_MUTEX
00184 UNIV_INTERN mysql_pfs_key_t rw_lock_debug_mutex_key;
00185 # endif
00186 
00187 /* If deadlock detection does not get immediately the mutex,
00188 it may wait for this event */
00189 UNIV_INTERN os_event_t    rw_lock_debug_event;
00190 /* This is set to TRUE, if there may be waiters for the event */
00191 UNIV_INTERN ibool   rw_lock_debug_waiters;
00192 
00193 /******************************************************************/
00195 static
00196 rw_lock_debug_t*
00197 rw_lock_debug_create(void);
00198 /*======================*/
00199 /******************************************************************/
00201 static
00202 void
00203 rw_lock_debug_free(
00204 /*===============*/
00205   rw_lock_debug_t* info);
00206 
00207 /******************************************************************/
00210 static
00211 rw_lock_debug_t*
00212 rw_lock_debug_create(void)
00213 /*======================*/
00214 {
00215   return((rw_lock_debug_t*) mem_alloc(sizeof(rw_lock_debug_t)));
00216 }
00217 
00218 /******************************************************************/
00220 static
00221 void
00222 rw_lock_debug_free(
00223 /*===============*/
00224   rw_lock_debug_t* info)
00225 {
00226   mem_free(info);
00227 }
00228 #endif /* UNIV_SYNC_DEBUG */
00229 
00230 /******************************************************************/
00235 UNIV_INTERN
00236 void
00237 rw_lock_create_func(
00238 /*================*/
00239   rw_lock_t*  lock,   
00240 #ifdef UNIV_DEBUG
00241 # ifdef UNIV_SYNC_DEBUG
00242   ulint   level,    
00243 # endif /* UNIV_SYNC_DEBUG */
00244   const char* cmutex_name,  
00245 #endif /* UNIV_DEBUG */
00246   const char* cfile_name, 
00247   ulint   cline)    
00248 {
00249   /* If this is the very first time a synchronization object is
00250   created, then the following call initializes the sync system. */
00251 
00252 #ifndef INNODB_RW_LOCKS_USE_ATOMICS
00253   mutex_create(rw_lock_mutex_key, rw_lock_get_mutex(lock),
00254          SYNC_NO_ORDER_CHECK);
00255 
00256   lock->mutex.cfile_name = cfile_name;
00257   lock->mutex.cline = cline;
00258 
00259   ut_d(lock->mutex.cmutex_name = cmutex_name);
00260   ut_d(lock->mutex.mutex_type = 1);
00261 #else /* INNODB_RW_LOCKS_USE_ATOMICS */
00262 # ifdef UNIV_DEBUG
00263   UT_NOT_USED(cmutex_name);
00264 # endif
00265 #endif /* INNODB_RW_LOCKS_USE_ATOMICS */
00266 
00267   lock->lock_word = X_LOCK_DECR;
00268   lock->waiters = 0;
00269 
00270   /* We set this value to signify that lock->writer_thread
00271   contains garbage at initialization and cannot be used for
00272   recursive x-locking. */
00273   lock->recursive = FALSE;
00274 
00275 #ifdef UNIV_SYNC_DEBUG
00276   UT_LIST_INIT(lock->debug_list);
00277 
00278   lock->level = level;
00279 #endif /* UNIV_SYNC_DEBUG */
00280 
00281   ut_d(lock->magic_n = RW_LOCK_MAGIC_N);
00282 
00283   lock->cfile_name = cfile_name;
00284   lock->cline = (unsigned int) cline;
00285 
00286   lock->count_os_wait = 0;
00287   lock->last_s_file_name = "not yet reserved";
00288   lock->last_x_file_name = "not yet reserved";
00289   lock->last_s_line = 0;
00290   lock->last_x_line = 0;
00291   lock->event = os_event_create(NULL);
00292   lock->wait_ex_event = os_event_create(NULL);
00293 
00294   mutex_enter(&rw_lock_list_mutex);
00295 
00296   ut_ad(UT_LIST_GET_FIRST(rw_lock_list) == NULL
00297         || UT_LIST_GET_FIRST(rw_lock_list)->magic_n == RW_LOCK_MAGIC_N);
00298 
00299   UT_LIST_ADD_FIRST(list, rw_lock_list, lock);
00300 
00301   mutex_exit(&rw_lock_list_mutex);
00302 }
00303 
00304 /******************************************************************/
00308 UNIV_INTERN
00309 void
00310 rw_lock_free_func(
00311 /*==============*/
00312   rw_lock_t*  lock) 
00313 {
00314   ut_ad(rw_lock_validate(lock));
00315   ut_a(lock->lock_word == X_LOCK_DECR);
00316 
00317 #ifndef INNODB_RW_LOCKS_USE_ATOMICS
00318   mutex_free(rw_lock_get_mutex(lock));
00319 #endif /* INNODB_RW_LOCKS_USE_ATOMICS */
00320 
00321   mutex_enter(&rw_lock_list_mutex);
00322   os_event_free(lock->event);
00323 
00324   os_event_free(lock->wait_ex_event);
00325 
00326   ut_ad(UT_LIST_GET_PREV(list, lock) == NULL
00327         || UT_LIST_GET_PREV(list, lock)->magic_n == RW_LOCK_MAGIC_N);
00328   ut_ad(UT_LIST_GET_NEXT(list, lock) == NULL
00329         || UT_LIST_GET_NEXT(list, lock)->magic_n == RW_LOCK_MAGIC_N);
00330 
00331   UT_LIST_REMOVE(list, rw_lock_list, lock);
00332 
00333   mutex_exit(&rw_lock_list_mutex);
00334 
00335   ut_d(lock->magic_n = 0);
00336 }
00337 
00338 #ifdef UNIV_DEBUG
00339 /******************************************************************/
00343 UNIV_INTERN
00344 ibool
00345 rw_lock_validate(
00346 /*=============*/
00347   rw_lock_t*  lock) 
00348 {
00349   ulint waiters;
00350   lint  lock_word;
00351 
00352   ut_a(lock);
00353 
00354   waiters = rw_lock_get_waiters(lock);
00355   lock_word = lock->lock_word;
00356 
00357   ut_ad(lock->magic_n == RW_LOCK_MAGIC_N);
00358   ut_a(waiters == 0 || waiters == 1);
00359   ut_a(lock_word > -X_LOCK_DECR ||(-lock_word) % X_LOCK_DECR == 0);
00360 
00361   return(TRUE);
00362 }
00363 #endif /* UNIV_DEBUG */
00364 
00365 /******************************************************************/
00370 UNIV_INTERN
00371 void
00372 rw_lock_s_lock_spin(
00373 /*================*/
00374   rw_lock_t*  lock, 
00375   ulint   pass, 
00377   const char* file_name, 
00378   ulint   line) 
00379 {
00380   ulint  index; /* index of the reserved wait cell */
00381   ulint  i = 0; /* spin round count */
00382 
00383   ut_ad(rw_lock_validate(lock));
00384 
00385   rw_s_spin_wait_count++; 
00386 lock_loop:
00387 
00388   /* Spin waiting for the writer field to become free */
00389   while (i < SYNC_SPIN_ROUNDS && lock->lock_word <= 0) {
00390     if (srv_spin_wait_delay) {
00391       ut_delay(ut_rnd_interval(0, srv_spin_wait_delay));
00392     }
00393 
00394     i++;
00395   }
00396 
00397   if (i == SYNC_SPIN_ROUNDS) {
00398     os_thread_yield();
00399   }
00400 
00401   if (srv_print_latch_waits) {
00402     fprintf(stderr,
00403       "Thread %lu spin wait rw-s-lock at %p"
00404       " cfile %s cline %lu rnds %lu\n",
00405       (ulong) os_thread_pf(os_thread_get_curr_id()),
00406       (void*) lock,
00407       lock->cfile_name, (ulong) lock->cline, (ulong) i);
00408   }
00409 
00410   /* We try once again to obtain the lock */
00411   if (TRUE == rw_lock_s_lock_low(lock, pass, file_name, line)) {
00412     rw_s_spin_round_count += i;
00413 
00414     return; /* Success */
00415   } else {
00416 
00417     if (i < SYNC_SPIN_ROUNDS) {
00418       goto lock_loop;
00419     }
00420 
00421     rw_s_spin_round_count += i;
00422 
00423     sync_array_reserve_cell(sync_primary_wait_array,
00424           lock, RW_LOCK_SHARED,
00425           file_name, line,
00426           &index);
00427 
00428     /* Set waiters before checking lock_word to ensure wake-up
00429                 signal is sent. This may lead to some unnecessary signals. */
00430     rw_lock_set_waiter_flag(lock);
00431 
00432     if (TRUE == rw_lock_s_lock_low(lock, pass, file_name, line)) {
00433       sync_array_free_cell(sync_primary_wait_array, index);
00434       return; /* Success */
00435     }
00436 
00437     if (srv_print_latch_waits) {
00438       fprintf(stderr,
00439         "Thread %lu OS wait rw-s-lock at %p"
00440         " cfile %s cline %lu\n",
00441         os_thread_pf(os_thread_get_curr_id()),
00442         (void*) lock, lock->cfile_name,
00443         (ulong) lock->cline);
00444     }
00445 
00446     /* these stats may not be accurate */
00447     lock->count_os_wait++;
00448     rw_s_os_wait_count++;
00449 
00450     sync_array_wait_event(sync_primary_wait_array, index);
00451 
00452     i = 0;
00453     goto lock_loop;
00454   }
00455 }
00456 
00457 /******************************************************************/
00465 UNIV_INTERN
00466 void
00467 rw_lock_x_lock_move_ownership(
00468 /*==========================*/
00469   rw_lock_t*  lock) 
00471 {
00472   ut_ad(rw_lock_is_locked(lock, RW_LOCK_EX));
00473 
00474   rw_lock_set_writer_id_and_recursion_flag(lock, TRUE);
00475 }
00476 
00477 /******************************************************************/
00480 UNIV_INLINE
00481 void
00482 rw_lock_x_lock_wait(
00483 /*================*/
00484   rw_lock_t*  lock, 
00485 #ifdef UNIV_SYNC_DEBUG
00486   ulint   pass, 
00488 #endif
00489   const char* file_name,
00490   ulint   line) 
00491 {
00492   ulint index;
00493   ulint i = 0;
00494 
00495   ut_ad(lock->lock_word <= 0);
00496 
00497   while (lock->lock_word < 0) {
00498     if (srv_spin_wait_delay) {
00499       ut_delay(ut_rnd_interval(0, srv_spin_wait_delay));
00500     }
00501     if(i < SYNC_SPIN_ROUNDS) {
00502       i++;
00503       continue;
00504     }
00505 
00506     /* If there is still a reader, then go to sleep.*/
00507     rw_x_spin_round_count += i;
00508     i = 0;
00509     sync_array_reserve_cell(sync_primary_wait_array,
00510           lock,
00511           RW_LOCK_WAIT_EX,
00512           file_name, line,
00513           &index);
00514     /* Check lock_word to ensure wake-up isn't missed.*/
00515     if(lock->lock_word < 0) {
00516 
00517       /* these stats may not be accurate */
00518       lock->count_os_wait++;
00519       rw_x_os_wait_count++;
00520 
00521                         /* Add debug info as it is needed to detect possible
00522                         deadlock. We must add info for WAIT_EX thread for
00523                         deadlock detection to work properly. */
00524 #ifdef UNIV_SYNC_DEBUG
00525       rw_lock_add_debug_info(lock, pass, RW_LOCK_WAIT_EX,
00526                  file_name, line);
00527 #endif
00528 
00529       sync_array_wait_event(sync_primary_wait_array,
00530                 index);
00531 #ifdef UNIV_SYNC_DEBUG
00532       rw_lock_remove_debug_info(lock, pass,
00533                  RW_LOCK_WAIT_EX);
00534 #endif
00535                         /* It is possible to wake when lock_word < 0.
00536                         We must pass the while-loop check to proceed.*/
00537     } else {
00538       sync_array_free_cell(sync_primary_wait_array,
00539                index);
00540     }
00541   }
00542   rw_x_spin_round_count += i;
00543 }
00544 
00545 /******************************************************************/
00548 UNIV_INLINE
00549 ibool
00550 rw_lock_x_lock_low(
00551 /*===============*/
00552   rw_lock_t*  lock, 
00553   ulint   pass, 
00555   const char* file_name,
00556   ulint   line) 
00557 {
00558   os_thread_id_t  curr_thread = os_thread_get_curr_id();
00559 
00560   if (rw_lock_lock_word_decr(lock, X_LOCK_DECR)) {
00561 
00562     /* lock->recursive also tells us if the writer_thread
00563     field is stale or active. As we are going to write
00564     our own thread id in that field it must be that the
00565     current writer_thread value is not active. */
00566     ut_a(!lock->recursive);
00567 
00568     /* Decrement occurred: we are writer or next-writer. */
00569     rw_lock_set_writer_id_and_recursion_flag(lock,
00570             pass ? FALSE : TRUE);
00571 
00572     rw_lock_x_lock_wait(lock,
00573 #ifdef UNIV_SYNC_DEBUG
00574             pass,
00575 #endif
00576                                     file_name, line);
00577 
00578   } else {
00579     /* Decrement failed: relock or failed lock */
00580     if (!pass && lock->recursive
00581         && os_thread_eq(lock->writer_thread, curr_thread)) {
00582       /* Relock */
00583                         lock->lock_word -= X_LOCK_DECR;
00584     } else {
00585       /* Another thread locked before us */
00586       return(FALSE);
00587     }
00588   }
00589 #ifdef UNIV_SYNC_DEBUG
00590   rw_lock_add_debug_info(lock, pass, RW_LOCK_EX,
00591              file_name, line);
00592 #endif
00593   lock->last_x_file_name = file_name;
00594   lock->last_x_line = (unsigned int) line;
00595 
00596   return(TRUE);
00597 }
00598 
00599 /******************************************************************/
00608 UNIV_INTERN
00609 void
00610 rw_lock_x_lock_func(
00611 /*================*/
00612   rw_lock_t*  lock, 
00613   ulint   pass, 
00615   const char* file_name,
00616   ulint   line) 
00617 {
00618   ulint index;  
00619   ulint i;  
00620   ibool spinning = FALSE;
00621 
00622   ut_ad(rw_lock_validate(lock));
00623 
00624   i = 0;
00625 
00626 lock_loop:
00627 
00628   if (rw_lock_x_lock_low(lock, pass, file_name, line)) {
00629     rw_x_spin_round_count += i;
00630 
00631     return; /* Locking succeeded */
00632 
00633   } else {
00634 
00635                 if (!spinning) {
00636                         spinning = TRUE;
00637                         rw_x_spin_wait_count++;
00638     }
00639 
00640     /* Spin waiting for the lock_word to become free */
00641     while (i < SYNC_SPIN_ROUNDS
00642            && lock->lock_word <= 0) {
00643       if (srv_spin_wait_delay) {
00644         ut_delay(ut_rnd_interval(0,
00645                srv_spin_wait_delay));
00646       }
00647 
00648       i++;
00649     }
00650     if (i == SYNC_SPIN_ROUNDS) {
00651       os_thread_yield();
00652     } else {
00653       goto lock_loop;
00654     }
00655   }
00656 
00657   rw_x_spin_round_count += i;
00658 
00659   if (srv_print_latch_waits) {
00660     fprintf(stderr,
00661       "Thread %lu spin wait rw-x-lock at %p"
00662       " cfile %s cline %lu rnds %lu\n",
00663       os_thread_pf(os_thread_get_curr_id()), (void*) lock,
00664       lock->cfile_name, (ulong) lock->cline, (ulong) i);
00665   }
00666 
00667   sync_array_reserve_cell(sync_primary_wait_array,
00668         lock,
00669         RW_LOCK_EX,
00670         file_name, line,
00671         &index);
00672 
00673   /* Waiters must be set before checking lock_word, to ensure signal
00674   is sent. This could lead to a few unnecessary wake-up signals. */
00675   rw_lock_set_waiter_flag(lock);
00676 
00677   if (rw_lock_x_lock_low(lock, pass, file_name, line)) {
00678     sync_array_free_cell(sync_primary_wait_array, index);
00679     return; /* Locking succeeded */
00680   }
00681 
00682   if (srv_print_latch_waits) {
00683     fprintf(stderr,
00684       "Thread %lu OS wait for rw-x-lock at %p"
00685       " cfile %s cline %lu\n",
00686       os_thread_pf(os_thread_get_curr_id()), (void*) lock,
00687       lock->cfile_name, (ulong) lock->cline);
00688   }
00689 
00690   /* these stats may not be accurate */
00691   lock->count_os_wait++;
00692   rw_x_os_wait_count++;
00693 
00694   sync_array_wait_event(sync_primary_wait_array, index);
00695 
00696   i = 0;
00697   goto lock_loop;
00698 }
00699 
00700 #ifdef UNIV_SYNC_DEBUG
00701 /******************************************************************/
00707 UNIV_INTERN
00708 void
00709 rw_lock_debug_mutex_enter(void)
00710 /*==========================*/
00711 {
00712 loop:
00713   if (0 == mutex_enter_nowait(&rw_lock_debug_mutex)) {
00714     return;
00715   }
00716 
00717   os_event_reset(rw_lock_debug_event);
00718 
00719   rw_lock_debug_waiters = TRUE;
00720 
00721   if (0 == mutex_enter_nowait(&rw_lock_debug_mutex)) {
00722     return;
00723   }
00724 
00725   os_event_wait(rw_lock_debug_event);
00726 
00727   goto loop;
00728 }
00729 
00730 /******************************************************************/
00732 UNIV_INTERN
00733 void
00734 rw_lock_debug_mutex_exit(void)
00735 /*==========================*/
00736 {
00737   mutex_exit(&rw_lock_debug_mutex);
00738 
00739   if (rw_lock_debug_waiters) {
00740     rw_lock_debug_waiters = FALSE;
00741     os_event_set(rw_lock_debug_event);
00742   }
00743 }
00744 
00745 /******************************************************************/
00747 UNIV_INTERN
00748 void
00749 rw_lock_add_debug_info(
00750 /*===================*/
00751   rw_lock_t*  lock,   
00752   ulint   pass,   
00753   ulint   lock_type,  
00754   const char* file_name,  
00755   ulint   line)   
00756 {
00757   rw_lock_debug_t*  info;
00758 
00759   ut_ad(lock);
00760   ut_ad(file_name);
00761 
00762   info = rw_lock_debug_create();
00763 
00764   rw_lock_debug_mutex_enter();
00765 
00766   info->file_name = file_name;
00767   info->line  = line;
00768   info->lock_type = lock_type;
00769   info->thread_id = os_thread_get_curr_id();
00770   info->pass  = pass;
00771 
00772   UT_LIST_ADD_FIRST(list, lock->debug_list, info);
00773 
00774   rw_lock_debug_mutex_exit();
00775 
00776   if ((pass == 0) && (lock_type != RW_LOCK_WAIT_EX)) {
00777     sync_thread_add_level(lock, lock->level);
00778   }
00779 }
00780 
00781 /******************************************************************/
00783 UNIV_INTERN
00784 void
00785 rw_lock_remove_debug_info(
00786 /*======================*/
00787   rw_lock_t*  lock,   
00788   ulint   pass,   
00789   ulint   lock_type)  
00790 {
00791   rw_lock_debug_t*  info;
00792 
00793   ut_ad(lock);
00794 
00795   if ((pass == 0) && (lock_type != RW_LOCK_WAIT_EX)) {
00796     sync_thread_reset_level(lock);
00797   }
00798 
00799   rw_lock_debug_mutex_enter();
00800 
00801   info = UT_LIST_GET_FIRST(lock->debug_list);
00802 
00803   while (info != NULL) {
00804     if ((pass == info->pass)
00805         && ((pass != 0)
00806       || os_thread_eq(info->thread_id,
00807           os_thread_get_curr_id()))
00808         && (info->lock_type == lock_type)) {
00809 
00810       /* Found! */
00811       UT_LIST_REMOVE(list, lock->debug_list, info);
00812       rw_lock_debug_mutex_exit();
00813 
00814       rw_lock_debug_free(info);
00815 
00816       return;
00817     }
00818 
00819     info = UT_LIST_GET_NEXT(list, info);
00820   }
00821 
00822   ut_error;
00823 }
00824 #endif /* UNIV_SYNC_DEBUG */
00825 
00826 #ifdef UNIV_SYNC_DEBUG
00827 /******************************************************************/
00831 UNIV_INTERN
00832 ibool
00833 rw_lock_own(
00834 /*========*/
00835   rw_lock_t*  lock,   
00836   ulint   lock_type)  
00838 {
00839   rw_lock_debug_t*  info;
00840 
00841   ut_ad(lock);
00842   ut_ad(rw_lock_validate(lock));
00843 
00844   rw_lock_debug_mutex_enter();
00845 
00846   info = UT_LIST_GET_FIRST(lock->debug_list);
00847 
00848   while (info != NULL) {
00849 
00850     if (os_thread_eq(info->thread_id, os_thread_get_curr_id())
00851         && (info->pass == 0)
00852         && (info->lock_type == lock_type)) {
00853 
00854       rw_lock_debug_mutex_exit();
00855       /* Found! */
00856 
00857       return(TRUE);
00858     }
00859 
00860     info = UT_LIST_GET_NEXT(list, info);
00861   }
00862   rw_lock_debug_mutex_exit();
00863 
00864   return(FALSE);
00865 }
00866 #endif /* UNIV_SYNC_DEBUG */
00867 
00868 /******************************************************************/
00871 UNIV_INTERN
00872 ibool
00873 rw_lock_is_locked(
00874 /*==============*/
00875   rw_lock_t*  lock,   
00876   ulint   lock_type)  
00878 {
00879   ibool ret = FALSE;
00880 
00881   ut_ad(lock);
00882   ut_ad(rw_lock_validate(lock));
00883 
00884   if (lock_type == RW_LOCK_SHARED) {
00885     if (rw_lock_get_reader_count(lock) > 0) {
00886       ret = TRUE;
00887     }
00888   } else if (lock_type == RW_LOCK_EX) {
00889     if (rw_lock_get_writer(lock) == RW_LOCK_EX) {
00890       ret = TRUE;
00891     }
00892   } else {
00893     ut_error;
00894   }
00895 
00896   return(ret);
00897 }
00898 
00899 #ifdef UNIV_SYNC_DEBUG
00900 /***************************************************************/
00902 UNIV_INTERN
00903 void
00904 rw_lock_list_print_info(
00905 /*====================*/
00906   FILE* file)   
00907 {
00908   rw_lock_t*  lock;
00909   ulint   count   = 0;
00910   rw_lock_debug_t* info;
00911 
00912   mutex_enter(&rw_lock_list_mutex);
00913 
00914   fputs("-------------\n"
00915         "RW-LATCH INFO\n"
00916         "-------------\n", file);
00917 
00918   lock = UT_LIST_GET_FIRST(rw_lock_list);
00919 
00920   while (lock != NULL) {
00921 
00922     count++;
00923 
00924 #ifndef INNODB_RW_LOCKS_USE_ATOMICS
00925     mutex_enter(&(lock->mutex));
00926 #endif
00927     if (lock->lock_word != X_LOCK_DECR) {
00928 
00929       fprintf(file, "RW-LOCK: %p ", (void*) lock);
00930 
00931       if (rw_lock_get_waiters(lock)) {
00932         fputs(" Waiters for the lock exist\n", file);
00933       } else {
00934         putc('\n', file);
00935       }
00936 
00937       info = UT_LIST_GET_FIRST(lock->debug_list);
00938       while (info != NULL) {
00939         rw_lock_debug_print(info);
00940         info = UT_LIST_GET_NEXT(list, info);
00941       }
00942     }
00943 #ifndef INNODB_RW_LOCKS_USE_ATOMICS
00944     mutex_exit(&(lock->mutex));
00945 #endif
00946 
00947     lock = UT_LIST_GET_NEXT(list, lock);
00948   }
00949 
00950   fprintf(file, "Total number of rw-locks %ld\n", count);
00951   mutex_exit(&rw_lock_list_mutex);
00952 }
00953 
00954 /***************************************************************/
00956 UNIV_INTERN
00957 void
00958 rw_lock_print(
00959 /*==========*/
00960   rw_lock_t*  lock) 
00961 {
00962   rw_lock_debug_t* info;
00963 
00964   fprintf(stderr,
00965     "-------------\n"
00966     "RW-LATCH INFO\n"
00967     "RW-LATCH: %p ", (void*) lock);
00968 
00969 #ifndef INNODB_RW_LOCKS_USE_ATOMICS
00970   /* We used to acquire lock->mutex here, but it would cause a
00971   recursive call to sync_thread_add_level() if UNIV_SYNC_DEBUG
00972   is defined.  Since this function is only invoked from
00973   sync_thread_levels_g(), let us choose the smaller evil:
00974   performing dirty reads instead of causing bogus deadlocks or
00975   assertion failures. */
00976 #endif
00977   if (lock->lock_word != X_LOCK_DECR) {
00978 
00979     if (rw_lock_get_waiters(lock)) {
00980       fputs(" Waiters for the lock exist\n", stderr);
00981     } else {
00982       putc('\n', stderr);
00983     }
00984 
00985     info = UT_LIST_GET_FIRST(lock->debug_list);
00986     while (info != NULL) {
00987       rw_lock_debug_print(info);
00988       info = UT_LIST_GET_NEXT(list, info);
00989     }
00990   }
00991 }
00992 
00993 /*********************************************************************/
00995 UNIV_INTERN
00996 void
00997 rw_lock_debug_print(
00998 /*================*/
00999   rw_lock_debug_t*  info) 
01000 {
01001   ulint rwt;
01002 
01003   rwt   = info->lock_type;
01004 
01005   fprintf(stderr, "Locked: thread %lu file %s line %lu  ",
01006     (ulong) os_thread_pf(info->thread_id), info->file_name,
01007     (ulong) info->line);
01008   if (rwt == RW_LOCK_SHARED) {
01009     fputs("S-LOCK", stderr);
01010   } else if (rwt == RW_LOCK_EX) {
01011     fputs("X-LOCK", stderr);
01012   } else if (rwt == RW_LOCK_WAIT_EX) {
01013     fputs("WAIT X-LOCK", stderr);
01014   } else {
01015     ut_error;
01016   }
01017   if (info->pass != 0) {
01018     fprintf(stderr, " pass value %lu", (ulong) info->pass);
01019   }
01020   putc('\n', stderr);
01021 }
01022 
01023 /***************************************************************/
01027 UNIV_INTERN
01028 ulint
01029 rw_lock_n_locked(void)
01030 /*==================*/
01031 {
01032   rw_lock_t*  lock;
01033   ulint   count   = 0;
01034 
01035   mutex_enter(&rw_lock_list_mutex);
01036 
01037   lock = UT_LIST_GET_FIRST(rw_lock_list);
01038 
01039   while (lock != NULL) {
01040 
01041     if (lock->lock_word != X_LOCK_DECR) {
01042       count++;
01043     }
01044 
01045     lock = UT_LIST_GET_NEXT(list, lock);
01046   }
01047 
01048   mutex_exit(&rw_lock_list_mutex);
01049 
01050   return(count);
01051 }
01052 #endif /* UNIV_SYNC_DEBUG */