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