p99_threads.h 16.4 KB
Newer Older
(no author)'s avatar
(no author) committed
1 2
/* This may look like nonsense, but it really is -*- mode: C -*-              */
/*                                                                            */
3
/* Except for parts copied from previous work and as explicitly stated below, */
(no author)'s avatar
(no author) committed
4
/* the authors and copyright holders for this work are as follows:            */
(no author)'s avatar
(no author) committed
5
/* (C) copyright  2011-2014 Jens Gustedt, INRIA, France                       */
(no author)'s avatar
(no author) committed
6 7 8 9 10 11 12 13
/* (C) copyright  2012 William Morris                                         */
/*                                                                            */
/* This file is free software; it is part of the P99 project.                 */
/* You can redistribute it and/or modify it under the terms of the QPL as     */
/* given in the file LICENSE. It is distributed without any warranty;         */
/* without even the implied warranty of merchantability or fitness for a      */
/* particular purpose.                                                        */
/*                                                                            */
Jens Gustedt's avatar
Jens Gustedt committed
14 15 16
#ifndef P99_THREADS_H
#define P99_THREADS_H 1

17
#include "p99_try.h"
18
#include "p99_tss.h"
19

20 21 22 23
#if p99_has_feature(threads_h)
# include <threads.h>
#elif defined(_XOPEN_SOURCE) || defined(_POSIX_C_SOURCE)
# include "p99_threads_posix.h"
Jens Gustedt's avatar
Jens Gustedt committed
24
#else
25
# error "no suitable thread implementation found"
Jens Gustedt's avatar
Jens Gustedt committed
26 27
#endif

28 29 30 31 32 33 34
#ifndef ONCE_FLAG_INIT
typedef struct p99_once_flag once_flag;
#define ONCE_FLAG_INIT P99_ONCE_FLAG_INIT
#define call_once p99_call_once
#endif

typedef struct p99_once_flag p99_once_flag;
35 36 37 38 39 40 41

enum p00_once {
  p00_once_uninit = 0,
  p00_once_started,
  p00_once_finished,
};

42 43
/**
 ** @brief complete object type that holds a flag for use by
44
 ** ::p99_call_once
45
 **
46
 ** From the wording of the standard it is not clear whether a variable of
47
 ** this type @b must be initialized by means of ::P99_ONCE_FLAG_INIT. The
48 49 50
 ** corresponding POSIX structure requires the analog.
 **
 ** Therefore we don't use the POSIX structure, here, but cook this
51
 ** ourselves with atomic variables. In this way we can guarantee that a
52
 ** ::p99_once_flag that is initialized by the default initializer always
53
 ** has the correct state.
54
 */
55
struct p99_once_flag {
56
  union {
57 58 59 60 61 62
    enum p00_once p00_done;
    enum p00_once volatile p00_vdone;
  } p00_done;
  thrd_t p00_id;
  void (*const p00_init)(void);
  atomic_flag p00_flg;
63 64
};

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
P00_DOCUMENT_TYPE_ARGUMENT(P99_DECLARE_INIT_ONCE, 0)
P00_DOCUMENT_IDENTIFIER_ARGUMENT(P99_DECLARE_INIT_ONCE, 1)
P00_DOCUMENT_IDENTIFIER_ARGUMENT(P99_DECLARE_INIT_ONCE, 2)
#define P99_DECLARE_INIT_ONCE(T, NAME, ARG)                     \
/** @remark wrapper type around a T that is initialized once */ \
struct NAME {                                                   \
  p99_once_flag p00_once;                                       \
  T p00_val;                                                    \
};                                                              \
P99_DECLARE_STRUCT(NAME);                                       \
p99_inline                                                      \
void P99_PASTE3(p00_, NAME, _init_func)(T* ARG);                \
p99_inline                                                      \
void P99_PASTE3(p00_, NAME, _init_once)(NAME* ARG) {            \
  if (P99_UNLIKELY(!ARG->p00_once.p00_done.p00_done))           \
    do {                                                        \
      P99_SPIN_EXCLUDE(&ARG->p00_once.p00_flg) {                \
        if (!ARG->p00_once.p00_done.p00_vdone) {                \
          P99_PASTE3(p00_, NAME, _init_func)(&ARG->p00_val);    \
          ARG->p00_once.p00_done.p00_vdone = true;              \
        }                                                       \
      }                                                         \
    } while (!ARG->p00_once.p00_done.p00_vdone);                \
}                                                               \
p99_inline                                                      \
void P99_PASTE3(p00_, NAME, _init_func)(T* ARG)

