00001 /* Part of the culibs project, <http://www.eideticdew.org/culibs/>. 00002 * Copyright (C) 2005--2009 Petter Urkedal <urkedal@nbi.dk> 00003 * 00004 * This program is free software: you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation, either version 3 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00016 */ 00017 00018 #ifndef CU_RAREX_H 00019 #define CU_RAREX_H 00020 00021 #include <cu/fwd.h> 00022 #include <atomic_ops.h> 00023 #include <cu/tstate.h> 00024 00025 CU_BEGIN_DECLARATIONS 00026 /** \defgroup cu_rarex_h cu/rarex.h: Read-Write Locks Optimised for Rarely Excluding Cases 00027 ** @{ \ingroup cu_util_mod 00028 ** 00029 ** Rarices are read-write locks implemented with a single word per lock (in 00030 ** addition to some per-thread data). They are cheap as long as they are 00031 ** read-locked, or write access do not conflict with another read or write, 00032 ** but significantly more expensive than mutices when they clog. In other 00033 ** words, use this only when two threads hardly ever compete for the same 00034 ** lock. 00035 ** 00036 ** If each of <i>N</i> threads spends <i>p</i> of its time accessing the lock 00037 ** (exclusive or not), then \f$q = (1 - p)^{N - 1}\f$ is the probability that 00038 ** a thread can acquire a write lock without delay. Note the locking time is 00039 ** included in \f$p\f$. For good efficiency keep \f$p \ll \frac1N\f$. 00040 ** 00041 ** If unsure, use \c pthread_mutex_t or similar instead. 00042 **/ 00043 00044 /** \ingroup cuP_mod */ 00045 #define cuP_RAREX_WRITE_MULTIPLIER 0x10000 00046 00047 /** Construct the \a rarex lock. */ 00048 CU_SINLINE void 00049 cu_rarex_init(cu_rarex_t *rarex) { AO_store(rarex, 0); } 00050 00051 void cuP_rarex_lock_read(cu_rarex_t *); /*!<\private*/ 00052 void cuP_rarex_lock_write(cu_rarex_t *); /*!<\private*/ 00053 void cuP_rarex_wake_up(cu_rarex_t *); /*!<\private*/ 00054 00055 /** Lock for non-exclusive access. May be called recursively, and should have 00056 ** matching calls to cu_rarex_unlock_read. */ 00057 CU_SINLINE void 00058 cu_rarex_lock_read(cu_rarex_t *rarex) 00059 { 00060 if (AO_fetch_and_add1_acquire_read(rarex) >= cuP_RAREX_WRITE_MULTIPLIER) 00061 cuP_rarex_lock_read(rarex); 00062 } 00063 00064 /** Unlock non-exclusive access. */ 00065 CU_SINLINE void 00066 cu_rarex_unlock_read(cu_rarex_t *rarex) 00067 { 00068 unsigned int snap = AO_fetch_and_sub1_release(rarex); 00069 if (snap > cuP_RAREX_WRITE_MULTIPLIER) 00070 cuP_rarex_wake_up(rarex); 00071 } 00072 00073 /** Try to lock for non-exclusive access without blocking. Returns true iff 00074 ** successful. */ 00075 CU_SINLINE cu_bool_t 00076 cu_rarex_trylock_read(cu_rarex_t *rarex) 00077 { 00078 if (AO_fetch_and_add1_acquire_read(rarex) 00079 >= cuP_RAREX_WRITE_MULTIPLIER) { 00080 cu_rarex_unlock_read(rarex); 00081 return cu_false; 00082 } 00083 else 00084 return cu_true; 00085 } 00086 00087 /** Lock for exclusive access. Caller must not own any access to any 00088 ** cu_rarex_t locks advance. */ 00089 CU_SINLINE void 00090 cu_rarex_lock_write(cu_rarex_t *rarex) 00091 { 00092 if (AO_fetch_and_add_acquire(rarex, cuP_RAREX_WRITE_MULTIPLIER) > 0) 00093 cuP_rarex_lock_write(rarex); 00094 } 00095 00096 /** Try to lock for exclusive access without blocking. Returns true iff 00097 ** successful. */ 00098 CU_SINLINE cu_bool_t 00099 cu_rarex_trylock_write(cu_rarex_t *rarex) 00100 { 00101 return AO_compare_and_swap_acquire(rarex, 0, cuP_RAREX_WRITE_MULTIPLIER); 00102 } 00103 00104 /** Release exclusive access. */ 00105 CU_SINLINE void 00106 cu_rarex_unlock_write(cu_rarex_t *rarex) 00107 { 00108 unsigned int snap; 00109 snap = AO_fetch_and_add_release_write(rarex, 00110 -cuP_RAREX_WRITE_MULTIPLIER); 00111 if (snap > cuP_RAREX_WRITE_MULTIPLIER) 00112 cuP_rarex_wake_up(rarex); 00113 } 00114 00115 /** Attempts to promote a read lock to a write lock. Returns non-zero on 00116 ** success, otherwise the lock remains for read-access. Note that depending 00117 ** on the outcome, caller must either use \ref cu_rarex_unlock_read or \ref 00118 ** cu_rarex_unlock_write to unlock. */ 00119 CU_SINLINE cu_bool_t 00120 cu_rarex_try_promote(cu_rarex_t *rarex) 00121 { 00122 return AO_compare_and_swap_full(rarex, 1, cuP_RAREX_WRITE_MULTIPLIER); 00123 } 00124 00125 /*!\deprecated Use cu_rarex_init. */ 00126 #define cu_rarex_cct cu_rarex_init 00127 00128 /** @} */ 00129 CU_END_DECLARATIONS 00130 00131 #endif