BRL-CAD
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-2024 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 
30 __BEGIN_DECLS
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 1024
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  */
68 DEPRECATED BU_EXPORT extern int bu_is_parallel(void);
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  */
78 BU_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  */
86 BU_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  */
102 BU_EXPORT extern void bu_nice_set(int newnice);
103 
104 /**
105  * Return the maximum number of physical CPUs that are considered to
106  * be available to this process now.
107  */
108 BU_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  */
184 BU_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 bu_log() acquires semaphore #0 (BU_SEM_SYSCALL).
200  */
201 
202 /**
203  *
204  */
205 BU_EXPORT extern int bu_semaphore_register(const char *name);
206 
207 
208 /**
209  * emaphores available for both library and application
210  * use.
211  *
212  */
213 #define BU_SEMAPHORE_DEFINE(x) x = bu_semaphore_register(CPP_STR(x))
214 
215 /**
216  * This semaphore is intended for short-lived protection.
217  *
218  * It is provided for both library and application use, code that
219  * doesn't call into a BRL-CAD library.
220  */
221 BU_EXPORT extern int BU_SEM_GENERAL;
222 
223 /**
224  * This semaphore is intended to protect general system calls.
225  *
226  * It is provided for both library and application use, code that
227  * doesn't call into a BRL-CAD library.
228  */
229 BU_EXPORT extern int BU_SEM_SYSCALL;
230 
231 /**
232  * FIXME: this one shouldn't need to be global.
233  */
234 BU_EXPORT extern int BU_SEM_MAPPEDFILE;
235 
236 
237 /*
238  * Automatic restart capability in bu_bomb(). The return from
239  * BU_SETJUMP is the return from the setjmp(). It is 0 on the first
240  * pass through, and non-zero when re-entered via a longjmp() from
241  * bu_bomb(). This is only safe to use in non-parallel applications.
242  */
243 #define BU_SETJUMP setjmp((bu_setjmp_valid[bu_parallel_id()]=1, bu_jmpbuf[bu_parallel_id()]))
244 #define BU_UNSETJUMP (bu_setjmp_valid[bu_parallel_id()]=0)
245 
246 /* These are global because BU_SETJUMP must be macro. Please don't touch. */
247 BU_EXPORT extern int bu_setjmp_valid[MAX_PSW]; /* !0 = bu_jmpbuf is valid */
248 BU_EXPORT extern jmp_buf bu_jmpbuf[MAX_PSW]; /* for BU_SETJUMP() */
249 
250 
251 /**
252  * Prepare 'nsemaphores' independent critical section semaphores. Die
253  * on error.
254  *
255  * Takes the place of 'n' separate calls to old RES_INIT(). Start by
256  * allocating array of "struct bu_semaphores", which has been arranged
257  * to contain whatever this system needs.
258  *
259  */
260 BU_EXPORT extern void bu_semaphore_init(unsigned int nsemaphores);
261 
262 /**
263  * Release all initialized semaphores and any associated memory.
264  *
265  * FIXME: per hacking, rename to bu_semaphore_clear()
266  */
267 BU_EXPORT extern void bu_semaphore_free(void);
268 
269 BU_EXPORT extern void bu_semaphore_acquire(unsigned int i);
270 
271 BU_EXPORT extern void bu_semaphore_release(unsigned int i);
272 
273 /** @} */
274 
275 __END_DECLS
276 
277 #endif /* BU_PARALLEL_H */
278 
279 /*
280  * Local Variables:
281  * mode: C
282  * tab-width: 8
283  * indent-tabs-mode: t
284  * c-file-style: "stroustrup"
285  * End:
286  * ex: shiftwidth=4 tabstop=8
287  */
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]
void bu_parallel(void(*func)(int func_cpu_id, void *func_data), size_t ncpu, void *data)
int bu_semaphore_register(const char *name)
semaphore implementation
void bu_semaphore_release(unsigned int i)
int bu_thread_id(void)
#define DEPRECATED
Definition: common.h:401