#define P99_INIT_ONCE(NAME, VARP) P99_PASTE3(p00_, NAME, _init_once)(VARP)

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
#define p00_call_once_2(FLAG, FUNC)                                          \
do {                                                                         \
  p99_once_flag *p00Mflag = (FLAG);                                          \
  if (P99_UNLIKELY(p00Mflag->p00_done.p00_done != p00_once_finished))        \
    do {                                                                     \
      atomic_flag_lock(&p00Mflag->p00_flg);                                  \
      switch (p00Mflag->p00_done.p00_vdone) {                                \
        /* we are doing the initialization */                                \
      case p00_once_uninit:                                                  \
        p00Mflag->p00_done.p00_done = 1;                                     \
        p00Mflag->p00_id = thrd_current();                                   \
        atomic_flag_unlock(&p00Mflag->p00_flg);                              \
        FUNC();                                                              \
        p00Mflag->p00_done.p00_done = 2;                                     \
        break;                                                               \
      case p00_once_started:                                                 \
        if (thrd_equal(p00Mflag->p00_id, thrd_current())) {                  \
          /* we are called recursively, abandon and return */                \
          atomic_flag_unlock(&p00Mflag->p00_flg);                            \
          p00Mflag = 0;                                                      \
          break;                                                             \
        }                                                                    \
        /* otherwise fall through */                                         \
      case p00_once_finished:                                                \
        atomic_flag_unlock(&p00Mflag->p00_flg);                              \
        break;                                                               \
      }                                                                      \
    } while (p00Mflag && p00Mflag->p00_done.p00_vdone != p00_once_finished); \
 } while (false)
123

124
p99_inline
125 126
void p00_call_once_1(p99_once_flag *p00_flag) {
  p00_call_once_2(p00_flag, p00_flag->p00_init);
127 128
}

129
#define p00_call_once_3(FLAG, FUNC, ...)                                     \
130 131 132 133 134 135 136 137 138 139 140
do {                                                                         \
  p99_once_flag *p00Mflag = (FLAG);                                          \
  if (P99_UNLIKELY(p00Mflag->p00_done.p00_done != p00_once_finished))        \
    do {                                                                     \
      atomic_flag_lock(&p00Mflag->p00_flg);                                  \
      switch (p00Mflag->p00_done.p00_vdone) {                                \
        /* we are doing the initialization */                                \
      case p00_once_uninit:                                                  \
        p00Mflag->p00_done.p00_done = 1;                                     \
        p00Mflag->p00_id = thrd_current();                                   \
        atomic_flag_unlock(&p00Mflag->p00_flg);                              \
141
        FUNC(__VA_ARGS__);                                                   \
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
        p00Mflag->p00_done.p00_done = 2;                                     \
        break;                                                               \
      case p00_once_started:                                                 \
        if (thrd_equal(p00Mflag->p00_id, thrd_current())) {                  \
          /* we are called recursively, abandon and return */                \
          atomic_flag_unlock(&p00Mflag->p00_flg);                            \
          p00Mflag = 0;                                                      \
          break;                                                             \
        }                                                                    \
        /* otherwise fall through */                                         \
      case p00_once_finished:                                                \
        atomic_flag_unlock(&p00Mflag->p00_flg);                              \
        break;                                                               \
      }                                                                      \
    } while (p00Mflag && p00Mflag->p00_done.p00_vdone != p00_once_finished); \
 } while (false)
158

Jens Gustedt's avatar
Jens Gustedt committed
159 160 161 162 163
#define p00_call_once(N, ...)                                  \
P99_IF_EQ_1(N)                                                 \
(p00_call_once_1(__VA_ARGS__))                                 \
(P99_IF_EQ_2(N)                                                \
 (p00_call_once_2(__VA_ARGS__))                                \
164 165
 (p00_call_once_3(__VA_ARGS__)))

166
/**
167
 ** @brief Call a function @a FUNC exactly once, optionally
168
 ** providing it with argument @a ARG
169
 **
170 171
 ** This is an extension of the standard function ::call_once.
 **
172 173
 ** - If @a ARG is given, it must be compatible with type @c void* and is
 **   passed to @a FUNC as an argument. In this case @a FUNC must have
174 175 176 177 178
 **   the prototype <code>void FUNC(void*)</code>.
 **
 ** - If @a ARG is omitted @a FUNC should have the prototype
 **   <code>void FUNC(void)</code>, i.e not take any argument.
 **
179 180
 ** - If the field @c p00_init is initialized with an appropriate
 **   function, the @a FUNC can also be omitted and @c p00_init is then
181 182 183 184 185 186 187 188 189 190
 **   called instead.
 **
 ** @remark The @a FLAG is only protected by an
 ** ::atomic_flag. Therefore the functions that are passed to this
 ** should be nice and short.
 **
 ** @remark These functions are protected against recursion and should
 ** not deadlock when they are confronted with cyclic
 ** dependencies. But obviously, the order in which such a cycle is
 ** taken is not predictable.
191
 **
192
 ** @see p99_once_flag
193 194 195 196
 **/
