BRL-CAD
Loading...
Searching...
No Matches
parallel.h
Go to the documentation of this file.
1/* P A R A L L E L . H
2 * BRL-CAD
3 *
4 * Copyright (c) 2004-2026 United States Government as represented by
5 * the U.S. Army Research Laboratory.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * version 2.1 as published by the Free Software Foundation.
10 *
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this file; see the file named COPYING for more
18 * information.
19 */
20
21#ifndef BU_PARALLEL_H
22#define BU_PARALLEL_H
23
24#include "common.h"
25
26#include <setjmp.h> /* for bu_setjmp */
27
28#include "bu/defines.h"
29
31
32/** @addtogroup bu_parallel
33 * @brief
34 * Thread based parallelism routines.
35 */
36/** @{ */
37
38/**
39 * MAX_PSW - The maximum number of processors that can be expected on
40 * this hardware. Used to allocate application-specific per-processor
41 * tables at compile-time and represent a hard limit on the number of
42 * processors/threads that may be spawned. The actual number of
43 * available processors is found at runtime by calling bu_avail_cpus()
44 */
45#define MAX_PSW 4096 /* FIXME: just enough for 64 x 64 threads */
46
47/**
48 * @brief
49 * subroutine to determine if we are multi-threaded
50 *
51 * This subroutine is separated off from parallel.c so that bu_bomb()
52 * and others can call it, without causing either parallel.c or
53 * semaphore.c to get referenced and thus causing the loader to drag
54 * in all the parallel processing stuff from the vendor library.
55 *
56 */
57
58/**
59 * This routine is DEPRECATED, do not use it. If you need a means to
60 * determine when an application is running bu_parallel(), please
61 * report this to our developers.
62 *
63 * Previously, this was a library-stateful way for bu_bomb() to tell
64 * if a parallel application is running. This routine now simply
65 * returns zero all the time, which permits BU_SETJUMP() error
66 * handling during bu_bomb().
67 */
69
70/**
71 * Returns the CPU number of the current bu_parallel() invoked thread.
72 *
73 * This routine is intended for indexing into per-cpu memory buffers.
74 * Values will be any number in the range of 0 to MAX_PSW. They are
75 * not guaranteed to be in any range except for non-parallel
76 * applications which will have ID 0 for the main process's thread.
77 */
78BU_EXPORT extern int bu_parallel_id(void);
79
80/**
81 * Returns the OS thread ID of the calling thread.
82 *
83 * This routine is intended for diagnostic and unique ID purposes, not
84 * for indexing into per-cpu memory buffers as values can be anything.
85 */
86BU_EXPORT extern int bu_thread_id(void);
87
88
89/**
90 * @brief
91 * routines for parallel processing
92 *
93 * Machine-specific routines for portable parallel processing.
94 *
95 */
96
97/**
98 * Without knowing what the current UNIX "nice" value is, change to a
99 * new absolute "nice" value. (The system routine makes a relative
100 * change).
101 */
103
104/**
105 * Return the maximum number of physical CPUs that are considered to
106 * be available to this process now.
107 */
108BU_EXPORT extern size_t bu_avail_cpus(void);
109
110
111/**
112 * Create parallel threads of execution.
113 *
114 * This function creates (at most) 'ncpu' copies of function 'func'
115 * all running in parallel, passing 'data' to each invocation.
116 * Specifying ncpu=0 will specify automatic parallelization, invoking
117 * parallel threads as cores become available. This is particularly
118 * useful during recursive invocations where the ncpu core count is
119 * limited by the parent context.
120 *
121 * Locking and work dispatching are handled by 'func' using a
122 * "self-dispatching" paradigm. This means you must manually protect
123 * shared data structures, e.g., via bu_semaphore_acquire().
124 *
125 * Lock-free execution is typically possible by creating data
126 * containers with MAX_PSW elements as bu_parallel will never execute
127 * more than that many threads. Calling bu_parallel_id() provides the
128 * id of the current thread, which can be used as an index into a
129 * MAX_PSW per-cpu array. The id is also passed as the func_cpu_id
130 * parameter to the bu_parallel callback function.
131 *
132 * All invocations of the specified 'func' callback function are
133 * passed two parameters: 1) it's assigned thread number and 2) a
134 * shared 'data' pointer for application use. Threads are assigned
135 * increasing numbers, starting with zero. Processes may also call
136 * bu_parallel_id() to obtain their thread number.
137 *
138 * Threads created with bu_parallel() may specify utilization of
139 * affinity locking to keep threads on a given physical CPU core.
140 * This behavior can be enabled at runtime by setting the environment
141 * variable LIBBU_AFFINITY=1. Note that this option may increase or
142 * even decrease performance, particularly on platforms with advanced
143 * scheduling, so testing is recommended.
144 *
145 * This function will not return control until all invocations of the
146 * subroutine are finished.
147 *
148 * In following is a working stand-alone example demonstrating how to
149 * call the bu_parallel() interface.
150 *
151 * @code
152 * void shoot_cells_in_series(int width, int height) {
153 * int i, j;
154 * for (i=0; i<height; i++) {
155 * for (j=0; j<width; j++) {
156 * printf("Shooting cell (%d, %d) on CPU %d\n", i, j, bu_parallel_id());
157 * }
158 * }
159 * }
160 *
161 * void shoot_row_per_thread(int cpu, void *mydata) {
162 * int i, j, width;
163 * width = *(int *)mydata;
164 * for (i=0; i<width; i++) {
165 * printf("Shooting cell (%d, %d) on CPU %d\n", i, cpu, bu_parallel_id());
166 * }
167 * }
168 *
169 * void shoot_cells_in_parallel(int width, int height) {
170 * bu_parallel(shoot_row_per_thread, height, &width);
171 * // we don't reach here until all threads complete
172 * }
173 *
174 * int main(int ac, char *av[]) {
175 * int width = 4, height = 4;
176 * printf("\nShooting cells one at a time, 4x4 grid:\n");
177 * shoot_cells_in_series(width, height);
178 * printf("\nShooting cells in parallel with 4 threads, one per row:\n");
179 * shoot_cells_in_parallel(width, height);
180 * return 0;
181 * }
182 * @endcode
183 */
184BU_EXPORT extern void bu_parallel(void (*func)(int func_cpu_id, void *func_data), size_t ncpu, void *data);
185
186
187/**
188 * @brief
189 * semaphore implementation
190 *
191 * Machine-specific routines for parallel processing. Primarily for
192 * handling semaphores to protect critical sections of code.
193 *
194 * The new paradigm: semaphores are referred to, not by a pointer, but
195 * by a small integer. This module is now responsible for obtaining
196 * whatever storage is needed to implement each semaphore.
197 *
198 * Note that these routines can't use bu_log() for error logging,
199 * because logging itself uses semaphores.
200 */
201
202/**
203 * Process-wide semaphores needed by core library infrastructure have fixed
204 * IDs. They are valid during static initialization and do not depend on
205 * library constructor or registration order. IDs not listed here and below
206 * BU_SEM_DYNAMIC_BASE are reserved for future core use.
207 *
208 * Keep this list as the single source for both the numeric constants and the
209 * reserved name registry in libbu.
210 */
211#define BU_SEMAPHORE_RESERVED_LIST(_entry) \
212 _entry(1, REGISTRY, "SEM_LOCK") \
213 _entry(2, GENERAL, "BU_SEM_GENERAL") \
214 _entry(3, SYSCALL, "BU_SEM_SYSCALL") \
215 _entry(4, MAPPEDFILE, "BU_SEM_MAPPEDFILE") \
216 _entry(5, THREAD, "BU_SEM_THREAD") \
217 _entry(6, MALLOC, "BU_SEM_MALLOC") \
218 _entry(7, DATETIME, "BU_SEM_DATETIME") \
219 _entry(8, DIR, "BU_SEM_DIR") \
220 _entry(9, LOG_HOOK, "BU_SEM_LOG_HOOK") \
221 _entry(16, RT_WORKER, "RT_SEM_WORKER") \
222 _entry(17, RT_RESULTS, "RT_SEM_RESULTS") \
223 _entry(18, RT_MODEL, "RT_SEM_MODEL") \
224 _entry(19, RT_TREE0, "RT_SEM_TREE0") \
225 _entry(20, RT_TREE1, "RT_SEM_TREE1") \
226 _entry(21, RT_TREE2, "RT_SEM_TREE2") \
227 _entry(22, RT_TREE3, "RT_SEM_TREE3") \
228 _entry(23, RT_DB_USES, "LIBRT_SEM_USES")
229
232#define BU_SEMAPHORE_RESERVED_ENUM(_id, _symbol, _name) BU_SEM_ID_ ## _symbol = _id,
234#undef BU_SEMAPHORE_RESERVED_ENUM
237
238/**
239 * Return the process-wide semaphore ID associated with name. Registration is
240 * thread-safe and idempotent: repeated registrations of the same name return
241 * the same ID. The registry keeps its own copy of name, and mappings remain
242 * valid for the lifetime of the process, including across
243 * bu_semaphore_free().
244 *
245 * Reserved core names return their fixed IDs. Other names are assigned from
246 * BU_SEM_DYNAMIC_BASE upward.
247 *
248 * A name must be non-NULL and non-empty.
249 */
250BU_EXPORT extern int bu_semaphore_register(const char *name);
251
252
253/**
254 * Define or update an application semaphore variable using the dynamic
255 * process-wide name registry. The assignment to x is not synchronized;
256 * applications should use this during initialization before x is shared.
257 *
258 */
259#define BU_SEMAPHORE_DEFINE(x) x = bu_semaphore_register(CPP_STR(x))
260
261/**
262 * This semaphore is intended for short-lived protection.
263 *
264 * It is provided for both library and application use, code that
265 * doesn't call into a BRL-CAD library.
266 */
267BU_EXPORT extern int BU_SEM_GENERAL;
268
269/**
270 * This semaphore is intended to protect general system calls.
271 *
272 * It is provided for both library and application use, code that
273 * doesn't call into a BRL-CAD library.
274 */
275BU_EXPORT extern int BU_SEM_SYSCALL;
276
277/**
278 * FIXME: this one shouldn't need to be global.
279 */
281
282/*
283 * Automatic restart capability in bu_bomb(). The return from
284 * BU_SETJUMP is the return from the setjmp(). It is 0 on the first
285 * pass through, and non-zero when re-entered via a longjmp() from
286 * bu_bomb(). This is only safe to use in non-parallel applications.
287 */
288#define BU_SETJUMP setjmp((bu_setjmp_valid[bu_parallel_id()]=1, bu_jmpbuf[bu_parallel_id()]))
289#define BU_UNSETJUMP (bu_setjmp_valid[bu_parallel_id()]=0)
291/* These are global because BU_SETJUMP must be macro. Please don't touch. */
292BU_EXPORT extern int bu_setjmp_valid[MAX_PSW]; /* !0 = bu_jmpbuf is valid */
293BU_EXPORT extern jmp_buf bu_jmpbuf[MAX_PSW]; /* for BU_SETJUMP() */
294
295
296/**
297 * Prepare 'nsemaphores' independent critical section semaphores. Die
298 * on error.
299 *
300 * Takes the place of 'n' separate calls to old RES_INIT(). Start by
301 * allocating array of "struct bu_semaphores", which has been arranged
302 * to contain whatever this system needs.
303 *
304 */
305BU_EXPORT extern void bu_semaphore_init(unsigned int nsemaphores);
307/**
308 * Release all initialized native semaphore objects. Registered name-to-ID
309 * mappings are process-lifetime state and are retained, so existing handles
310 * remain valid if semaphores are subsequently used again.
311 *
312 * The caller must ensure no other thread is using a semaphore while this
313 * routine runs.
314 *
315 * FIXME: per hacking, rename to bu_semaphore_clear()
316 */
317BU_EXPORT extern void bu_semaphore_free(void);
318
319BU_EXPORT extern void bu_semaphore_acquire(unsigned int i);
320
321BU_EXPORT extern void bu_semaphore_release(unsigned int i);
322
323/** @} */
324
326
327#endif /* BU_PARALLEL_H */
328
329/*
330 * Local Variables:
331 * mode: C
332 * tab-width: 8
333 * indent-tabs-mode: t
334 * c-file-style: "stroustrup"
335 * End:
336 * ex: shiftwidth=4 tabstop=8
337 */
Definition dvec.h:74
Header file for the BRL-CAD common definitions.
void bu_semaphore_acquire(unsigned int i)
int bu_setjmp_valid[MAX_PSW]
int BU_SEM_SYSCALL
void bu_nice_set(int newnice)
routines for parallel processing
void bu_semaphore_free(void)
void bu_semaphore_init(unsigned int nsemaphores)
int bu_parallel_id(void)
DEPRECATED int bu_is_parallel(void)
subroutine to determine if we are multi-threaded
#define MAX_PSW
Definition parallel.h:45
int BU_SEM_GENERAL
int BU_SEM_MAPPEDFILE
size_t bu_avail_cpus(void)
jmp_buf bu_jmpbuf[MAX_PSW]
#define BU_SEMAPHORE_RESERVED_LIST(_entry)
semaphore implementation
Definition parallel.h:211
void bu_parallel(void(*func)(int func_cpu_id, void *func_data), size_t ncpu, void *data)
int bu_semaphore_register(const char *name)
bu_semaphore_reserved_id
Definition parallel.h:230
void bu_semaphore_release(unsigned int i)
int bu_thread_id(void)
@ BU_SEM_DYNAMIC_BASE
Definition parallel.h:235
@ BU_SEM_ID_NONE
Definition parallel.h:231
#define DEPRECATED
Definition common.h:433
#define BU_SEMAPHORE_RESERVED_ENUM(_id, _symbol, _name)
Definition parallel.h:232