P99
p99_threads_posix.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_POSIX_H
23 #define P99_THREADS_POSIX_H 1
24 
62 #define P99_ONCE_FLAG_INIT { { 0 } }
63 
64 #ifndef PTHREAD_DESTRUCTOR_ITERATIONS
65 # warning "definition of PTHREAD_DESTRUCTOR_ITERATIONS is missing"
66 
72 # define TSS_DTOR_ITERATIONS 1
73 #else
74 # define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
75 #endif
76 
93 P99_ENC_DECLARE(pthread_cond_t, cnd_t);
94 
95 typedef struct p00_thrd p00_thrd;
108 P99_ENC_DECLARE(struct p00_thrd*, thrd_t);
109 
115 P99_ENC_DECLARE(pthread_mutex_t, mtx_t);
116 
122 typedef int (*thrd_start_t)(void*);
123 
124 
125 
130 struct p00_thrd {
131  atomic_flag p00_detached;
132  unsigned p00_foreign;
133  int p00_ret;
134  pthread_t p00_id;
135  union {
136  struct {
137  thrd_start_t p00_func;
138  void *p00_arg;
139  } p00_init;
140  jmp_buf p00_jmp;
141  } p00_ovrl;
142 };
143 
152 enum mtx_type {
158 
164 
171 
184 
193 
202 };
203 
204 
209 // 7.26.2 Initialization functions
210 
215 void thrd_yield(void) {
216  if (P99_UNLIKELY(sched_yield())) errno = 0;
217 }
218 
219 /* Tentative definitions for global variables. This has the advantage
220  that this defines weak symbols and we avoid to have to create a
221  specific library. */
222 P99_DECLARE_THREAD_LOCAL(p00_thrd *, p00_thrd_local);
223 
224 #define P00_THRD_LOCAL P99_THREAD_LOCAL(p00_thrd_local)
225 
226 P99_WEAK(p00_foreign_nb)
227 _Atomic(size_t) p00_foreign_nb;
228 
229 P99_WEAK(p00_foreign_tab)
230 p00_thrd ** p00_foreign_tab;
231 
232 P99_WEAK(p00_foreign_cleanup)
233 void p00_foreign_cleanup(void) {
234  size_t p00_foreign = atomic_load_explicit(&p00_foreign_nb, memory_order_consume);
235  p00_thrd ** p00_thrd = p00_foreign_tab;
236  p00_foreign_tab = 0;
237  if (p00_foreign) {
238  /* Ensure that all data is synchronized with all threads, not only
239  with the last one that changed p00_foreign_nb. */
240  atomic_thread_fence(memory_order_seq_cst);
241  for (size_t p00_i = 0; p00_i < p00_foreign; ++p00_i) {
242  if (!pthread_equal(p00_thrd[p00_i]->p00_id, pthread_self()))
243  fputs("found foreign thread\n", stderr);
244  free(p00_thrd[p00_i]);
245  }
246  }
247  free(p00_thrd);
248 }
249 
256 thrd_t thrd_current(void) {
257  p00_thrd * p00_loc = P00_THRD_LOCAL;
258  if (P99_UNLIKELY(!p00_loc)) {
259  size_t p00_nb = atomic_fetch_add_explicit(&p00_foreign_nb, 1, memory_order_acq_rel);
260  if (!p00_nb) atexit(p00_foreign_cleanup);
261  if ((p00_nb^(p00_nb-1)) == (p00_nb+(p00_nb-1))) {
262  p00_foreign_tab = realloc(p00_foreign_tab, sizeof(p00_thrd*[2*(p00_nb+1)]));
263  }
264  p00_loc = malloc(sizeof *p00_loc);
265  p00_foreign_tab[p00_nb] = p00_loc;
266  *p00_loc = (p00_thrd) {
267  .p00_id = pthread_self(),
268  .p00_foreign = p00_nb + 1,
269  };
270  P00_THRD_LOCAL = p00_loc;
271  if (p00_nb) {
272  union {
273  unsigned char raw[16];
274  pthread_t thrd;
275  unsigned long long ull;
276  } id = { .raw = { 0 } };
277  id.thrd = p00_loc->p00_id;
278  fprintf(stderr, "foreign thread %llu is %zu\n", id.ull, p00_nb + 1);
279  }
280  }
281  return (thrd_t)P99_ENC_INIT(p00_loc);
282 }
283 
292 int thrd_equal(thrd_t p00_thr0, thrd_t p00_thr1) {
293  return P99_ENC(p00_thr0) == P99_ENC(p00_thr1);
294 }
295 
296 
297 // 7.26.3 Condition variable functions
298 
307 int cnd_broadcast(cnd_t *p00_cond) {
308  return pthread_cond_broadcast(&P99_ENCP(p00_cond)) ? thrd_error : thrd_success;
309 }
310 
315 void cnd_destroy(cnd_t *p00_cond) {
316  (void)pthread_cond_destroy(&P99_ENCP(p00_cond));
317 }
318 
329 int cnd_init(cnd_t *p00_cond) {
330  int p00_ret = pthread_cond_init(&P99_ENCP(p00_cond), 0);
331  switch (p00_ret) {
332  case 0: return thrd_success;
333  case ENOMEM: return thrd_nomem;
334  default: return thrd_error;
335  }
336 }
337 
346 int cnd_signal(cnd_t *p00_cond) {
347  return pthread_cond_signal(&P99_ENCP(p00_cond)) ? thrd_error : thrd_success;
348 }
349 
350 #if (POSIX_TIMEOUTS > 0) || !defined(POSIX_TIMEOUTS)
351 
362 int cnd_timedwait(cnd_t *restrict p00_cond, mtx_t *restrict p00_mtx, const struct timespec *restrict p00_ts) {
363  int p00_ret = pthread_cond_timedwait(&P99_ENCP(p00_cond), &P99_ENCP(p00_mtx), p00_ts);
364  switch (p00_ret) {
365  case 0: return thrd_success;
366  case ETIMEDOUT: return thrd_timedout;
367  default: return thrd_error;
368  };
369 }
370 
371 #endif
372 
381 int cnd_wait(cnd_t *p00_cond, mtx_t *p00_mtx) {
382  return pthread_cond_wait(&P99_ENCP(p00_cond), &P99_ENCP(p00_mtx)) ? thrd_error : thrd_success;
383 }
384 
385 // 7.26.4 Mutex functions
386 
391 void mtx_destroy(mtx_t *p00_mtx) {
392  (void)pthread_mutex_destroy(&P99_ENCP(p00_mtx));
393 }
394 
406 int mtx_init(mtx_t *p00_mtx, int p00_type) {
407  if (p00_type & mtx_extras) {
408  pthread_mutexattr_t p00_attr;
409  int p00_ret = pthread_mutexattr_init(&p00_attr);
410  if (P99_UNLIKELY(p00_ret)) return thrd_error;
411  switch (p00_type & mtx_extras) {
412  case mtx_normal: p00_ret = PTHREAD_MUTEX_NORMAL; break;
413  case mtx_errorcheck: p00_ret = PTHREAD_MUTEX_ERRORCHECK; break;
414  case mtx_recursive: p00_ret = PTHREAD_MUTEX_RECURSIVE; break;
415  default: return thrd_error;
416  }
417  p00_ret = pthread_mutexattr_settype(&p00_attr, p00_ret);
418  if (P99_UNLIKELY(p00_ret)) return thrd_error;
419  p00_ret = pthread_mutex_init(&P99_ENCP(p00_mtx), &p00_attr);
420  if (P99_UNLIKELY(p00_ret)) return thrd_error;
421  } else {
422  int p00_ret = pthread_mutex_init(&P99_ENCP(p00_mtx), 0);
423  if (P99_UNLIKELY(p00_ret)) return thrd_error;
424  }
425  return thrd_success;
426 }
427 
435 int mtx_lock(mtx_t *p00_mtx) {
436  return pthread_mutex_lock(&P99_ENCP(p00_mtx)) ? thrd_error : thrd_success;
437 }
438 
439 #if (POSIX_TIMEOUTS > 0) || !defined(POSIX_TIMEOUTS)
440 
451 int mtx_timedlock(mtx_t *restrict p00_mtx, const struct timespec *restrict p00_ts) {
452  int p00_ret = pthread_mutex_timedlock(&P99_ENCP(p00_mtx), p00_ts);
453  switch (p00_ret) {
454  case 0: return thrd_success;
455  case ETIMEDOUT: return thrd_timedout;
456  default: return thrd_error;
457  };
458 }
459 
460 #endif
461 
470 int mtx_trylock(mtx_t *p00_mtx) {
471  int p00_ret = pthread_mutex_trylock(&P99_ENCP(p00_mtx));
472  switch (p00_ret) {
473  case 0: return thrd_success;
474  case EBUSY: return thrd_busy;
475  default: return thrd_error;
476  };
477 }
478 
486 int mtx_unlock(mtx_t *p00_mtx) {
487  return pthread_mutex_unlock(&P99_ENCP(p00_mtx)) ? thrd_error : thrd_success;
488 }
489 
490 
491 P99_SETJMP_INLINE(p00_thrd_create)
492 void * p00_thrd_create(void* p00_context);
493 
503 int thrd_create(thrd_t *p00_thr, thrd_start_t p00_func, void *p00_arg) {
504  p00_thrd * p00_cntxt = malloc(sizeof *p00_cntxt);
505  if (!p00_cntxt) return thrd_nomem;
506  *p00_cntxt = (p00_thrd const) {
507  .p00_ovrl = {
508  .p00_init = {
509  .p00_func = p00_func,
510  .p00_arg = p00_arg,
511  },
512  },
513  .p00_detached = ATOMIC_FLAG_INIT,
514  };
515  int p00_ret = pthread_create(&p00_cntxt->p00_id, 0, p00_thrd_create, p00_cntxt);
516  if (P99_UNLIKELY(p00_ret)) {
517  free(p00_cntxt);
518  switch (p00_ret) {
519  case ENOMEM: return thrd_nomem;
520  default: return thrd_error;
521  };
522  } else {
523  P99_ENCP(p00_thr) = p00_cntxt;
524  return thrd_success;
525  }
526 }
527 
536 int thrd_detach(thrd_t p00_thr) {
537  /* The thread is not yet detached so its pthread id is still
538  valid. If it already has finished, this will just free the
539  resources that pthread holds for it. */
540  int p00_ret = pthread_detach(P99_ENC(p00_thr)->p00_id) ? thrd_error : thrd_success;
541  if (atomic_flag_test_and_set_explicit(&P99_ENC(p00_thr)->p00_detached, memory_order_acq_rel)) {
542  /* The thread has already finished. Free the state, since nobody
543  will join it, anyhow. */
544  free(P99_ENC(p00_thr));
545  return thrd_success;
546  } else {
547  return p00_ret;
548  }
549 }
550 
551 #ifdef P00_DOXYGEN
552 
555 p99_inline void thrd_exit(int p00_res);
556 #else
558 _Noreturn
559 void thrd_exit(int p00_res) {
560  p00_thrd * p00_cntxt = P00_THRD_LOCAL;
561  if (P99_LIKELY(p00_cntxt)) {
562  if (!p00_cntxt->p00_foreign) {
563  p00_cntxt->p00_ret = p00_res;
564  longjmp(p00_cntxt->p00_ovrl.p00_jmp, 1);
565  } else {
566  free(p00_cntxt);
567  P00_THRD_LOCAL = 0;
568  }
569  }
570  /* should only be reached by threads that where created directly
571  with pthreads, e.g main */
572  fprintf(stderr,
573  "P99: We are exiting a foreign thread (main?) with error code %d\n"
574  "P99: There is no consistent way we can transmit that code to the environment\n",
575  p00_res);
576  pthread_exit(0);
577 }
578 #endif
579 
580 #if defined(P99_INTERCEPT_MAIN) || defined(P00_DOXYGEN)
581 
601 int p99_threads_main(int, char*[]);
602 
603 struct p00_threads_main_arg {
604  int p00_argc;
605  char**p00_argv;
606 };
607 
608 P99_WEAK(p00_threads_main)
609 int p00_threads_main(void* p00_arg) {
610  struct p00_threads_main_arg *p00_a = p00_arg;
611  int p00_argc = p00_a->p00_argc;
612  char**p00_argv = p00_a->p00_argv;
613  free(p00_arg);
614  /* To have the same behavior as prescribed by the standard, we have
615  to exit the whole process if main returns. The only thing that we
616  wouldn't be able to emulate is the fact that leaving main without
617  return statement is permitted. But the compiler should warn the
618  user about that. */
619  int p00_ret = p99_threads_main(p00_argc, p00_argv);
620  fprintf(stderr, "exiting main thread with code %d", p00_ret);
621  fflush(0);
622  exit(p00_ret);
623 }
624 
626  thrd_t id = P99_INIT;
627  struct p00_threads_main_arg * p00_arg = malloc(sizeof *p00_arg);
628  *p00_arg = (struct p00_threads_main_arg) {
629  .p00_argc = *p00_argc,
630  .p00_argv = *p00_argv,
631  };
632  thrd_create(&id, p00_threads_main, p00_arg);
633  thrd_detach(id);
634  pthread_exit(0);
635 }
636 
637 #undef main
638 #define main p99_threads_main
639 #endif
640 
641 
642 
650 int thrd_join(thrd_t p00_thr, int *p00_res) {
651  void *p00_res0;
652  if (P99_UNLIKELY(pthread_join(P99_ENC(p00_thr)->p00_id, &p00_res0))) return thrd_error;
653  if (p00_res) *p00_res = P99_ENC(p00_thr)->p00_ret;
654  free(P99_ENC(p00_thr));
655  return thrd_success;
656 }
657 
668 int thrd_sleep(const struct timespec *p00_duration, struct timespec *p00_remaining) {
669  errno = 0;
670  int p00_ret = nanosleep(p00_duration, p00_remaining);
671  if (p00_ret) {
672  p00_ret = (errno == EINTR) ? thrd_intr : thrd_error;
673  errno = 0;
674  return p00_ret;
675  } else return thrd_success;
676 }
677 
678 /*
679  This is static inline because of the following message from gcc:
680 
681  sorry, unimplemented: function ‘p00_thrd_create’ can never be inlined because it uses setjmp
682 */
684 P99_SETJMP_INLINE(p00_thrd_create)
685 void * p00_thrd_create(void* p00_context) {
686  p00_thrd * p00_cntxt = p00_context;
687  P00_THRD_LOCAL = p00_cntxt;
688  {
689  thrd_start_t p00_func = p00_cntxt->p00_ovrl.p00_init.p00_func;
690  void * p00_arg = p00_cntxt->p00_ovrl.p00_init.p00_arg;
691  if (!setjmp(p00_cntxt->p00_ovrl.p00_jmp)) {
692  p00_cntxt->p00_ret = p00_func(p00_arg);
693  }
694  if (atomic_flag_test_and_set_explicit(&p00_cntxt->p00_detached, memory_order_acq_rel)) {
695  free(p00_cntxt);
696  }
697  }
698  P00_THRD_LOCAL = 0;
699  return 0;
700 }
701 
702 
708 #endif /* P99_THREADS_POSIX_H */
mtx_errorcheck
@ mtx_errorcheck
passed to ::mtx_init to create a mutex object that returns an error when locked twice
Definition: p99_threads_posix.h:192
mtx_normal
@ mtx_normal
passed to ::mtx_init to create a mutex object that deadlocks when locked twice
Definition: p99_threads_posix.h:183
void
void void
Definition: p99_bitset.h:84
P99_DECLARE_THREAD_LOCAL
P99_DECLARE_THREAD_LOCAL(p00_thrd *, p00_thrd_local)
thrd_timedout
@ thrd_timedout
returned by a timed wait function to indicate that the time specified in the call was reached without...
Definition: p99_tss.h:114
thrd_busy
@ thrd_busy
returned by a function to indicate that the requested operation failed because a resource requested b...
Definition: p99_tss.h:124
pthread_cond_init
#define pthread_cond_init(...)
Default arguments for POSIX function pthread_cond_init.
Definition: p99_posix_default.h:52
mtx_timed
@ mtx_timed
passed to ::mtx_init to create a mutex object that supports timeout
Definition: p99_threads_posix.h:170
thrd_start_t
int(* thrd_start_t)(void *)
function pointer type int (*)(void*) that is passed to ::thrd_create to create a new thread
Definition: p99_threads_posix.h:122
P99_LIKELY
#define P99_LIKELY(...)
Mark the conditional expression as being likely.
Definition: p99_compiler.h:1010
P99_WARN_UNUSED_RESULT
#define P99_WARN_UNUSED_RESULT
On architectures that support this, warn if the result of a function is not used.
Definition: p99_compiler.h:602
pthread_mutex_init
#define pthread_mutex_init(...)
Default arguments for POSIX function pthread_mutex_init.
Definition: p99_posix_default.h:58
mtx_type
mtx_type
C11 mutex types.
Definition: p99_threads_posix.h:152
p99_threads_main
int p99_threads_main(int, char *[])
A replacement name for the users main function, experimental.
P99_ENC
#define P99_ENC(OBJ)
Access an encapsulated object.
Definition: p99_type.h:190
_Noreturn
#define _Noreturn
Declare a function that doesn't return to the caller.
Definition: p99_compiler.h:835
thrd_success
@ thrd_success
returned by a function to indicate that the requested operation succeeded
Definition: p99_tss.h:118
p99_inline
#define p99_inline
Try to force a function always to be inlined.
Definition: p99_compiler.h:496
mtx_plain
@ mtx_plain
passed to ::mtx_init to create a mutex object that supports neither timeout nor test and return
Definition: p99_threads_posix.h:157
P99_INIT
#define P99_INIT
A catch all 0-initializer.
Definition: p99_int.h:1065
thrd_nomem
@ thrd_nomem
returned by a function to indicate that the requested operation failed because it was unable to alloc...
Definition: p99_tss.h:133
P99_WEAK
P99_WEAK(p00_foreign_nb) atomic_size_t p00_foreign_nb
mtx_recursive
@ mtx_recursive
passed to ::mtx_init to create a mutex object that supports recursive locking
Definition: p99_threads_posix.h:163
P99_CONST_FUNCTION
#define P99_CONST_FUNCTION
On architectures that support this, assert that a function is "const", i.e only depends on parameters...
Definition: p99_compiler.h:622
mtx_extras
@ mtx_extras
used internally
Definition: p99_threads_posix.h:201
longjmp
#define longjmp(...)
Default arguments for C99 function longjmp
Definition: p99_c99_default.h:55
thrd_intr
@ thrd_intr
(extension) returned by ::thrd_sleep to indicate that the corresponding request has been interrupted ...
Definition: p99_tss.h:138
thrd_error
@ thrd_error
returned by a function to indicate that the requested operation failed
Definition: p99_tss.h:128
P99_SETJMP_INLINE
P99_SETJMP_INLINE(p00_thrd_create) void *p00_thrd_create(void *p00_context)
Definition: p99_threads_posix.h:684
P99_ENC_INIT
#define P99_ENC_INIT(V)
Initialize an encapsulated object.
Definition: p99_type.h:206
P99_ENCP
#define P99_ENCP(OBJP)
Access an encapsulated object through a pointer.
Definition: p99_type.h:196
P99_UNLIKELY
#define P99_UNLIKELY(...)
Mark the conditional expression as being unlikely.
Definition: p99_compiler.h:994
errno
errno
Definition: p99_constraint.h:199
P99_ENC_DECLARE
P99_ENC_DECLARE(pthread_cond_t, cnd_t)
complete object type that holds an identifier for a condition variable
P99_MAIN_INTERCEPT
P99_MAIN_INTERCEPT(p99_threads_main)
Definition: p99_threads_posix.h:625