#ifdef P00_DOXYGEN
#define p99_call_once(FLAG, FUNC, ARG)
#else
197
#define p99_call_once(...) p00_call_once(P99_NARG(__VA_ARGS__), __VA_ARGS__)
198 199
#endif

200 201
#ifdef P00_DOXYGEN
/**
202
 ** @brief Define a function that will be called exactly once by
203 204 205 206 207
 ** <code>P99_INIT_CHAIN(T)</code>.
 **
 ** The function has a prototype of <code>void someFunctionName(void)</code>.
 **
 ** @a T can be any valid identifier, the real function name will
208
 ** be mangled such that it will not clash with an existing name.
209 210 211 212 213
 **
 ** The ... list (optional) can be used to give a list of dependencies
 ** from other ::P99_INIT_CHAIN functions.
 ** @code
 ** P99_DEFINE_ONCE_CHAIN(toto) {
214
 **  // initialize some shared ressource
215 216 217 218 219 220 221 222
 ** }
 **
 ** P99_DEFINE_ONCE_CHAIN(tutu, toto) {
 **   // empty
 ** }
 ** @endcode
 **
 ** This will ensure that <code>P99_INIT_CHAIN(toto)</code> is always
223 224 225
 ** triggered by <code>P99_INIT_CHAIN(tutu)</code> and is run before we run
 ** the function @c tutu itself. As shown above in the example, many functions
 ** will be empty, serving just to ensure that all dynamic
226 227 228 229
 ** dependencies are initialized in the right order.
 ** @see P99_DECLARE_ONCE_CHAIN
 ** @see P99_INIT_CHAIN
 **/
Jens Gustedt's avatar
Jens Gustedt committed
230
#define P99_DEFINE_ONCE_CHAIN(T, ...)                          \
231
p99_once_flag p99_ ## T ## _once;                              \
232 233
void p00_ ## T ## _once_init(void)
#else
Jens Gustedt's avatar
Jens Gustedt committed
234
#define P99_DEFINE_ONCE_CHAIN(...)                             \
235 236 237
P99_IF_ELSE(P99_HAS_COMMA(__VA_ARGS__))                        \
 (P00_P99_DEFINE_ONCE_CHAIN_1(__VA_ARGS__))                    \
 (P00_P99_DEFINE_ONCE_CHAIN_0(__VA_ARGS__))
238 239
#endif

Jens Gustedt's avatar
Jens Gustedt committed
240 241
#define P00_P99_DEFINE_ONCE_CHAIN_0(T)                         \
static void P99_PASTE3(p00_, T, _once_init)(void);             \
242
p99_once_flag P99_PASTE3(p99_, T, _once) = {                   \
243
  .p00_init = P99_PASTE3(p00_, T, _once_init),                 \
Jens Gustedt's avatar
Jens Gustedt committed
244
};                                                             \
245 246 247 248 249 250 251 252 253 254 255
static void P99_PASTE3(p00_, T, _once_init)(void)

#define P00_ONCE_INIT(_0, T, _2) P99_INIT_CHAIN(T)

#define P00_P99_DEFINE_ONCE_CHAIN_1(T, ...)                              \
static void P99_PASTE3(p00_, T, _once_init0)(void);                      \
static void P99_PASTE3(p00_, T, _once_init)(void) {                      \
  P99_FOR(, P99_NARG(__VA_ARGS__), P00_SEP, P00_ONCE_INIT, __VA_ARGS__); \
  /* fprintf(stderr, "Initializing " #T "\n");*/                         \
  P99_PASTE3(p00_, T, _once_init0)();                                    \
 }                                                                       \
256
struct p99_once_flag P99_PASTE3(p99_, T, _once) = {                      \
257
  .p00_init = P99_PASTE3(p00_, T, _once_init),                           \
258 259 260 261 262 263 264 265 266 267 268
};                                                                       \
static void P99_PASTE3(p00_, T, _once_init0)(void)

/**
 ** @brief Declare the symbols that are needed for the macro
 ** ::P99_INIT_CHAIN().
 **
 ** @param T should be unique for each use of this macro.
 ** @see P99_INIT_CHAIN
 ** @see P99_DEFINE_ONCE_CHAIN
 **/
Jens Gustedt's avatar
Jens Gustedt committed
269
#define P99_DECLARE_ONCE_CHAIN(T)                              \
270
extern p99_once_flag P99_PASTE3(p99_, T, _once)
271 272 273

