Mercurial > hg > sv-dependency-builds
comparison src/fftw-3.3.8/threads/threads.c @ 167:bd3cc4d1df30
Add FFTW 3.3.8 source, and a Linux build
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Tue, 19 Nov 2019 14:52:55 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
166:cbd6d7e562c7 | 167:bd3cc4d1df30 |
---|---|
1 /* | |
2 * Copyright (c) 2003, 2007-14 Matteo Frigo | |
3 * Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology | |
4 * | |
5 * This program is free software; you can redistribute it and/or modify | |
6 * it under the terms of the GNU General Public License as published by | |
7 * the Free Software Foundation; either version 2 of the License, or | |
8 * (at your option) any later version. | |
9 * | |
10 * This program is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 * GNU General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU General Public License | |
16 * along with this program; if not, write to the Free Software | |
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
18 * | |
19 */ | |
20 | |
21 /* threads.c: Portable thread spawning for loops, via the X(spawn_loop) | |
22 function. The first portion of this file is a set of macros to | |
23 spawn and join threads on various systems. */ | |
24 | |
25 #include "threads/threads.h" | |
26 #include "api/api.h" | |
27 | |
28 #if defined(USING_POSIX_THREADS) | |
29 | |
30 #include <pthread.h> | |
31 | |
32 #ifdef HAVE_UNISTD_H | |
33 # include <unistd.h> | |
34 #endif | |
35 | |
36 /* imlementation of semaphores and mutexes: */ | |
37 #if (defined(_POSIX_SEMAPHORES) && (_POSIX_SEMAPHORES >= 200112L)) | |
38 | |
39 /* If optional POSIX semaphores are supported, use them to | |
40 implement both semaphores and mutexes. */ | |
41 # include <semaphore.h> | |
42 # include <errno.h> | |
43 | |
44 typedef sem_t os_sem_t; | |
45 | |
46 static void os_sem_init(os_sem_t *s) { sem_init(s, 0, 0); } | |
47 static void os_sem_destroy(os_sem_t *s) { sem_destroy(s); } | |
48 | |
49 static void os_sem_down(os_sem_t *s) | |
50 { | |
51 int err; | |
52 do { | |
53 err = sem_wait(s); | |
54 } while (err == -1 && errno == EINTR); | |
55 CK(err == 0); | |
56 } | |
57 | |
58 static void os_sem_up(os_sem_t *s) { sem_post(s); } | |
59 | |
60 /* | |
61 The reason why we use sem_t to implement mutexes is that I have | |
62 seen mysterious hangs with glibc-2.7 and linux-2.6.22 when using | |
63 pthread_mutex_t, but no hangs with sem_t or with linux >= | |
64 2.6.24. For lack of better information, sem_t looks like the | |
65 safest choice. | |
66 */ | |
67 typedef sem_t os_mutex_t; | |
68 static void os_mutex_init(os_mutex_t *s) { sem_init(s, 0, 1); } | |
69 #define os_mutex_destroy os_sem_destroy | |
70 #define os_mutex_lock os_sem_down | |
71 #define os_mutex_unlock os_sem_up | |
72 | |
73 #else | |
74 | |
75 /* If optional POSIX semaphores are not defined, use pthread | |
76 mutexes for mutexes, and simulate semaphores with condition | |
77 variables */ | |
78 typedef pthread_mutex_t os_mutex_t; | |
79 | |
80 static void os_mutex_init(os_mutex_t *s) | |
81 { | |
82 pthread_mutex_init(s, (pthread_mutexattr_t *)0); | |
83 } | |
84 | |
85 static void os_mutex_destroy(os_mutex_t *s) { pthread_mutex_destroy(s); } | |
86 static void os_mutex_lock(os_mutex_t *s) { pthread_mutex_lock(s); } | |
87 static void os_mutex_unlock(os_mutex_t *s) { pthread_mutex_unlock(s); } | |
88 | |
89 typedef struct { | |
90 pthread_mutex_t m; | |
91 pthread_cond_t c; | |
92 volatile int x; | |
93 } os_sem_t; | |
94 | |
95 static void os_sem_init(os_sem_t *s) | |
96 { | |
97 pthread_mutex_init(&s->m, (pthread_mutexattr_t *)0); | |
98 pthread_cond_init(&s->c, (pthread_condattr_t *)0); | |
99 | |
100 /* wrap initialization in lock to exploit the release | |
101 semantics of pthread_mutex_unlock() */ | |
102 pthread_mutex_lock(&s->m); | |
103 s->x = 0; | |
104 pthread_mutex_unlock(&s->m); | |
105 } | |
106 | |
107 static void os_sem_destroy(os_sem_t *s) | |
108 { | |
109 pthread_mutex_destroy(&s->m); | |
110 pthread_cond_destroy(&s->c); | |
111 } | |
112 | |
113 static void os_sem_down(os_sem_t *s) | |
114 { | |
115 pthread_mutex_lock(&s->m); | |
116 while (s->x <= 0) | |
117 pthread_cond_wait(&s->c, &s->m); | |
118 --s->x; | |
119 pthread_mutex_unlock(&s->m); | |
120 } | |
121 | |
122 static void os_sem_up(os_sem_t *s) | |
123 { | |
124 pthread_mutex_lock(&s->m); | |
125 ++s->x; | |
126 pthread_cond_signal(&s->c); | |
127 pthread_mutex_unlock(&s->m); | |
128 } | |
129 | |
130 #endif | |
131 | |
132 #define FFTW_WORKER void * | |
133 | |
134 static void os_create_thread(FFTW_WORKER (*worker)(void *arg), | |
135 void *arg) | |
136 { | |
137 pthread_attr_t attr; | |
138 pthread_t tid; | |
139 | |
140 pthread_attr_init(&attr); | |
141 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); | |
142 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | |
143 | |
144 pthread_create(&tid, &attr, worker, (void *)arg); | |
145 pthread_attr_destroy(&attr); | |
146 } | |
147 | |
148 static void os_destroy_thread(void) | |
149 { | |
150 pthread_exit((void *)0); | |
151 } | |
152 | |
153 /* support for static mutexes */ | |
154 typedef pthread_mutex_t os_static_mutex_t; | |
155 #define OS_STATIC_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER | |
156 static void os_static_mutex_lock(os_static_mutex_t *s) { pthread_mutex_lock(s); } | |
157 static void os_static_mutex_unlock(os_static_mutex_t *s) { pthread_mutex_unlock(s); } | |
158 | |
159 #elif defined(__WIN32__) || defined(_WIN32) || defined(_WINDOWS) | |
160 /* hack: windef.h defines INT for its own purposes and this causes | |
161 a conflict with our own INT in ifftw.h. Divert the windows | |
162 definition into another name unlikely to cause a conflict */ | |
163 #define INT magnus_ab_INTegro_seclorum_nascitur_ordo | |
164 #include <windows.h> | |
165 #include <process.h> | |
166 #include <intrin.h> | |
167 #undef INT | |
168 | |
169 typedef HANDLE os_mutex_t; | |
170 | |
171 static void os_mutex_init(os_mutex_t *s) | |
172 { | |
173 *s = CreateMutex(NULL, FALSE, NULL); | |
174 } | |
175 | |
176 static void os_mutex_destroy(os_mutex_t *s) | |
177 { | |
178 CloseHandle(*s); | |
179 } | |
180 | |
181 static void os_mutex_lock(os_mutex_t *s) | |
182 { | |
183 WaitForSingleObject(*s, INFINITE); | |
184 } | |
185 | |
186 static void os_mutex_unlock(os_mutex_t *s) | |
187 { | |
188 ReleaseMutex(*s); | |
189 } | |
190 | |
191 typedef HANDLE os_sem_t; | |
192 | |
193 static void os_sem_init(os_sem_t *s) | |
194 { | |
195 *s = CreateSemaphore(NULL, 0, 0x7FFFFFFFL, NULL); | |
196 } | |
197 | |
198 static void os_sem_destroy(os_sem_t *s) | |
199 { | |
200 CloseHandle(*s); | |
201 } | |
202 | |
203 static void os_sem_down(os_sem_t *s) | |
204 { | |
205 WaitForSingleObject(*s, INFINITE); | |
206 } | |
207 | |
208 static void os_sem_up(os_sem_t *s) | |
209 { | |
210 ReleaseSemaphore(*s, 1, NULL); | |
211 } | |
212 | |
213 #define FFTW_WORKER unsigned __stdcall | |
214 typedef unsigned (__stdcall *winthread_start) (void *); | |
215 | |
216 static void os_create_thread(winthread_start worker, | |
217 void *arg) | |
218 { | |
219 _beginthreadex((void *)NULL, /* security attrib */ | |
220 0, /* stack size */ | |
221 worker, /* start address */ | |
222 arg, /* parameters */ | |
223 0, /* creation flags */ | |
224 (unsigned *)NULL); /* tid */ | |
225 } | |
226 | |
227 static void os_destroy_thread(void) | |
228 { | |
229 _endthreadex(0); | |
230 } | |
231 | |
232 /* windows does not have statically-initialized mutexes---fake a | |
233 spinlock */ | |
234 typedef volatile LONG os_static_mutex_t; | |
235 #define OS_STATIC_MUTEX_INITIALIZER 0 | |
236 static void os_static_mutex_lock(os_static_mutex_t *s) | |
237 { | |
238 while (InterlockedExchange(s, 1) == 1) { | |
239 YieldProcessor(); | |
240 Sleep(0); | |
241 } | |
242 } | |
243 static void os_static_mutex_unlock(os_static_mutex_t *s) | |
244 { | |
245 LONG old = InterlockedExchange(s, 0); | |
246 A(old == 1); | |
247 } | |
248 #else | |
249 #error "No threading layer defined" | |
250 #endif | |
251 | |
252 /************************************************************************/ | |
253 | |
254 /* Main code: */ | |
255 struct worker { | |
256 os_sem_t ready; | |
257 os_sem_t done; | |
258 struct work *w; | |
259 struct worker *cdr; | |
260 }; | |
261 | |
262 static struct worker *make_worker(void) | |
263 { | |
264 struct worker *q = (struct worker *)MALLOC(sizeof(*q), OTHER); | |
265 os_sem_init(&q->ready); | |
266 os_sem_init(&q->done); | |
267 return q; | |
268 } | |
269 | |
270 static void unmake_worker(struct worker *q) | |
271 { | |
272 os_sem_destroy(&q->done); | |
273 os_sem_destroy(&q->ready); | |
274 X(ifree)(q); | |
275 } | |
276 | |
277 struct work { | |
278 spawn_function proc; | |
279 spawn_data d; | |
280 struct worker *q; /* the worker responsible for performing this work */ | |
281 }; | |
282 | |
283 static os_mutex_t queue_lock; | |
284 static os_sem_t termination_semaphore; | |
285 | |
286 static struct worker *worker_queue; | |
287 #define WITH_QUEUE_LOCK(what) \ | |
288 { \ | |
289 os_mutex_lock(&queue_lock); \ | |
290 what; \ | |
291 os_mutex_unlock(&queue_lock); \ | |
292 } | |
293 | |
294 static FFTW_WORKER worker(void *arg) | |
295 { | |
296 struct worker *ego = (struct worker *)arg; | |
297 struct work *w; | |
298 | |
299 for (;;) { | |
300 /* wait until work becomes available */ | |
301 os_sem_down(&ego->ready); | |
302 | |
303 w = ego->w; | |
304 | |
305 /* !w->proc ==> terminate worker */ | |
306 if (!w->proc) break; | |
307 | |
308 /* do the work */ | |
309 w->proc(&w->d); | |
310 | |
311 /* signal that work is done */ | |
312 os_sem_up(&ego->done); | |
313 } | |
314 | |
315 /* termination protocol */ | |
316 os_sem_up(&termination_semaphore); | |
317 | |
318 os_destroy_thread(); | |
319 /* UNREACHABLE */ | |
320 return 0; | |
321 } | |
322 | |
323 static void enqueue(struct worker *q) | |
324 { | |
325 WITH_QUEUE_LOCK({ | |
326 q->cdr = worker_queue; | |
327 worker_queue = q; | |
328 }); | |
329 } | |
330 | |
331 static struct worker *dequeue(void) | |
332 { | |
333 struct worker *q; | |
334 | |
335 WITH_QUEUE_LOCK({ | |
336 q = worker_queue; | |
337 if (q) | |
338 worker_queue = q->cdr; | |
339 }); | |
340 | |
341 if (!q) { | |
342 /* no worker is available. Create one */ | |
343 q = make_worker(); | |
344 os_create_thread(worker, q); | |
345 } | |
346 | |
347 return q; | |
348 } | |
349 | |
350 | |
351 static void kill_workforce(void) | |
352 { | |
353 struct work w; | |
354 | |
355 w.proc = 0; | |
356 | |
357 WITH_QUEUE_LOCK({ | |
358 /* tell all workers that they must terminate. | |
359 | |
360 Because workers enqueue themselves before signaling the | |
361 completion of the work, all workers belong to the worker queue | |
362 if we get here. Also, all workers are waiting at | |
363 os_sem_down(ready), so we can hold the queue lock without | |
364 deadlocking */ | |
365 while (worker_queue) { | |
366 struct worker *q = worker_queue; | |
367 worker_queue = q->cdr; | |
368 q->w = &w; | |
369 os_sem_up(&q->ready); | |
370 os_sem_down(&termination_semaphore); | |
371 unmake_worker(q); | |
372 } | |
373 }); | |
374 } | |
375 | |
376 static os_static_mutex_t initialization_mutex = OS_STATIC_MUTEX_INITIALIZER; | |
377 | |
378 int X(ithreads_init)(void) | |
379 { | |
380 os_static_mutex_lock(&initialization_mutex); { | |
381 os_mutex_init(&queue_lock); | |
382 os_sem_init(&termination_semaphore); | |
383 | |
384 WITH_QUEUE_LOCK({ | |
385 worker_queue = 0; | |
386 }); | |
387 } os_static_mutex_unlock(&initialization_mutex); | |
388 | |
389 return 0; /* no error */ | |
390 } | |
391 | |
392 /* Distribute a loop from 0 to loopmax-1 over nthreads threads. | |
393 proc(d) is called to execute a block of iterations from d->min | |
394 to d->max-1. d->thr_num indicate the number of the thread | |
395 that is executing proc (from 0 to nthreads-1), and d->data is | |
396 the same as the data parameter passed to X(spawn_loop). | |
397 | |
398 This function returns only after all the threads have completed. */ | |
399 void X(spawn_loop)(int loopmax, int nthr, spawn_function proc, void *data) | |
400 { | |
401 int block_size; | |
402 struct work *r; | |
403 int i; | |
404 | |
405 A(loopmax >= 0); | |
406 A(nthr > 0); | |
407 A(proc); | |
408 | |
409 if (!loopmax) return; | |
410 | |
411 /* Choose the block size and number of threads in order to (1) | |
412 minimize the critical path and (2) use the fewest threads that | |
413 achieve the same critical path (to minimize overhead). | |
414 e.g. if loopmax is 5 and nthr is 4, we should use only 3 | |
415 threads with block sizes of 2, 2, and 1. */ | |
416 block_size = (loopmax + nthr - 1) / nthr; | |
417 nthr = (loopmax + block_size - 1) / block_size; | |
418 | |
419 STACK_MALLOC(struct work *, r, sizeof(struct work) * nthr); | |
420 | |
421 /* distribute work: */ | |
422 for (i = 0; i < nthr; ++i) { | |
423 struct work *w = &r[i]; | |
424 spawn_data *d = &w->d; | |
425 | |
426 d->max = (d->min = i * block_size) + block_size; | |
427 if (d->max > loopmax) | |
428 d->max = loopmax; | |
429 d->thr_num = i; | |
430 d->data = data; | |
431 w->proc = proc; | |
432 | |
433 if (i == nthr - 1) { | |
434 /* do the work ourselves */ | |
435 proc(d); | |
436 } else { | |
437 /* assign a worker to W */ | |
438 w->q = dequeue(); | |
439 | |
440 /* tell worker w->q to do it */ | |
441 w->q->w = w; /* Dirac could have written this */ | |
442 os_sem_up(&w->q->ready); | |
443 } | |
444 } | |
445 | |
446 for (i = 0; i < nthr - 1; ++i) { | |
447 struct work *w = &r[i]; | |
448 os_sem_down(&w->q->done); | |
449 enqueue(w->q); | |
450 } | |
451 | |
452 STACK_FREE(r); | |
453 } | |
454 | |
455 void X(threads_cleanup)(void) | |
456 { | |
457 kill_workforce(); | |
458 os_mutex_destroy(&queue_lock); | |
459 os_sem_destroy(&termination_semaphore); | |
460 } | |
461 | |
462 static os_static_mutex_t install_planner_hooks_mutex = OS_STATIC_MUTEX_INITIALIZER; | |
463 static os_mutex_t planner_mutex; | |
464 static int planner_hooks_installed = 0; | |
465 | |
466 static void lock_planner_mutex(void) | |
467 { | |
468 os_mutex_lock(&planner_mutex); | |
469 } | |
470 | |
471 static void unlock_planner_mutex(void) | |
472 { | |
473 os_mutex_unlock(&planner_mutex); | |
474 } | |
475 | |
476 void X(threads_register_planner_hooks)(void) | |
477 { | |
478 os_static_mutex_lock(&install_planner_hooks_mutex); { | |
479 if (!planner_hooks_installed) { | |
480 os_mutex_init(&planner_mutex); | |
481 X(set_planner_hooks)(lock_planner_mutex, unlock_planner_mutex); | |
482 planner_hooks_installed = 1; | |
483 } | |
484 } os_static_mutex_unlock(&install_planner_hooks_mutex); | |
485 } |