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 }