1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9#ifndef _GDK_SYSTEM_H_
10#define _GDK_SYSTEM_H_
11
12#ifdef WIN32
13#ifndef LIBGDK
14#define gdk_export extern __declspec(dllimport)
15#else
16#define gdk_export extern __declspec(dllexport)
17#endif
18#else
19#define gdk_export extern
20#endif
21
22/* if __has_attribute is not known to the preprocessor, we ignore
23 * attributes completely; if it is known, use it to find out whether
24 * specific attributes that we use are known */
25#ifndef __has_attribute
26#ifndef __GNUC__
27#define __has_attribute(attr) 0
28#ifndef __attribute__
29#define __attribute__(attr) /* empty */
30#endif
31#else
32/* older GCC does have attributes, but not __has_attribute and not all
33 * attributes that we use are known */
34#define __has_attribute__alloc_size__ 1
35#define __has_attribute__cold__ 1
36#define __has_attribute__format__ 1
37#define __has_attribute__malloc__ 1
38#define __has_attribute__noreturn__ 1
39#define __has_attribute__returns_nonnull__ 0
40#define __has_attribute__visibility__ 1
41#define __has_attribute__warn_unused_result__ 1
42#define __has_attribute(attr) __has_attribute##attr
43#endif
44#endif
45#if !__has_attribute(__warn_unused_result__)
46#define __warn_unused_result__
47#endif
48#if !__has_attribute(__malloc__)
49#define __malloc__
50#endif
51#if !__has_attribute(__alloc_size__)
52#define __alloc_size__(a)
53#endif
54#if !__has_attribute(__format__)
55#define __format__(a,b,c)
56#endif
57#if !__has_attribute(__noreturn__)
58#define __noreturn__
59#endif
60/* these are used in some *private.h files */
61#if !__has_attribute(__visibility__)
62#define __visibility__(a)
63#elif defined(__CYGWIN__)
64#define __visibility__(a)
65#endif
66#if !__has_attribute(__cold__)
67#define __cold__
68#endif
69
70/* also see gdk.h for these */
71#define THRDMASK (1)
72#define THRDDEBUG if (GDKdebug & THRDMASK)
73#define TEMMASK (1<<10)
74#define TEMDEBUG if (GDKdebug & TEMMASK)
75
76/*
77 * @- pthreads Includes and Definitions
78 */
79#ifdef HAVE_PTHREAD_H
80/* don't re-include config.h; on Windows, don't redefine pid_t in an
81 * incompatible way */
82#undef HAVE_CONFIG_H
83#ifdef pid_t
84#undef pid_t
85#endif
86#include <sched.h>
87#include <pthread.h>
88#endif
89
90#ifdef HAVE_SEMAPHORE_H
91# include <semaphore.h>
92#endif
93
94#ifdef HAVE_DISPATCH_DISPATCH_H
95#include <dispatch/dispatch.h>
96#endif
97
98#ifdef HAVE_SYS_PARAM_H
99# include <sys/param.h> /* prerequisite of sys/sysctl on OpenBSD */
100#endif
101#ifdef BSD /* BSD macro is defined in sys/param.h */
102# include <sys/sysctl.h>
103#endif
104
105/* new pthread interface, where the thread id changed to a struct */
106#ifdef PTW32_VERSION
107#define PTW32 1
108#endif
109
110/* debug and errno integers */
111gdk_export int GDKdebug;
112gdk_export void GDKsetdebug(int debug);
113gdk_export int GDKverbose;
114gdk_export void GDKsetverbose(int verbosity);
115
116gdk_export int GDKnr_threads;
117
118/* API */
119
120/*
121 * @- sleep
122 */
123
124gdk_export void MT_sleep_ms(unsigned int ms);
125
126/*
127 * @- MT Thread Api
128 */
129typedef size_t MT_Id; /* thread number. will not be zero */
130
131enum MT_thr_detach { MT_THR_JOINABLE, MT_THR_DETACHED };
132
133gdk_export bool MT_thread_init(void);
134gdk_export int MT_create_thread(MT_Id *t, void (*function) (void *),
135 void *arg, enum MT_thr_detach d,
136 const char *threadname);
137gdk_export const char *MT_thread_getname(void);
138gdk_export void *MT_thread_getdata(void);
139gdk_export void MT_thread_setdata(void *data);
140gdk_export void MT_exiting_thread(void);
141gdk_export MT_Id MT_getpid(void);
142gdk_export int MT_join_thread(MT_Id t);
143
144#if SIZEOF_VOID_P == 4
145/* "limited" stack size on 32-bit systems */
146/* to avoid address space fragmentation */
147#define THREAD_STACK_SIZE ((size_t)1024*1024)
148#else
149/* "increased" stack size on 64-bit systems */
150/* since some compilers seem to require this */
151/* for burg-generated code in pathfinder */
152/* and address space fragmentation is no issue */
153#define THREAD_STACK_SIZE ((size_t)2*1024*1024)
154#endif
155
156
157/*
158 * @- MT Lock API
159 */
160#include "matomic.h"
161
162/* define this to keep lock statistics (can be expensive) */
163/* #define LOCK_STATS */
164
165/* define this if you want to use pthread (or Windows) locks instead
166 * of atomic instructions for locking (latching) */
167#ifndef WIN32
168/* on Linux (and in general pthread using systems) use native locks;
169 * on Windows use locks based on atomic instructions and sleeps since
170 * the Windows lock implementations (Mutex and CriticalSection) are
171 * too heavy and impossible to initialize statically */
172#define USE_NATIVE_LOCKS 1
173#endif
174
175#ifdef LOCK_STATS
176
177#define _DBG_LOCK_COUNT_0(l) \
178 do { \
179 (void) ATOMIC_INC(&GDKlockcnt); \
180 TEMDEBUG fprintf(stderr, "#%s: %s: locking %s...\n", \
181 MT_thread_getname(), __func__, (l)->name); \
182 } while (0)
183
184#define _DBG_LOCK_LOCKER(l) \
185 do { \
186 (l)->locker = __func__; \
187 (l)->thread = MT_thread_getname(); \
188 } while (0)
189
190#define _DBG_LOCK_UNLOCKER(l) \
191 do { \
192 (l)->locker = __func__; \
193 (l)->thread = NULL; \
194 TEMDEBUG fprintf(stderr, "#%s: %s: unlocking %s\n", \
195 MT_thread_getname(), __func__, (l)->name); \
196 } while (0)
197
198#define _DBG_LOCK_CONTENTION(l) \
199 do { \
200 TEMDEBUG fprintf(stderr, "#%s: %s: lock %s contention\n", \
201 MT_thread_getname(), __func__, (l)->name); \
202 (void) ATOMIC_INC(&GDKlockcontentioncnt); \
203 (void) ATOMIC_INC(&(l)->contention); \
204 } while (0)
205
206#define _DBG_LOCK_SLEEP(l) ((void) ATOMIC_INC(&(l)->sleep))
207
208#define _DBG_LOCK_COUNT_2(l) \
209 do { \
210 (l)->count++; \
211 if ((l)->next == (struct MT_Lock *) -1) { \
212 while (ATOMIC_TAS(&GDKlocklistlock) != 0) \
213 ; \
214 (l)->next = GDKlocklist; \
215 GDKlocklist = (l); \
216 ATOMIC_CLEAR(&GDKlocklistlock); \
217 } \
218 TEMDEBUG fprintf(stderr, "#%s: %s: locking %s complete\n", \
219 MT_thread_getname(), __func__, (l)->name); \
220 } while (0)
221
222#define _DBG_LOCK_INIT(l) \
223 do { \
224 (l)->count = 0; \
225 ATOMIC_INIT(&(l)->contention, 0); \
226 ATOMIC_INIT(&(l)->sleep, 0); \
227 (l)->locker = NULL; \
228 (l)->thread = NULL; \
229 /* if name starts with "sa_" don't link in GDKlocklist */ \
230 /* since the lock is in memory that is governed by the */ \
231 /* SQL storage allocator, and hence we have no control */ \
232 /* over when the lock is destroyed and the memory freed */ \
233 if (strncmp((l)->name, "sa_", 3) != 0) { \
234 MT_Lock * volatile _p; \
235 while (ATOMIC_TAS(&GDKlocklistlock) != 0) \
236 ; \
237 for (_p = GDKlocklist; _p; _p = _p->next) \
238 assert(_p != (l)); \
239 (l)->next = GDKlocklist; \
240 GDKlocklist = (l); \
241 ATOMIC_CLEAR(&GDKlocklistlock); \
242 } else { \
243 (l)->next = NULL; \
244 } \
245 } while (0)
246
247#define _DBG_LOCK_DESTROY(l) \
248 do { \
249 /* if name starts with "sa_" don't link in GDKlocklist */ \
250 /* since the lock is in memory that is governed by the */ \
251 /* SQL storage allocator, and hence we have no control */ \
252 /* over when the lock is destroyed and the memory freed */ \
253 if (strncmp((l)->name, "sa_", 3) != 0) { \
254 MT_Lock * volatile *_p; \
255 while (ATOMIC_TAS(&GDKlocklistlock) != 0) \
256 ; \
257 for (_p = &GDKlocklist; *_p; _p = &(*_p)->next) \
258 if ((l) == *_p) { \
259 *_p = (l)->next; \
260 break; \
261 } \
262 ATOMIC_CLEAR(&GDKlocklistlock); \
263 ATOMIC_DESTROY(&(l)->contention); \
264 ATOMIC_DESTROY(&(l)->sleep); \
265 } \
266 } while (0)
267
268#else
269
270#define _DBG_LOCK_COUNT_0(l) ((void) 0)
271#define _DBG_LOCK_CONTENTION(l) ((void) 0)
272#define _DBG_LOCK_SLEEP(l) ((void) 0)
273#define _DBG_LOCK_COUNT_2(l) ((void) 0)
274#define _DBG_LOCK_INIT(l) ((void) 0)
275#define _DBG_LOCK_DESTROY(l) ((void) 0)
276#define _DBG_LOCK_LOCKER(l) ((void) 0)
277#define _DBG_LOCK_UNLOCKER(l) ((void) 0)
278
279#endif
280
281#ifdef USE_NATIVE_LOCKS
282
283#if !defined(HAVE_PTHREAD_H) && defined(WIN32)
284typedef struct MT_Lock {
285 HANDLE lock;
286 char name[16];
287#ifdef LOCK_STATS
288 size_t count;
289 ATOMIC_TYPE contention;
290 ATOMIC_TYPE sleep;
291 struct MT_Lock * volatile next;
292 const char *locker;
293 const char *thread;
294#endif
295} MT_Lock;
296
297#ifdef LOCK_STATS
298#define MT_LOCK_INITIALIZER(n) { .lock = NULL, .name = n, .next = (struct MT_Lock *) -1, }
299#else
300#define MT_LOCK_INITIALIZER(n) { .lock = NULL, .name = n, }
301#endif
302
303#pragma intrinsic(_InterlockedCompareExchangePointer)
304
305#define MT_lock_init(l, n) \
306 do { \
307 assert((l)->lock == NULL); \
308 (l)->lock = CreateMutex(NULL, 0, NULL); \
309 strcpy_len((l)->name, (n), sizeof((l)->name)); \
310 _DBG_LOCK_INIT(l); \
311 } while (0)
312
313static bool inline
314MT_lock_try(MT_Lock *l)
315{
316 if (l->lock == NULL) {
317 HANDLE p = CreateMutex(NULL, 0, NULL);
318 if (_InterlockedCompareExchangePointer(
319 &l->lock, p, NULL) != NULL)
320 CloseHandle(p);
321 }
322 return WaitForSingleObject(l->lock, 0) == WAIT_OBJECT_0;
323}
324
325#define MT_lock_set(l) \
326 do { \
327 _DBG_LOCK_COUNT_0(l); \
328 if (!MT_lock_try(l)) { \
329 _DBG_LOCK_CONTENTION(l); \
330 MT_thread_setlockwait(l); \
331 do \
332 _DBG_LOCK_SLEEP(l); \
333 while (WaitForSingleObject( \
334 (l)->lock, INFINITE) != WAIT_OBJECT_0); \
335 MT_thread_setlockwait(NULL); \
336 } \
337 _DBG_LOCK_LOCKER(l); \
338 _DBG_LOCK_COUNT_2(l); \
339 } while (0)
340
341#define MT_lock_unset(l) \
342 do { \
343 assert((l)->lock); \
344 _DBG_LOCK_UNLOCKER(l); \
345 ReleaseMutex((l)->lock); \
346 } while (0)
347
348#define MT_lock_destroy(l) \
349 do { \
350 assert((l)->lock); \
351 _DBG_LOCK_DESTROY(l); \
352 CloseHandle((l)->lock); \
353 (l)->lock = NULL; \
354 } while (0)
355
356#else
357
358typedef struct MT_Lock {
359 pthread_mutex_t lock;
360 char name[16];
361#ifdef LOCK_STATS
362 size_t count;
363 ATOMIC_TYPE contention;
364 ATOMIC_TYPE sleep;
365 struct MT_Lock * volatile next;
366 const char *locker;
367 const char *thread;
368#endif
369} MT_Lock;
370
371#ifdef LOCK_STATS
372#define MT_LOCK_INITIALIZER(n) { .lock = PTHREAD_MUTEX_INITIALIZER, .name = n, .next = (struct MT_Lock *) -1, }
373#else
374#define MT_LOCK_INITIALIZER(n) { .lock = PTHREAD_MUTEX_INITIALIZER, .name = n, }
375#endif
376
377#define MT_lock_init(l, n) \
378 do { \
379 pthread_mutex_init(&(l)->lock, 0); \
380 strcpy_len((l)->name, (n), sizeof((l)->name)); \
381 _DBG_LOCK_INIT(l); \
382 } while (0)
383
384#define MT_lock_try(l) (pthread_mutex_trylock(&(l)->lock) == 0)
385
386#ifdef LOCK_STATS
387#define MT_lock_set(l) \
388 do { \
389 _DBG_LOCK_COUNT_0(l); \
390 if (!MT_lock_try(l)) { \
391 _DBG_LOCK_CONTENTION(l); \
392 MT_thread_setlockwait(l); \
393 do \
394 _DBG_LOCK_SLEEP(l); \
395 while (pthread_mutex_lock(&(l)->lock) != 0); \
396 MT_thread_setlockwait(NULL); \
397 } \
398 _DBG_LOCK_LOCKER(l); \
399 _DBG_LOCK_COUNT_2(l); \
400 } while (0)
401#else
402#define MT_lock_set(l) pthread_mutex_lock(&(l)->lock)
403#endif
404
405#define MT_lock_unset(l) \
406 do { \
407 _DBG_LOCK_UNLOCKER(l); \
408 pthread_mutex_unlock(&(l)->lock); \
409 } while (0)
410
411#define MT_lock_destroy(l) \
412 do { \
413 _DBG_LOCK_DESTROY(l); \
414 pthread_mutex_destroy(&(l)->lock); \
415 } while (0)
416
417#endif
418
419#else
420
421/* if LOCK_STATS is set, we maintain a bunch of counters and maintain
422 * a linked list of active locks */
423typedef struct MT_Lock {
424 ATOMIC_FLAG lock;
425 char name[16];
426#ifdef LOCK_STATS
427 size_t count;
428 ATOMIC_TYPE contention;
429 ATOMIC_TYPE sleep;
430 struct MT_Lock * volatile next;
431 const char *locker;
432 const char *thread;
433#endif
434} MT_Lock;
435
436#ifdef LOCK_STATS
437#define MT_LOCK_INITIALIZER(n) { .next = (struct MT_Lock *) -1, .name = n, }
438#else
439#define MT_LOCK_INITIALIZER(n) { .name = n, }
440#endif
441
442#define MT_lock_try(l) (ATOMIC_TAS(&(l)->lock) == 0)
443
444#define MT_lock_set(l) \
445 do { \
446 _DBG_LOCK_COUNT_0(l); \
447 if (!MT_lock_try(l)) { \
448 /* we didn't get the lock */ \
449 unsigned _spincnt = 0; \
450 _DBG_LOCK_CONTENTION(l); \
451 MT_thread_setlockwait(l); \
452 do { \
453 if ((++_spincnt & 2047) == 0) { \
454 _DBG_LOCK_SLEEP(l); \
455 MT_sleep_ms(1); \
456 } \
457 } while (!MT_lock_try(l)); \
458 MT_thread_setlockwait(NULL); \
459 } \
460 _DBG_LOCK_LOCKER(l); \
461 _DBG_LOCK_COUNT_2(l); \
462 } while (0)
463
464#define MT_lock_init(l, n) \
465 do { \
466 size_t nlen; \
467 ATOMIC_CLEAR(&(l)->lock); \
468 nlen = strlen(n); \
469 if (nlen >= sizeof((l)->name)) \
470 nlen = sizeof((l)->name) - 1; \
471 memcpy((l)->name, (n), nlen + 1); \
472 (l)->name[sizeof((l)->name) - 1] = 0; \
473 _DBG_LOCK_INIT(l); \
474 } while (0)
475
476#define MT_lock_unset(l) \
477 do { \
478 /* lock should be locked */ \
479 assert(ATOMIC_TAS(&(l)->lock) != 0); \
480 _DBG_LOCK_UNLOCKER(l); \
481 ATOMIC_CLEAR(&(l)->lock); \
482 } while (0)
483
484#define MT_lock_destroy(l) _DBG_LOCK_DESTROY(l)
485
486#endif
487
488#ifdef LOCK_STATS
489gdk_export void GDKlockstatistics(int);
490gdk_export MT_Lock * volatile GDKlocklist;
491gdk_export ATOMIC_FLAG GDKlocklistlock;
492gdk_export ATOMIC_TYPE GDKlockcnt;
493gdk_export ATOMIC_TYPE GDKlockcontentioncnt;
494gdk_export ATOMIC_TYPE GDKlocksleepcnt;
495#endif
496
497/*
498 * @- MT Semaphore API
499 */
500#if !defined(HAVE_PTHREAD_H) && defined(WIN32)
501
502typedef struct {
503 HANDLE sema;
504 char name[16];
505} MT_Sema;
506
507#define MT_sema_init(s, nr, n) \
508 do { \
509 assert((s)->sema == NULL); \
510 strcpy_len((s)->name, (n), sizeof((s)->name)); \
511 (s)->sema = CreateSemaphore(NULL, nr, 0x7fffffff, NULL); \
512 } while (0)
513
514#define MT_sema_destroy(s) \
515 do { \
516 assert((s)->sema != NULL); \
517 CloseHandle((s)->sema); \
518 (s)->sema = NULL; \
519 } while (0)
520
521#define MT_sema_up(s) ReleaseSemaphore((s)->sema, 1, NULL)
522
523#define MT_sema_down(s) \
524 do { \
525 TEMDEBUG fprintf(stderr, "#%s: %s: sema %s down...\n", \
526 MT_thread_getname(), __func__, (s)->name); \
527 if (WaitForSingleObject((s)->sema, 0) != WAIT_OBJECT_0) { \
528 MT_thread_setsemawait(s); \
529 while (WaitForSingleObject((s)->sema, INFINITE) != WAIT_OBJECT_0) \
530 ; \
531 MT_thread_setsemawait(NULL); \
532 } \
533 TEMDEBUG fprintf(stderr, "#%s: %s: sema %s down complete\n", \
534 MT_thread_getname(), __func__, (s)->name); \
535 } while (0)
536
537#elif defined(HAVE_DISPATCH_SEMAPHORE_CREATE)
538
539/* MacOS X */
540typedef struct {
541 dispatch_semaphore_t sema;
542 char name[16];
543} MT_Sema;
544
545#define MT_sema_init(s, nr, n) \
546 do { \
547 strcpy_len((s)->name, (n), sizeof((s)->name)); \
548 (s)->sema = dispatch_semaphore_create((long) (nr)); \
549 } while (0)
550
551#define MT_sema_destroy(s) dispatch_release((s)->sema)
552#define MT_sema_up(s) dispatch_semaphore_signal((s)->sema)
553#define MT_sema_down(s) dispatch_semaphore_wait((s)->sema, DISPATCH_TIME_FOREVER)
554
555#elif defined(_AIX) || defined(__MACH__)
556
557/* simulate semaphores using mutex and condition variable */
558
559typedef struct {
560 int cnt;
561 pthread_mutex_t mutex;
562 pthread_cond_t cond;
563 char name[16];
564} MT_Sema;
565
566#define MT_sema_init(s, nr, n) \
567 do { \
568 strcpy_len((s)->name, (n), sizeof((s)->name)); \
569 (s)->cnt = (nr); \
570 pthread_mutex_init(&(s)->mutex, 0); \
571 pthread_cond_init(&(s)->cond, 0); \
572 } while (0)
573
574#define MT_sema_destroy(s) \
575 do { \
576 pthread_mutex_destroy(&(s)->mutex); \
577 pthread_cond_destroy(&(s)->cond); \
578 } while (0)
579
580#define MT_sema_up(s) \
581 do { \
582 pthread_mutex_lock(&(s)->mutex); \
583 if ((s)->cnt++ < 0) { \
584 pthread_cond_signal(&(s)->cond); \
585 } \
586 pthread_mutex_unlock(&(s)->mutex); \
587 } while (0)
588
589#define MT_sema_down(s) \
590 do { \
591 TEMDEBUG fprintf(stderr, "#%s: %s: sema %s down...\n", \
592 MT_thread_getname(), __func__, (s)->name); \
593 pthread_mutex_lock(&(s)->mutex); \
594 if (--(s)->cnt < 0) { \
595 MT_thread_setsemawait(s); \
596 do { \
597 pthread_cond_wait(&(s)->cond, \
598 &(s)->mutex); \
599 } while ((s)->cnt < 0); \
600 MT_thread_setsemawait(NULL); \
601 pthread_mutex_unlock(&(s)->mutex); \
602 } \
603 TEMDEBUG fprintf(stderr, "#%s: %s: sema %s down complete\n", \
604 MT_thread_getname(), __func__, (s)->name); \
605 } while (0)
606
607#else
608
609typedef struct {
610 sem_t sema;
611 char name[16];
612} MT_Sema;
613
614#define MT_sema_init(s, nr, n) \
615 do { \
616 strcpy_len((s)->name, (n), sizeof((s)->name)); \
617 sem_init(&(s)->sema, 0, nr); \
618 } while (0)
619
620#define MT_sema_destroy(s) sem_destroy(&(s)->sema)
621
622#define MT_sema_up(s) \
623 do { \
624 TEMDEBUG fprintf(stderr, "#%s: %s: sema %s up\n", \
625 MT_thread_getname(), __func__, (s)->name); \
626 sem_post(&(s)->sema); \
627 } while (0)
628
629#define MT_sema_down(s) \
630 do { \
631 TEMDEBUG fprintf(stderr, "#%s: %s: sema %s down...\n", \
632 MT_thread_getname(), __func__, (s)->name); \
633 if (sem_trywait(&(s)->sema) != 0) { \
634 MT_thread_setsemawait(s); \
635 while (sem_wait(&(s)->sema) != 0) \
636 ; \
637 MT_thread_setsemawait(NULL); \
638 } \
639 TEMDEBUG fprintf(stderr, "#%s: %s: sema %s down complete\n", \
640 MT_thread_getname(), __func__, (s)->name); \
641 } while (0)
642
643#endif
644
645gdk_export void MT_thread_setlockwait(MT_Lock *lock);
646gdk_export void MT_thread_setsemawait(MT_Sema *sema);
647gdk_export void MT_thread_setworking(const char *work);
648
649gdk_export int MT_check_nr_cores(void);
650
651#endif /*_GDK_SYSTEM_H_*/
652