P99
p99_threads.h
Go to the documentation of this file.
1 /* This may look like nonsense, but it really is -*- mode: C; coding: utf-8 -*- */
2 /* */
3 /* Except for parts copied from previous work and as explicitly stated below, */
4 /* the authors and copyright holders for this work are as follows: */
5 /* (C) copyright 2011-2014, 2018 Jens Gustedt, INRIA, France */
6 /* (C) copyright 2012 William Morris */
7 /* */
8 /* This file is free software; it is part of the P99 project. */
9 /* */
10 /* Licensed under the Apache License, Version 2.0 (the "License"); */
11 /* you may not use this file except in compliance with the License. */
12 /* You may obtain a copy of the License at */
13 /* */
14 /* http://www.apache.org/licenses/LICENSE-2.0 */
15 /* */
16 /* Unless required by applicable law or agreed to in writing, software */
17 /* distributed under the License is distributed on an "AS IS" BASIS, */
18 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
19 /* See the License for the specific language governing permissions and */
20 /* limitations under the License. */
21 /* */
22 #ifndef P99_THREADS_H
23 #define P99_THREADS_H 1
24 
25 #include "p99_try.h"
26 #include "p99_tss.h"
27 
28 #if p99_has_feature(threads_h)
29 # include <threads.h>
30 #elif defined(_XOPEN_SOURCE) || defined(_POSIX_C_SOURCE)
31 # include "p99_threads_posix.h"
32 #else
33 # error "no suitable thread implementation found"
34 #endif
35 
36 #ifndef ONCE_FLAG_INIT
37 typedef struct p99_once_flag once_flag;
38 #define ONCE_FLAG_INIT P99_ONCE_FLAG_INIT
39 #define call_once p99_call_once
40 #endif
41 
42 typedef struct p99_once_flag p99_once_flag;
43 
44 enum p00_once {
45  p00_once_uninit = 0,
46  p00_once_started,
47  p00_once_finished,
48 };
49 
63 struct p99_once_flag {
64  union {
65  enum p00_once p00_done;
66  enum p00_once volatile p00_vdone;
67  } p00_done;
68  thrd_t p00_id;
69  void (*const p00_init)(void);
70  atomic_flag p00_flg;
71 };
72 
73 P00_DOCUMENT_TYPE_ARGUMENT(P99_DECLARE_INIT_ONCE, 0)
74 P00_DOCUMENT_IDENTIFIER_ARGUMENT(P99_DECLARE_INIT_ONCE, 1)
75 P00_DOCUMENT_IDENTIFIER_ARGUMENT(P99_DECLARE_INIT_ONCE, 2)
76 #define P99_DECLARE_INIT_ONCE(T, NAME, ARG) \
77  \
78 struct NAME { \
79  p99_once_flag p00_once; \
80  T p00_val; \
81 }; \
82 P99_DECLARE_STRUCT(NAME); \
83 p99_inline \
84 void P99_PASTE3(p00_, NAME, _init_func)(T* ARG); \
85 p99_inline \
86 void P99_PASTE3(p00_, NAME, _init_once)(NAME* ARG) { \
87  if (P99_UNLIKELY(!ARG->p00_once.p00_done.p00_done)) \
88  do { \
89  P99_SPIN_EXCLUDE(&ARG->p00_once.p00_flg) { \
90  if (!ARG->p00_once.p00_done.p00_vdone) { \
91  P99_PASTE3(p00_, NAME, _init_func)(&ARG->p00_val); \
92  ARG->p00_once.p00_done.p00_vdone = true; \
93  } \
94  } \
95  } while (!ARG->p00_once.p00_done.p00_vdone); \
96 } \
97 p99_inline \
98 void P99_PASTE3(p00_, NAME, _init_func)(T* ARG)
99 
100 #define P99_INIT_ONCE(NAME, VARP) P99_PASTE3(p00_, NAME, _init_once)(VARP)
101 
102 #define p00_call_once_2(FLAG, FUNC) \
103 do { \
104  p99_once_flag *p00Mflag = (FLAG); \
105  if (P99_UNLIKELY(p00Mflag->p00_done.p00_done != p00_once_finished)) \
106  do { \
107  atomic_flag_lock(&p00Mflag->p00_flg); \
108  switch (p00Mflag->p00_done.p00_vdone) { \
109  /* we are doing the initialization */ \
110  case p00_once_uninit: \
111  p00Mflag->p00_done.p00_done = 1; \
112  p00Mflag->p00_id = thrd_current(); \
113  atomic_flag_unlock(&p00Mflag->p00_flg); \
114  FUNC(); \
115  p00Mflag->p00_done.p00_done = 2; \
116  break; \
117  case p00_once_started: \
118  if (thrd_equal(p00Mflag->p00_id, thrd_current())) { \
119  /* we are called recursively, abandon and return */ \
120  atomic_flag_unlock(&p00Mflag->p00_flg); \
121  p00Mflag = 0; \
122  break; \
123  } \
124  /* otherwise fall through */ \
125  case p00_once_finished: \
126  atomic_flag_unlock(&p00Mflag->p00_flg); \
127  break; \
128  } \
129  } while (p00Mflag && p00Mflag->p00_done.p00_vdone != p00_once_finished); \
130  } while (false)
131 
133 void p00_call_once_1(p99_once_flag *p00_flag) {
134  p00_call_once_2(p00_flag, p00_flag->p00_init);
135 }
136 
137 #define p00_call_once_3(FLAG, FUNC, ...) \
138 do { \
139  p99_once_flag *p00Mflag = (FLAG); \
140  if (P99_UNLIKELY(p00Mflag->p00_done.p00_done != p00_once_finished)) \
141  do { \
142  atomic_flag_lock(&p00Mflag->p00_flg); \
143  switch (p00Mflag->p00_done.p00_vdone) { \
144  /* we are doing the initialization */ \
145  case p00_once_uninit: \
146  p00Mflag->p00_done.p00_done = 1; \
147  p00Mflag->p00_id = thrd_current(); \
148  atomic_flag_unlock(&p00Mflag->p00_flg); \
149  FUNC(__VA_ARGS__); \
150  p00Mflag->p00_done.p00_done = 2; \
151  break; \
152  case p00_once_started: \
153  if (thrd_equal(p00Mflag->p00_id, thrd_current())) { \
154  /* we are called recursively, abandon and return */ \
155  atomic_flag_unlock(&p00Mflag->p00_flg); \
156  p00Mflag = 0; \
157  break; \
158  } \
159  /* otherwise fall through */ \
160  case p00_once_finished: \
161  atomic_flag_unlock(&p00Mflag->p00_flg); \
162  break; \
163  } \
164  } while (p00Mflag && p00Mflag->p00_done.p00_vdone != p00_once_finished); \
165  } while (false)
166 
167 #define p00_call_once(N, ...) \
168 P99_IF_EQ_1(N) \
169 (p00_call_once_1(__VA_ARGS__)) \
170 (P99_IF_EQ_2(N) \
171  (p00_call_once_2(__VA_ARGS__)) \
172  (p00_call_once_3(__VA_ARGS__)))
173 
202 #ifdef P00_DOXYGEN
203 #define p99_call_once(FLAG, FUNC, ARG)
204 #else
205 #define p99_call_once(...) p00_call_once(P99_NARG(__VA_ARGS__), __VA_ARGS__)
206 #endif
207 
208 #ifdef P00_DOXYGEN
209 
238 #define P99_DEFINE_ONCE_CHAIN(T, ...) \
239 p99_once_flag p99_ ## T ## _once; \
240 void p00_ ## T ## _once_init(void)
241 #else
242 #define P99_DEFINE_ONCE_CHAIN(...) \
243 P99_IF_ELSE(P99_HAS_COMMA(__VA_ARGS__)) \
244  (P00_P99_DEFINE_ONCE_CHAIN_1(__VA_ARGS__)) \
245  (P00_P99_DEFINE_ONCE_CHAIN_0(__VA_ARGS__))
246 #endif
247 
248 #define P00_P99_DEFINE_ONCE_CHAIN_0(T) \
249 static void P99_PASTE3(p00_, T, _once_init)(void); \
250 p99_once_flag P99_PASTE3(p99_, T, _once) = { \
251  .p00_init = P99_PASTE3(p00_, T, _once_init), \
252 }; \
253 static void P99_PASTE3(p00_, T, _once_init)(void)
254 
255 #define P00_ONCE_INIT(_0, T, _2) P99_INIT_CHAIN(T)
256 
257 #define P00_P99_DEFINE_ONCE_CHAIN_1(T, ...) \
258 static void P99_PASTE3(p00_, T, _once_init0)(void); \
259 static void P99_PASTE3(p00_, T, _once_init)(void) { \
260  P99_FOR(, P99_NARG(__VA_ARGS__), P00_SEP, P00_ONCE_INIT, __VA_ARGS__); \
261  /* fprintf(stderr, "Initializing " #T "\n");*/ \
262  P99_PASTE3(p00_, T, _once_init0)(); \
263  } \
264 struct p99_once_flag P99_PASTE3(p99_, T, _once) = { \
265  .p00_init = P99_PASTE3(p00_, T, _once_init), \
266 }; \
267 static void P99_PASTE3(p00_, T, _once_init0)(void)
268 
277 #define P99_DECLARE_ONCE_CHAIN(T) \
278 extern p99_once_flag P99_PASTE3(p99_, T, _once)
279 
292 #define P99_INIT_CHAIN(T) \
293 p99_call_once(&P99_PASTE3(p99_, T, _once), P99_PASTE3(p99_, T, _once).p00_init)
294 
308 P00_DOCUMENT_PERMITTED_ARGUMENT(P99_MUTUAL_EXCLUDE, 0)
309 #define P99_MUTUAL_EXCLUDE(MUT) P00_MUTUAL_EXCLUDE(MUT, P99_UNIQ(mut))
310 
311 # if !P99_SIMPLE_BLOCKS
312 # define P00_MUTUAL_EXCLUDE(MUT, UNIQ) \
313 P00_BLK_START \
314 P00_BLK_DECL(int, p00_errNo, 0) \
315 P99_GUARDED_BLOCK(mtx_t*, \
316  UNIQ, \
317  &(MUT), \
318  (void)(P99_UNLIKELY(p00_errNo = mtx_lock(UNIQ)) \
319  && (fprintf(stderr, \
320  __FILE__ ":" \
321  P99_STRINGIFY(__LINE__) ": lock error for " \
322  P99_STRINGIFY(MUT) ", %s\n", \
323  strerror(p00_errNo)), 1) \
324  && (UNIQ = 0, 1) \
325  && (P99_UNWIND(-1), 1) \
326  ), \
327  (void)(UNIQ \
328  && mtx_unlock(UNIQ)))
329 # else
330 # define P00_MUTUAL_EXCLUDE(MUT, UNIQ) \
331 P00_BLK_START \
332 P00_BLK_DECL(mtx_t*, UNIQ, &(MUT)) \
333 P00_BLK_BEFAFT(mtx_lock(UNIQ), mtx_unlock(UNIQ))
334 # endif
335 
336 p99_inline thrd_t* thrd_t_init(thrd_t *p00_id) {
337  if (p00_id) {
338  *p00_id = P99_LVAL(thrd_t const);
339  }
340  return p00_id;
341 }
342 
343 p99_inline void thrd_t_destroy(thrd_t *p00_id) {
344  /* special care for bogus warning given by icc */
345  (void)p00_id;
346 }
347 
349 char const* thrd2str(char *p00_buf, thrd_t p00_id) {
350  unsigned char *p00_p = (unsigned char*)&p00_id;
351  for (size_t p00_i = 0; p00_i < sizeof(thrd_t); ++p00_i) {
352  snprintf(p00_buf + 2*p00_i, 3, "%02X", p00_p[p00_i]);
353  }
354  return p00_buf;
355 }
356 
357 #define THRD2STR(ID) thrd2str((char[1 + sizeof(thrd_t) * 2]){0}, (ID))
358 
359 
360 #endif
void
void void
Definition: p99_bitset.h:84
P99_BLOCK_DOCUMENT
#define P99_BLOCK_DOCUMENT
Add some default documentation and links to the following block macro.
Definition: p99_block.h:732
p99_once_flag
complete object type that holds a flag for use by p99_call_once
Definition: p99_threads.h:62
p99_tss.h
p99_threads_posix.h
thrd_t_init
thrd_t * thrd_t_init(thrd_t *p00_id)
Definition: p99_threads.h:335
p99_inline
#define p99_inline
Try to force a function always to be inlined.
Definition: p99_compiler.h:496
thrd2str
char const * thrd2str(char *p00_buf, thrd_t p00_id)
Definition: p99_threads.h:348
P99_LVAL
#define P99_LVAL(...)
Define an lvalue of type T, where T is the first parameter in the variable parameter list.
Definition: p99_int.h:1084
p99_try.h
P99_DECLARE_INIT_ONCE
#define P99_DECLARE_INIT_ONCE(T, NAME, ARG)
Definition: p99_threads.h:75
P99_MUTUAL_EXCLUDE
#define P99_MUTUAL_EXCLUDE(MUT)
Protect the following block or statement with mtx_t MUT.
Definition: p99_threads.h:308
thrd_t_destroy
void thrd_t_destroy(thrd_t *p00_id)
Definition: p99_threads.h:342