• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

I_Lock.h

Go to the documentation of this file.
00001 /** @file
00002 
00003   Basic locks for threads
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 #ifndef _I_Lock_h_
00025 #define _I_Lock_h_
00026 
00027 #include "libts.h"
00028 #include "I_Thread.h"
00029 
00030 #define MAX_LOCK_TIME   HRTIME_MSECONDS(200)
00031 #define THREAD_MUTEX_THREAD_HOLDING     (-1024*1024)
00032 
00033 class EThread;
00034 typedef EThread *EThreadPtr;
00035 typedef volatile EThreadPtr VolatileEThreadPtr;
00036 
00037 inkcoreapi extern void lock_waiting(const char *file, int line, const char *handler);
00038 inkcoreapi extern void lock_holding(const char *file, int line, const char *handler);
00039 extern void lock_taken(const char *file, int line, const char *handler);
00040 
00041 /**
00042   Lock object used in continuations and threads.
00043 
00044   The ProxyMutex class is the main synchronization object used
00045   throughout the Event System. It is a reference counted object
00046   that provides mutually exclusive access to a resource. Since the
00047   Event System is multithreaded by design, the ProxyMutex is required
00048   to protect data structures and state information that could
00049   otherwise be affected by the action of concurrent threads.
00050 
00051   A ProxyMutex object has an ink_mutex member (defined in ink_mutex.h)
00052   which is a wrapper around the platform dependent mutex type. This
00053   member allows the ProxyMutex to provide the functionallity required
00054   by the users of the class without the burden of platform specific
00055   function calls.
00056 
00057   The ProxyMutex also has a reference to the current EThread holding
00058   the lock as a back pointer for verifying that it is released
00059   correctly.
00060 
00061   Acquiring/Releasing locks:
00062 
00063   Included with the ProxyMutex class, there are several macros that
00064   allow you to lock/unlock the underlying mutex object.
00065 
00066 */
00067 class ProxyMutex: public RefCountObj
00068 {
00069 public:
00070   /**
00071     Underlying mutex object.
00072 
00073     The platform independent mutex for the ProxyMutex class. You
00074     must not modify or set it directly.
00075 
00076   */
00077   // coverity[uninit_member]
00078   ink_mutex the_mutex;
00079 
00080   /**
00081     Backpointer to owning thread.
00082 
00083     This is a pointer to the thread currently holding the mutex
00084     lock.  You must not modify or set this value directly.
00085 
00086   */
00087   volatile EThreadPtr thread_holding;
00088   
00089   int nthread_holding;
00090 
00091 #ifdef DEBUG
00092   ink_hrtime hold_time;
00093   const char *file;
00094   int line;
00095   const char *handler;
00096 
00097 #  ifdef MAX_LOCK_TAKEN
00098   int taken;
00099 #  endif                        //MAX_LOCK_TAKEN
00100 
00101 #  ifdef LOCK_CONTENTION_PROFILING
00102   int total_acquires, blocking_acquires,
00103     nonblocking_acquires, successful_nonblocking_acquires, unsuccessful_nonblocking_acquires;
00104   void print_lock_stats(int flag);
00105 #  endif                        //LOCK_CONTENTION_PROFILING
00106 #endif                          //DEBUG
00107   void free();
00108 
00109   /**
00110     Constructor - use new_ProxyMutex() instead.
00111 
00112     The constructor of a ProxyMutex object. Initializes the state
00113     of the object but leaves the initialization of the mutex member
00114     until it is needed (through init()). Do not use this constructor,
00115     the preferred mechanism for creating a ProxyMutex is via the
00116     new_ProxyMutex function, which provides a faster allocation.
00117 
00118   */
00119   ProxyMutex()
00120   {
00121     thread_holding = NULL;
00122     nthread_holding = 0;
00123 #ifdef DEBUG
00124     hold_time = 0;
00125     file = NULL;
00126     line = 0;
00127     handler = NULL;
00128 #  ifdef MAX_LOCK_TAKEN
00129     taken = 0;
00130 #  endif                        //MAX_LOCK_TAKEN
00131 #  ifdef LOCK_CONTENTION_PROFILING
00132     total_acquires = 0;
00133     blocking_acquires = 0;
00134     nonblocking_acquires = 0;
00135     successful_nonblocking_acquires = 0;
00136     unsuccessful_nonblocking_acquires = 0;
00137 #  endif                        //LOCK_CONTENTION_PROFILING
00138 #endif                          //DEBUG
00139     // coverity[uninit_member]
00140   }
00141 
00142   /**
00143     Initializes the underlying mutex object.
00144 
00145     After constructing your ProxyMutex object, use this function
00146     to initialize the underlying mutex object with an optional name.
00147 
00148     @param name Name to identify this ProxyMutex. Its use depends
00149       on the given platform.
00150 
00151   */
00152   void init(const char *name = "UnnamedMutex") {
00153     ink_mutex_init(&the_mutex, name);
00154   }
00155 };
00156 
00157 // The ClassAlocator for ProxyMutexes
00158 extern inkcoreapi ClassAllocator<ProxyMutex> mutexAllocator;
00159 
00160 inline bool
00161 Mutex_trylock(
00162 #ifdef DEBUG
00163                const char *afile, int aline, const char *ahandler,
00164 #endif
00165                ProxyMutex * m, EThread * t)
00166 {
00167 
00168   ink_assert(t != 0);
00169   ink_assert(t == (EThread*)this_thread());
00170   if (m->thread_holding != t) {
00171     if (!ink_mutex_try_acquire(&m->the_mutex)) {
00172 #ifdef DEBUG
00173       lock_waiting(m->file, m->line, m->handler);
00174 #ifdef LOCK_CONTENTION_PROFILING
00175       m->unsuccessful_nonblocking_acquires++;
00176       m->nonblocking_acquires++;
00177       m->total_acquires++;
00178       m->print_lock_stats(0);
00179 #endif //LOCK_CONTENTION_PROFILING
00180 #endif //DEBUG
00181       return false;
00182     }
00183     ink_assert(m->thread_holding = t);
00184 #ifdef DEBUG
00185     m->file = afile;
00186     m->line = aline;
00187     m->handler = ahandler;
00188     m->hold_time = ink_get_hrtime();
00189 #ifdef MAX_LOCK_TAKEN
00190     m->taken++;
00191 #endif //MAX_LOCK_TAKEN
00192 #endif //DEBUG
00193   }
00194 #ifdef DEBUG
00195 #ifdef LOCK_CONTENTION_PROFILING
00196   m->successful_nonblocking_acquires++;
00197   m->nonblocking_acquires++;
00198   m->total_acquires++;
00199   m->print_lock_stats(0);
00200 #endif //LOCK_CONTENTION_PROFILING
00201 #endif //DEBUG
00202   m->nthread_holding++;
00203   return true;
00204 }
00205 
00206 inline bool
00207 Mutex_trylock_spin(
00208 #ifdef DEBUG
00209                     const char *afile, int aline, const char *ahandler,
00210 #endif
00211                     ProxyMutex * m, EThread * t, int spincnt = 1)
00212 {
00213 
00214   ink_assert(t != 0);
00215   if (m->thread_holding != t) {
00216     int locked;
00217     do {
00218       if ((locked = ink_mutex_try_acquire(&m->the_mutex)))
00219         break;
00220     } while (--spincnt);
00221     if (!locked) {
00222 #ifdef DEBUG
00223       lock_waiting(m->file, m->line, m->handler);
00224 #ifdef LOCK_CONTENTION_PROFILING
00225       m->unsuccessful_nonblocking_acquires++;
00226       m->nonblocking_acquires++;
00227       m->total_acquires++;
00228       m->print_lock_stats(0);
00229 #endif //LOCK_CONTENTION_PROFILING
00230 #endif //DEBUG
00231       return false;
00232     }
00233     m->thread_holding = t;
00234     ink_assert(m->thread_holding);
00235 #ifdef DEBUG
00236     m->file = afile;
00237     m->line = aline;
00238     m->handler = ahandler;
00239     m->hold_time = ink_get_hrtime();
00240 #ifdef MAX_LOCK_TAKEN
00241     m->taken++;
00242 #endif //MAX_LOCK_TAKEN
00243 #endif //DEBUG
00244   }
00245 #ifdef DEBUG
00246 #ifdef LOCK_CONTENTION_PROFILING
00247   m->successful_nonblocking_acquires++;
00248   m->nonblocking_acquires++;
00249   m->total_acquires++;
00250   m->print_lock_stats(0);
00251 #endif //LOCK_CONTENTION_PROFILING
00252 #endif //DEBUG
00253   m->nthread_holding++;
00254   return true;
00255 }
00256 
00257 inline int
00258 Mutex_lock(
00259 #ifdef DEBUG
00260             const char *afile, int aline, const char *ahandler,
00261 #endif
00262             ProxyMutex * m, EThread * t)
00263 {
00264 
00265   ink_assert(t != 0);
00266   if (m->thread_holding != t) {
00267     ink_mutex_acquire(&m->the_mutex);
00268     m->thread_holding = t;
00269     ink_assert(m->thread_holding);
00270 #ifdef DEBUG
00271     m->file = afile;
00272     m->line = aline;
00273     m->handler = ahandler;
00274     m->hold_time = ink_get_hrtime();
00275 #ifdef MAX_LOCK_TAKEN
00276     m->taken++;
00277 #endif //MAX_LOCK_TAKEN
00278 #endif //DEBUG
00279   }
00280 #ifdef DEBUG
00281 #ifdef LOCK_CONTENTION_PROFILING
00282   m->blocking_acquires++;
00283   m->total_acquires++;
00284   m->print_lock_stats(0);
00285 #endif // LOCK_CONTENTION_PROFILING
00286 #endif //DEBUG
00287   m->nthread_holding++;
00288   return true;
00289 }
00290 
00291 inline void
00292 Mutex_unlock(ProxyMutex * m, EThread * t)
00293 {
00294   if (m->nthread_holding) {
00295     ink_assert(t == m->thread_holding);
00296     m->nthread_holding--;
00297     if (!m->nthread_holding) {
00298 #ifdef DEBUG
00299       if (ink_get_hrtime() - m->hold_time > MAX_LOCK_TIME)
00300         lock_holding(m->file, m->line, m->handler);
00301 #ifdef MAX_LOCK_TAKEN
00302       if (m->taken > MAX_LOCK_TAKEN)
00303         lock_taken(m->file, m->line, m->handler);
00304 #endif //MAX_LOCK_TAKEN
00305       m->file = NULL;
00306       m->line = 0;
00307       m->handler = NULL;
00308 #endif //DEBUG
00309       ink_assert(m->thread_holding);
00310       m->thread_holding = 0;
00311       ink_mutex_release(&m->the_mutex);
00312     }
00313   }
00314 }
00315 
00316 struct MutexLock
00317 {
00318   Ptr<ProxyMutex> m;
00319 
00320   MutexLock():m(NULL)
00321   {
00322   };
00323 
00324   MutexLock(
00325 #ifdef DEBUG
00326              const char *afile, int aline, const char *ahandler,
00327 #endif                          //DEBUG
00328              ProxyMutex * am, EThread * t):m(am)
00329   {
00330 
00331     Mutex_lock(
00332 #ifdef DEBUG
00333                 afile, aline, ahandler,
00334 #endif //DEBUG
00335                 m, t);
00336   }
00337 
00338   void set_and_take(
00339 #ifdef DEBUG
00340                      const char *afile, int aline, const char *ahandler,
00341 #endif                          //DEBUG
00342                      ProxyMutex * am, EThread * t)
00343   {
00344 
00345     m = am;
00346     Mutex_lock(
00347 #ifdef DEBUG
00348                 afile, aline, ahandler,
00349 #endif //DEBUG
00350                 m, t);
00351   }
00352 
00353   void release()
00354   {
00355     if (m)
00356       Mutex_unlock(m, m->thread_holding);
00357     m.clear();
00358   }
00359 
00360   ~MutexLock()
00361   {
00362     if (m)
00363       Mutex_unlock(m, m->thread_holding);
00364   }
00365 
00366   int operator!() const { return false; }
00367   operator bool() { return true; }
00368 };
00369 
00370 struct MutexTryLock
00371 {
00372   Ptr<ProxyMutex> m;
00373   volatile bool lock_acquired;
00374 
00375   MutexTryLock(
00376 #ifdef DEBUG
00377                   const char *afile, int aline, const char *ahandler,
00378 #endif                          //DEBUG
00379                   ProxyMutex * am, EThread * t)
00380   {
00381       lock_acquired = Mutex_trylock(
00382 #ifdef DEBUG
00383                                      afile, aline, ahandler,
00384 #endif //DEBUG
00385                                      am, t);
00386       if (lock_acquired)
00387         m = am;
00388   }
00389 
00390   MutexTryLock(
00391 #ifdef DEBUG
00392                 const char *afile, int aline, const char *ahandler,
00393 #endif                          //DEBUG
00394                 ProxyMutex * am, EThread * t, int sp)
00395   {
00396       lock_acquired = Mutex_trylock_spin(
00397 #ifdef DEBUG
00398                                           afile, aline, ahandler,
00399 #endif //DEBUG
00400                                           am, t, sp);
00401       if (lock_acquired)
00402         m = am;
00403   }
00404 
00405   ~MutexTryLock()
00406   {
00407     if (m.m_ptr)
00408       Mutex_unlock(m.m_ptr, m.m_ptr->thread_holding);
00409   }
00410 
00411   void release()
00412   {
00413     if (m.m_ptr) {
00414       Mutex_unlock(m.m_ptr, m.m_ptr->thread_holding);
00415       m.clear();
00416     }
00417     lock_acquired = false;
00418   }
00419 
00420   int operator!() const { return !lock_acquired; }
00421   operator bool() const { return lock_acquired; }
00422 };
00423 
00424 inline void
00425 ProxyMutex::free()
00426 {
00427 #ifdef DEBUG
00428 #ifdef LOCK_CONTENTION_PROFILING
00429   print_lock_stats(1);
00430 #endif
00431 #endif
00432   ink_mutex_destroy(&the_mutex);
00433   mutexAllocator.free(this);
00434 }
00435 
00436 // TODO should take optional mutex "name" identifier, to pass along to the init() fun
00437 /**
00438   Creates a new ProxyMutex object.
00439 
00440   This is the preferred mechanism for constructing objects of the
00441   ProxyMutex class. It provides you with faster allocation than
00442   that of the normal constructor.
00443 
00444   @return A pointer to a ProxyMutex object appropriate for the build
00445     environment.
00446 
00447 */
00448 inline ProxyMutex *
00449 new_ProxyMutex()
00450 {
00451   ProxyMutex *m = mutexAllocator.alloc();
00452   m->init();
00453   return m;
00454 }
00455 
00456 /*------------------------------------------------------*\
00457 |  Macros                                                |
00458 \*------------------------------------------------------*/
00459 
00460 /**
00461   Blocks until the lock to the ProxyMutex is acquired.
00462 
00463   This macro performs a blocking call until the lock to the ProxyMutex
00464   is acquired. This call allocates a special object that holds the
00465   lock to the ProxyMutex only for the scope of the function or
00466   region. It is a good practice to delimit such scope explicitly
00467   with '&#123;' and '&#125;'.
00468 
00469   @param _l Arbitrary name for the lock to use in this call
00470   @param _m A pointer to (or address of) a ProxyMutex object
00471   @param _t The current EThread executing your code.
00472 
00473 */
00474 #  ifdef DEBUG
00475 #    define MUTEX_LOCK(_l,_m,_t) MutexLock _l(__FILE__,__LINE__,NULL,_m,_t)
00476 #  else
00477 #    define MUTEX_LOCK(_l,_m,_t) MutexLock _l(_m,_t)
00478 #  endif //DEBUG
00479 
00480 #  ifdef DEBUG
00481 /**
00482   Attempts to acquire the lock to the ProxyMutex.
00483 
00484   This macro attempts to acquire the lock to the specified ProxyMutex
00485   object in a non-blocking manner. After using the macro you can
00486   see if it was successful by comparing the lock variable with true
00487   or false (the variable name passed in the _l parameter).
00488 
00489   @param _l Arbitrary name for the lock to use in this call (lock variable)
00490   @param _m A pointer to (or address of) a ProxyMutex object
00491   @param _t The current EThread executing your code.
00492 
00493 */
00494 #    define MUTEX_TRY_LOCK(_l,_m,_t) \
00495 MutexTryLock _l(__FILE__,__LINE__,(char*)NULL,_m,_t)
00496 
00497 /**
00498   Attempts to acquire the lock to the ProxyMutex.
00499 
00500   This macro performs up to the specified number of attempts to
00501   acquire the lock on the ProxyMutex object. It does so by running
00502   a busy loop (busy wait) '_sc' times. You should use it with care
00503   since it blocks the thread during that time and wastes CPU time.
00504 
00505   @param _l Arbitrary name for the lock to use in this call (lock variable)
00506   @param _m A pointer to (or address of) a ProxyMutex object
00507   @param _t The current EThread executing your code.
00508   @param _sc The number of attempts or spin count. It must be a positive value.
00509 
00510 */
00511 #    define MUTEX_TRY_LOCK_SPIN(_l,_m,_t,_sc) \
00512 MutexTryLock _l(__FILE__,__LINE__,(char*)NULL,_m,_t,_sc)
00513 
00514 
00515 /**
00516   Attempts to acquire the lock to the ProxyMutex.
00517 
00518   This macro attempts to acquire the lock to the specified ProxyMutex
00519   object in a non-blocking manner. After using the macro you can
00520   see if it was successful by comparing the lock variable with true
00521   or false (the variable name passed in the _l parameter).
00522 
00523   @param _l Arbitrary name for the lock to use in this call (lock variable)
00524   @param _m A pointer to (or address of) a ProxyMutex object
00525   @param _t The current EThread executing your code.
00526   @param _c Continuation whose mutex will be attempted to lock.
00527 
00528 */
00529 
00530 #    define MUTEX_TRY_LOCK_FOR(_l,_m,_t,_c) \
00531 MutexTryLock _l(__FILE__,__LINE__,NULL,_m,_t)
00532 #  else //DEBUG
00533 #    define MUTEX_TRY_LOCK(_l,_m,_t) MutexTryLock _l(_m,_t)
00534 #    define MUTEX_TRY_LOCK_SPIN(_l,_m,_t,_sc) MutexTryLock _l(_m,_t,_sc)
00535 #    define MUTEX_TRY_LOCK_FOR(_l,_m,_t,_c) MutexTryLock _l(_m,_t)
00536 #  endif //DEBUG
00537 
00538 /**
00539   Releases the lock on a ProxyMutex.
00540 
00541   This macro releases the lock on the ProxyMutex, provided it is
00542   currently held. The lock must have been successfully acquired
00543   with one of the MUTEX macros.
00544 
00545   @param _l Arbitrary name for the lock to use in this call (lock
00546     variable) It must be the same name as the one used to acquire the
00547     lock.
00548 
00549 */
00550 #  define MUTEX_RELEASE(_l) (_l).release()
00551 
00552 /////////////////////////////////////
00553 // DEPRECATED DEPRECATED DEPRECATED
00554 #ifdef DEBUG
00555 #  define MUTEX_TAKE_TRY_LOCK(_m,_t) \
00556 Mutex_trylock(__FILE__,__LINE__,(char*)NULL,_m,_t)
00557 #  define MUTEX_TAKE_TRY_LOCK_FOR(_m,_t,_c) \
00558 Mutex_trylock(__FILE__,__LINE__,(char*)NULL,_m,_t)
00559 #  define MUTEX_TAKE_TRY_LOCK_FOR_SPIN(_m,_t,_c,_sc) \
00560 Mutex_trylock_spin(__FILE__,__LINE__,NULL,_m,_t,_sc)
00561 #else
00562 #  define MUTEX_TAKE_TRY_LOCK(_m,_t) Mutex_trylock(_m,_t)
00563 #  define MUTEX_TAKE_TRY_LOCK_FOR(_m,_t,_c) Mutex_trylock(_m,_t)
00564 #  define MUTEX_TAKE_TRY_LOCK_FOR_SPIN(_m,_t,_c,_sc) \
00565 Mutex_trylock_spin(_m,_t,_sc)
00566 #endif
00567 
00568 #ifdef DEBUG
00569 #  define MUTEX_TAKE_LOCK(_m,_t)\
00570 Mutex_lock(__FILE__,__LINE__,(char*)NULL,_m,_t)
00571 #  define MUTEX_SET_AND_TAKE_LOCK(_s,_m,_t)\
00572 _s.set_and_take(__FILE__,__LINE__,(char*)NULL,_m,_t)
00573 #  define MUTEX_TAKE_LOCK_FOR(_m,_t,_c) \
00574 Mutex_lock(__FILE__,__LINE__,NULL,_m,_t)
00575 #else
00576 #  define MUTEX_TAKE_LOCK(_m,_t) Mutex_lock(_m,_t)
00577 #  define MUTEX_SET_AND_TAKE_LOCK(_s,_m,_t)_s.set_and_take(_m,_t)
00578 #  define MUTEX_TAKE_LOCK_FOR(_m,_t,_c) Mutex_lock(_m,_t)
00579 #endif //DEBUG
00580 
00581 #define MUTEX_UNTAKE_LOCK(_m,_t) Mutex_unlock(_m,_t)
00582 // DEPRECATED DEPRECATED DEPRECATED
00583 /////////////////////////////////////
00584 
00585 #endif // _Lock_h_

Generated by  doxygen 1.7.1