/**
 ** @brief Ensure that the function that was defined with
274
 ** ::P99_DEFINE_ONCE_CHAIN has been called exactly once before proceeding.
275
 **
276
 ** Such a call could be placed at the beginning of a user function to
277
 ** ensure that a shared resource is always initialized before its
278 279 280
 ** use. A better strategy though would be to call ::P99_INIT_CHAIN
 ** from @c main, e.g., before any threads of the application are
 ** started, or through the mechanism provided by :::P99_INIT_FUNCTION.
281 282 283
 ** @see P99_DECLARE_ONCE_CHAIN
 ** @see P99_DEFINE_ONCE_CHAIN
 **/
284
#define P99_INIT_CHAIN(T)                                                       \
285
p99_call_once(&P99_PASTE3(p99_, T, _once), P99_PASTE3(p99_, T, _once).p00_init)
286

287 288 289 290 291 292 293
/**
 ** @brief Protect the following block or statement with @c
 ** mtx_t @a MUT.
 **
 ** @see P99_CRITICAL for a tool that uses a spinlock that is
 ** allocated behind the scene.
 **
294
 ** This does some rudimentary error checking for the result of
295
 ** locking. If an error occurs the whole block and any other
296
 ** enclosing blocks that are protected with P99_UNWIND_PROTECT are
297 298 299
 ** aborted.
 **/
P99_BLOCK_DOCUMENT
300
P00_DOCUMENT_PERMITTED_ARGUMENT(P99_MUTUAL_EXCLUDE, 0)
301 302
#define P99_MUTUAL_EXCLUDE(MUT) P00_MUTUAL_EXCLUDE(MUT, P99_UNIQ(mut))

303 304
# if !P99_SIMPLE_BLOCKS
#  define P00_MUTUAL_EXCLUDE(MUT, UNIQ)                                          \
Jens Gustedt's avatar
Jens Gustedt committed
305 306 307
P00_BLK_START                                                                    \
P00_BLK_DECL(int, p00_errNo, 0)                                                  \
P99_GUARDED_BLOCK(mtx_t*,                                                        \
308
                  UNIQ,                                                          \
Jens Gustedt's avatar
Jens Gustedt committed
309
                  &(MUT),                                                        \
310
                  (void)(P99_UNLIKELY(p00_errNo = mtx_lock(UNIQ))                \
Jens Gustedt's avatar
Jens Gustedt committed
311 312
                         && (fprintf(stderr,                                     \
                                     __FILE__ ":"                                \
313
                                     P99_STRINGIFY(__LINE__) ": lock error for " \
314
                                     P99_STRINGIFY(MUT) ", %s\n",                \
Jens Gustedt's avatar
Jens Gustedt committed
315
                                     strerror(p00_errNo)), 1)                    \
316
                         && (UNIQ = 0, 1)                                        \
Jens Gustedt's avatar
Jens Gustedt committed
317 318
                         && (P99_UNWIND(-1), 1)                                  \
                         ),                                                      \
319 320
                  (void)(UNIQ                                                    \
                         && mtx_unlock(UNIQ)))
321
# else
(no author)'s avatar
(no author) committed
322 323 324
#  define P00_MUTUAL_EXCLUDE(MUT, UNIQ)                        \
P00_BLK_START                                                  \
P00_BLK_DECL(mtx_t*, UNIQ, &(MUT))                             \
325 326
P00_BLK_BEFAFT(mtx_lock(UNIQ), mtx_unlock(UNIQ))
# endif
327

328 329 330
p99_inline thrd_t* thrd_t_init(thrd_t *p00_id) {
  if (p00_id) {
    *p00_id = P99_LVAL(thrd_t const);
Jens Gustedt's avatar
Jens Gustedt committed
331
  }
332
  return p00_id;
333 334
}

335 336 337
p99_inline void thrd_t_destroy(thrd_t *p00_id) {
  /* special care for bogus warning given by icc */
  (void)p00_id;
Jens Gustedt's avatar
Jens Gustedt committed
338 339 340
}

p99_inline
341 342 343 344
char const* thrd2str(char *p00_buf, thrd_t p00_id) {
  unsigned char *p00_p = (unsigned char*)&p00_id;
  for (size_t p00_i = 0; p00_i < sizeof(thrd_t); ++p00_i) {
    snprintf(p00_buf + 2*p00_i, 3, "%02X", p00_p[p00_i]);
Jens Gustedt's avatar
Jens Gustedt committed
345
  }
346
  return p00_buf;
Jens Gustedt's avatar
Jens Gustedt committed
347 348
}

349
#define THRD2STR(ID) thrd2str((char[1 + sizeof(thrd_t) * 2]){0}, (ID))
350

351

Jens Gustedt's avatar
Jens Gustedt committed
352
#endif