forked from SevenChords/CipesAtHome
-
Notifications
You must be signed in to change notification settings - Fork 0
/
openmp_once.h
154 lines (137 loc) · 8.64 KB
/
openmp_once.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
* A definition for a once construct for OpenMP.
* Similar to pthread_once, except takes a block instead of taking a function pointer.
*
* Example usage:
* openmp_once_t is_thing_done = OPENMP_ONCE_INIT
*
* bool doTheThingButOnce() {
* OPENMP_ONCE(is_thing_done, thing_section) {
* bool succeeded = tryTheThing;
* thing_section->succeeded = succeeded;
* } OPENMP_ONCE_CLOSE
* return is_thing_done->succeeded;
* }
*
* Here is to hoping in the future openmp adds a '#pragma omp once' directive, and then this won't be needed anymore.
*
* All parameters to the public macros MUST be side effect free (this is repeated in the documentation for each macro).
*
* NOTE: These macros rely on threadprivate semantics and omp critical sections, which may have unexpected results
* when used outside of a parallel region. It is recommended you use a separate "once API" to handle calls
* outside a parallel region. An alternative, if there will be no threads calling from outside of a parallel region except one,
* is a dedicated openmp_once_t for the outside of the parallel block case.
* You can tell the difference at runtime by calling "omp_in_parallel()" from omp.h.
*/
#ifndef OPENMP_ONCE_H_
#define OPENMP_ONCE_H_
#include <stdbool.h>
#include "absl/base/port.h"
#ifdef __cplusplus
extern "C" {
#endif
// The "tried" member gets set and read by the ONCE macros.
// The "succeeded" member is free for use by the program.
// It has no special meaning to OPENMP_ONCE.
// Typically intended to be set while under the inner critical section block the user provides.
typedef struct openmp_once_t {
// DO NOT WRITE TO THIS VARIABLE YOURSELF. The OPENMP_ONCE macros take care of it.
// Must be written only under a critical section (usually handled by _CIPES_OPENMP_ONCE). Can be read outside of it just fine.
bool tried;
#ifndef NO_SUCCEEDED_FLAG
bool succeeded; // Usually the idea is that this gets written under block the user supplies if the underlying action succeeded, but using it is optional.
#endif
} openmp_once_t;
#define OPENMP_ONCE_INIT (openmp_once_t){false, false}
// Internal helper macro, do not use externally
// Short for CIPES_ONCE_MAKE_LOCAL_NAME
#define _CIPES_OMLN(x) _##x##_tried_local
// @formatter:off
//printf("Thread: %d Section: %s Local var: %d once_struct.tried: %d once_struct.succeeded: %d\n", omp_in_parallel() ? omp_get_thread_num() : -1, _XSTR(critical_section_name), (local_var), (once_struct).tried, (once_struct).succeeded);
// Internal helper macro, do not use externally
#define _OPENMP_ONCE_IMPL(local_var, once_struct, once_struct_volatile, critical_section_name) \
if (ABSL_PREDICT_FALSE(!(local_var) || !((local_var) = (once_struct).tried))) \
_Pragma(_XSTR(omp critical(critical_section_name))) \
{ \
/* Force read as volatile so we don't get the compiler using a cached read from above. */ \
(local_var) = (once_struct_volatile).tried; \
(once_struct).tried = 1; \
if (!(local_var))
// Runs the specified block of code only once based on the state of the given state struct.
// The block is responsible for setting succeeded to true if it did succeed based on its definition
// (or leave it untouched if you don't care, you just want it run once).
// As the block following this macro runs under a OpenMP critical section, you cannot return out of it.
//
// All parameters MUST be side effect free.
//
// After closing the block, you must follow it with OPENMP_ONCE_CLOSE (either on the same line or the next, your choice)
//
// once_struct_var: An *identifier* to the appropriate openmp_once_t used as backing state (and not a pointer to it).
// This is because it names the static variable to store thread local state based on this name.
// If you cannot have a simple identifier for whatever reason, use OPENMP_ONCE_WITH_LOCAL_NAME.
// If it is threadprivate, then the block will happen at most once among all threads.
// If it is not threadprivate, then the block will be run once for every thread, but
// all of them will be guarded by the same critical section so only one thread can
// can have the block run at once (useful for APIs that require at most one thread call them)
// If you need it once for every thread and no critical block, then you aren't using OpenMP's
// tooling at all, and thus this API isn't appropriate for you.
// critical_section_name: the name of the OpenMP critical section. Do not surround with quotes.
#define OPENMP_ONCE(once_struct_var, critical_section_name) \
static bool _CIPES_OMLN(once_struct_var) = false; \
_Pragma(_XSTR(omp threadprivate(_CIPES_OMLN(once_struct_var)))) \
_OPENMP_ONCE_IMPL(_CIPES_OMLN(once_struct_var), once_struct_var, (volatile openmp_once_t)once_struct_var, critical_section_name)
// Runs the specified block of code only once based on the state of the given state struct.
// The block is responsible for setting succeeded to true if it did succeed based on its definition
// (or leave it untouched if you don't care, you just want it run once).
// As the block following this macro runs under a OpenMP critical section, you cannot return out of it.
//
// All parameters MUST be side effect free.
//
// After closing the block, you must follow it with OPENMP_ONCE_CLOSE (either on the same line or the next, your choice)
//
// local_name: the name of the *already defined* thread local variable to use to prevent the having to read under
// critical section every time (once a thread sees "tried" as true, it doesn't need to take the lock anymore).
// Must be a variable declared (and not a pointer to such) that was made thread local
// using '#pragma omp threadprivate(threadprivate_local_name)
// If you must have a pointer to an already declared one use OPENMP_ONCE_USE_PTRS.
// once_struct: the openmp_once_t struct to use as the backing state (not a pointer to it)
// If you need a pointer, use OPENMP_ONCE_USE_PTRS.
// This can be either threadprivate or not.
// If it is threadprivate, then the block will happen at most once among all threads.
// If it is not threadprivate, then the block will be run once for every thread, but
// all of them will be guarded by the same critical section so only one thread can
// can have the block run at once (useful for APIs that require at most one thread call them)
// If you need it once for every thread and no critical block, then you aren't using OpenMP's
// tooling at all, and thus this API isn't appropriate for you.
// critical_section_name: the name of the OpenMP critical section. Do not surround with quotes.
#define OPENMP_ONCE_WITH_LOCAL_NAME(threadprivate_local_name, once_struct, critical_section_name) \
_OPENMP_ONCE_IMPL(threadprivate_local_name, once_struct, (volatile openmp_once_t)once_struct, critical_section_name)
// Runs the specified block of code only once based on the state of the given state struct.
// The block is responsible for setting succeeded to true if it did succeed based on its definition
// (or leave it untouched if you don't care, you just want it run once).
// As the block following this macro runs under a OpenMP critical section, you cannot return out of it.
//
// All parameters MUST be side effect free.
//
// After closing the block, you must follow it with OPENMP_ONCE_CLOSE (either on the same line or the next, your choice)
//
// local_ptr: A pointer to an *already defined* thread local variable to use to prevent the having to read under
// critical section every time (once a thread sees "tried" as true, it doesn't need to take the lock anymore).
// Must be a variable declared made thread local using '#pragma omp threadprivate(local_name)
// If the passed in pointer was from a bool* variable itself, it *also* must be declared threadprivate.
// once_struct_ptr: the pointer to an openmp_once_t to use as the backing state.
// If it is threadprivate, then the block will happen at most once among all threads.
// If it is not threadprivate, then the block will be run once for every thread, but
// all of them will be guarded by the same critical section so only one thread can
// can have the block run at once (useful for APIs that require at most one thread call them)
// If you need it once for every thread and no critical block, then you aren't using OpenMP's
// tooling at all, and thus this API isn't appropriate for you.
// critical_section_name: the name of the OpenMP critical section. Do not surround with quotes.
#define OPENMP_ONCE_WITH_PTRS(local_ptr, once_struct_ptr, critical_section_name) \
_OPENMP_ONCE_IMPL(*local_ptr, *once_struct_ptr, *((volatile openmp_once_t *)once_struct_ptr), critical_section_name)
#define OPENMP_ONCE_CLOSE }
// @formatter:on
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* OPENMP_ONCE_H_ */