1 /* 2 ** Copyright (C) 2006-2020 by Carnegie Mellon University. 3 ** 4 ** @OPENSOURCE_LICENSE_START@ 5 ** See license information in ../../LICENSE.txt 6 ** @OPENSOURCE_LICENSE_END@ 7 */ 8 9 /* 10 ** skthread.h 11 ** 12 ** Common thread routines. 13 ** 14 */ 15 #ifndef _SKTHREAD_H 16 #define _SKTHREAD_H 17 #ifdef __cplusplus 18 extern "C" { 19 #endif 20 21 #include <silk/silk.h> 22 23 RCSIDENTVAR(rcsID_SKTHREAD_H, "$SiLK: skthread.h ef14e54179be 2020-04-14 21:57:45Z mthomas $"); 24 25 #include <silk/sklog.h> 26 27 28 #define SKTHREAD_UNKNOWN_ID UINT32_MAX 29 30 31 /** 32 * Intitialize the skthread module. This function is expected be 33 * called by the program's primary thread, and the function must be 34 * called before calling skthread_create() or 35 * skthread_create_detached(). 36 * 37 * Set the name of the current thread to `name', which must be a 38 * string that is valid for the lifetime of the thread. Set the ID 39 * of the current thread to 0. 40 * 41 * This function is a no-op if it has been called previously and 42 * succeeded. 43 * 44 * Return 0 on success, -1 on failure. 45 */ 46 int 47 skthread_init( 48 const char *name); 49 50 /** 51 * Teardown function for the skthread module. This function is 52 * expected be called by the program's primary thread, and the 53 * function should be called once all other threads have exited. 54 */ 55 void 56 skthread_teardown( 57 void); 58 59 /** 60 * Spawn a simple thread and invoke the function 'fn' with the 61 * argument 'arg'. Call skthread_ignore_signals() within the 62 * context of the new thread. 63 * 64 * Set the thread's name to 'name', which must be a string that is 65 * valid for the lifetime of the thread. Set the thread's ID to 66 * the next unused integer value. 67 * 68 * Return 0 on success, errno on failure. 69 */ 70 int 71 skthread_create( 72 const char *name, 73 pthread_t *thread, 74 void *(*fn)(void *), 75 void *arg); 76 77 /** 78 * Similar to skthread_create(), except the thread is created with 79 * the detached attribute set. 80 */ 81 int 82 skthread_create_detached( 83 const char *name, 84 pthread_t *thread, 85 void *(*fn)(void *), 86 void *arg); 87 88 /** 89 * Return the name of the calling thread that was specified with 90 * skthread_init(), skthread_create(), or 91 * skthread_create_detached(). 92 * 93 * Return the string "unknown" if a name was not set for the 94 * calling thread. 95 */ 96 const char * 97 skthread_name( 98 void); 99 100 /** 101 * Return the id of the calling thread. 102 * 103 * Return the value SKTHREAD_UNKNOWN_ID if an id was not set for 104 * the calling thread. 105 */ 106 uint32_t 107 skthread_id( 108 void); 109 110 /** 111 * Tell the current thread to ignore all signals except those 112 * indicating a failure (SIGABRT, SIGBUS, SIGSEGV, ...). 113 */ 114 void 115 skthread_ignore_signals( 116 void); 117 118 119 120 /* 121 * Thread debug logging. 122 * 123 * Wrappers around DEBUGMSG() that prepend the message with the 124 * current file name, line number, thread name, and thread id. 125 */ 126 #define SKTHREAD_DEBUG_PREFIX(m_fmt) \ 127 "%s:%d <%s:%" PRIu32 "> " m_fmt, \ 128 __FILE__, __LINE__, skthread_name(), skthread_id() 129 130 #define SKTHREAD_DEBUG_PRINT1(m_fmt) \ 131 DEBUGMSG(SKTHREAD_DEBUG_PREFIX(m_fmt)) 132 #define SKTHREAD_DEBUG_PRINT2(m_fmt, m_a2) \ 133 DEBUGMSG(SKTHREAD_DEBUG_PREFIX(m_fmt), (m_a2)) 134 #define SKTHREAD_DEBUG_PRINT3(m_fmt, m_a2, m_a3) \ 135 DEBUGMSG(SKTHREAD_DEBUG_PREFIX(m_fmt), (m_a2), (m_a3)) 136 #define SKTHREAD_DEBUG_PRINT4(m_fmt, m_a2, m_a3, m_a4) \ 137 DEBUGMSG(SKTHREAD_DEBUG_PREFIX(m_fmt), (m_a2), (m_a3), (m_a4)) 138 139 140 /* 141 * Use the above wrappers over DEBUGMSG() when SKTHREAD_DEBUG_MUTEX 142 * is defined and has a value greater than 0. 143 */ 144 #if defined(SKTHREAD_DEBUG_MUTEX) && SKTHREAD_DEBUG_MUTEX > 0 145 #define SKT_D2(x, y) SKTHREAD_DEBUG_PRINT2(x, y) 146 #define SKT_D3(x, y, z) SKTHREAD_DEBUG_PRINT3(x, y, z) 147 #define SKT_D4(x, y, z, zz) SKTHREAD_DEBUG_PRINT4(x, y, z, zz) 148 #else 149 #define SKT_D2(x, y) 150 #define SKT_D3(x, y, z) 151 #define SKT_D4(x, y, z, zz) 152 #endif 153 154 155 /* 156 * Wrappers around pthread mutex / condition functions 157 */ 158 159 /* Wrapper around pthread_mutex_init() */ 160 #define MUTEX_INIT(m_mutex) pthread_mutex_init((m_mutex), NULL) 161 162 /* Wrapper around pthread_mutex_destroy() */ 163 #define MUTEX_DESTROY(m_mutex) pthread_mutex_destroy((m_mutex)) 164 165 /* Wrapper around pthread_cond_init() */ 166 #define MUTEX_COND_INIT(m_cond) pthread_cond_init((m_cond), NULL) 167 168 /* Wrapper around pthread_cond_destroy() */ 169 #define MUTEX_COND_DESTROY(m_cond) pthread_cond_destroy((m_cond)) 170 171 /* Wrapper around pthread_mutex_lock() */ 172 #define MUTEX_LOCK(m_mutex) \ 173 do { \ 174 SKT_D2("MUTEX LOCKING %p", (void *)(m_mutex)); \ 175 pthread_mutex_lock(m_mutex); \ 176 SKT_D2("MUTEX IN LOCK %p", (void *)(m_mutex)); \ 177 } while (0) 178 179 /* Wrapper around pthread_mutex_unlock() */ 180 #define MUTEX_UNLOCK(m_mutex) \ 181 do { \ 182 SKT_D2("MUTEX UNLOCKING %p", (void *)(m_mutex)); \ 183 pthread_mutex_unlock(m_mutex); \ 184 } while (0) 185 186 /* Wrapper around pthread_cond_wait() */ 187 #define MUTEX_WAIT(m_cond, m_mutex) \ 188 do { \ 189 SKT_D3("MUTEX WAIT %p (Unlocked %p)", \ 190 (void *)(m_cond), (void *)(m_mutex)); \ 191 pthread_cond_wait((m_cond), (m_mutex)); \ 192 SKT_D3("MUTEX RESUME %p (Locked %p)", \ 193 (void *)(m_cond), (void *)(m_mutex)); \ 194 } while (0) 195 196 /* Wrapper around pthread_cond_timedwait(); result of call is stored 197 * in memory location referenced by 'm_retval'. */ 198 #define MUTEX_TIMEDWAIT(m_cond, m_mutex, m_timespec, m_retval) \ 199 do { \ 200 SKT_D3("MUTEX WAIT %p (Unlocked %p)", \ 201 (void *)(m_cond), (void *)(m_mutex)); \ 202 *(m_retval) = (pthread_cond_timedwait( \ 203 (m_cond), (m_mutex), (m_timespec))); \ 204 SKT_D4("MUTEX RESUME %p (Locked %p) (%s)", \ 205 (void *)(m_cond), (void *)(m_mutex), \ 206 (0 == *(m_retval)) ? "Signaled" : "Timed-out"); \ 207 } while (0) 208 209 /* Wrapper around pthread_cond_signal() */ 210 #define MUTEX_SIGNAL(m_cond) \ 211 do { \ 212 SKT_D2("SIGNALING %p", (void *)(m_cond)); \ 213 pthread_cond_signal(m_cond); \ 214 } while (0) 215 216 /* Wrapper around pthread_cond_broadcast() */ 217 #define MUTEX_BROADCAST(m_cond) \ 218 do { \ 219 SKT_D2("BROADCASTING %p", (void *)(m_cond)); \ 220 pthread_cond_broadcast(m_cond); \ 221 } while (0) 222 223 #if defined(SKTHREAD_DEBUG_MUTEX) && SKTHREAD_DEBUG_MUTEX > 0 224 #define ASSERT_MUTEX_LOCKED(m_mutex) \ 225 if (pthread_mutex_trylock((m_mutex)) == EBUSY) { } else { \ 226 SKT_D2("Unexpectedly found %p to be unlocked", \ 227 (void *)(m_mutex)); \ 228 skAbort(); \ 229 } 230 #else 231 #define ASSERT_MUTEX_LOCKED(m_mutex) \ 232 assert(pthread_mutex_trylock((m_mutex)) == EBUSY) 233 #endif 234 235 236 237 /* 238 * Read / Write Lock Macros 239 */ 240 241 #ifdef SK_HAVE_PTHREAD_RWLOCK 242 # define RWMUTEX pthread_rwlock_t 243 244 extern int skthread_too_many_readlocks; 245 246 # define RW_MUTEX_INIT(m_mutex) pthread_rwlock_init((m_mutex), NULL) 247 248 # define RW_MUTEX_DESTROY(m_mutex) \ 249 do { \ 250 SKT_D2("RW MUTEX DESTROY %p", (void *)(m_mutex)); \ 251 pthread_rwlock_destroy((m_mutex)); \ 252 } while (0) 253 254 # define READ_LOCK(m_mutex) \ 255 do { \ 256 SKT_D2("READ MUTEX LOCKING %p", (void *)(m_mutex)); \ 257 while (pthread_rwlock_rdlock(m_mutex) == EAGAIN) { \ 258 if (!skthread_too_many_readlocks) { \ 259 skthread_too_many_readlocks = 1; \ 260 WARNINGMSG(("WARNING: Too many read locks; " \ 261 "spinlocking enabled to compensate")); \ 262 } \ 263 } \ 264 SKT_D2("READ MUTEX IN LOCK %p", (void *)(m_mutex)); \ 265 } while (0) 266 267 # define WRITE_LOCK(m_mutex) \ 268 do { \ 269 SKT_D2("WRITE MUTEX LOCKING %p", (void *)(m_mutex)); \ 270 pthread_rwlock_wrlock(m_mutex); \ 271 SKT_D2("WRITE MUTEX IN LOCK %p", (void *)(m_mutex)); \ 272 } while (0) 273 274 # define RW_MUTEX_UNLOCK(m_mutex) \ 275 do { \ 276 SKT_D2("RW MUTEX UNLOCKING %p", (void *)(m_mutex)); \ 277 pthread_rwlock_unlock(m_mutex); \ 278 } while (0) 279 280 #if defined(SKTHREAD_DEBUG_MUTEX) && SKTHREAD_DEBUG_MUTEX > 0 281 282 # define ASSERT_RW_MUTEX_LOCKED(m_mutex) \ 283 do { \ 284 int wrlock_ret = pthread_rwlock_trywrlock(m_mutex); \ 285 if (EBUSY == wrlock_ret || EDEADLK == wrlock_ret) { } else { \ 286 SKT_D2("Unexpectedly found %p to be unlocked", \ 287 (void *)(m_mutex)); \ 288 skAbort(); \ 289 } \ 290 } while (0) 291 292 # define ASSERT_RW_MUTEX_WRITE_LOCKED(m_mutex) \ 293 do { \ 294 int rdlock_ret = pthread_rwlock_tryrdlock(m_mutex); \ 295 if (EBUSY == rdlock_ret || EDEADLK == rdlock_ret) { } else { \ 296 SKT_D2("Expected %p to be write locked but got a read lock", \ 297 (void *)(m_mutex)); \ 298 skAbort(); \ 299 } \ 300 } while (0) 301 302 #elif !defined(NDEBUG) 303 304 # define ASSERT_RW_MUTEX_LOCKED(m_mutex) \ 305 do { \ 306 int wrlock_ret = pthread_rwlock_trywrlock(m_mutex); \ 307 assert(EBUSY == wrlock_ret || EDEADLK == wrlock_ret); \ 308 } while (0) 309 310 # define ASSERT_RW_MUTEX_WRITE_LOCKED(m_mutex) \ 311 do { \ 312 int rdlock_ret = pthread_rwlock_tryrdlock(m_mutex); \ 313 assert(EBUSY == rdlock_ret || EDEADLK == rdlock_ret); \ 314 } while (0) 315 316 #else 317 /* no-ops */ 318 # define ASSERT_RW_MUTEX_LOCKED(m_mutex) 319 # define ASSERT_RW_MUTEX_WRITE_LOCKED(m_mutex) 320 #endif /* NDEBUG */ 321 322 #else /* #ifdef SK_HAVE_PTHREAD_RWLOCK */ 323 324 /* 325 * No support for read/write locks; use ordinary locks 326 */ 327 328 # define RWMUTEX pthread_mutex_t 329 # define RW_MUTEX_INIT MUTEX_INIT 330 # define RW_MUTEX_DESTROY MUTEX_DESTROY 331 # define READ_LOCK MUTEX_LOCK 332 # define WRITE_LOCK MUTEX_LOCK 333 # define RW_MUTEX_UNLOCK MUTEX_UNLOCK 334 # define ASSERT_RW_MUTEX_LOCKED(m_mutex) ASSERT_MUTEX_LOCKED(m_mutex) 335 # define ASSERT_RW_MUTEX_WRITE_LOCKED(m_mutex) ASSERT_MUTEX_LOCKED(m_mutex) 336 337 #endif /* #else of #ifdef SK_HAVE_PTHREAD_RWLOCK */ 338 339 340 #ifdef __cplusplus 341 } 342 #endif 343 #endif /* _SKTHREAD_H */ 344 345 /* 346 ** Local Variables: 347 ** mode:c 348 ** indent-tabs-mode:nil 349 ** c-basic-offset:4 350 ** End: 351 */ 352