LCOV - code coverage report
Current view: top level - gdk - gdk_system.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 155 209 74.2 %
Date: 2021-09-14 19:48:19 Functions: 20 24 83.3 %

          Line data    Source code
       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 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : /*
      10             :  * @a Niels Nes, Peter Boncz
      11             :  * @+ Threads
      12             :  * This file contains a wrapper layer for threading, hence the
      13             :  * underscore convention MT_x (Multi-Threading).  As all platforms
      14             :  * that MonetDB runs on now support POSIX Threads (pthreads), this
      15             :  * wrapping layer has become rather thin.
      16             :  *
      17             :  * In the late 1990s when multi-threading support was introduced in
      18             :  * MonetDB, pthreads was just emerging as a standard API and not
      19             :  * widely adopted yet.  The earliest MT implementation focused on SGI
      20             :  * Unix and provided multi- threading using multiple processses, and
      21             :  * shared memory.
      22             :  *
      23             :  * One of the relics of this model, namely the need to pre-allocate
      24             :  * locks and semaphores, and consequently a maximum number of them,
      25             :  * has been removed in the latest iteration of this layer.
      26             :  *
      27             :  */
      28             : /*
      29             :  * @- Mthreads Routine implementations
      30             :  */
      31             : #include "monetdb_config.h"
      32             : #include "mstring.h"
      33             : #include "gdk.h"
      34             : #include "gdk_system.h"
      35             : #include "gdk_system_private.h"
      36             : 
      37             : #include <time.h>
      38             : 
      39             : #ifdef HAVE_FTIME
      40             : #include <sys/timeb.h>            /* ftime */
      41             : #endif
      42             : #ifdef HAVE_SYS_TIME_H
      43             : #include <sys/time.h>             /* gettimeofday */
      44             : #endif
      45             : 
      46             : #include <signal.h>
      47             : #include <string.h>               /* for strerror */
      48             : #include <unistd.h>               /* for sysconf symbols */
      49             : 
      50             : #ifdef LOCK_STATS
      51             : 
      52             : ATOMIC_TYPE GDKlockcnt = ATOMIC_VAR_INIT(0);
      53             : ATOMIC_TYPE GDKlockcontentioncnt = ATOMIC_VAR_INIT(0);
      54             : ATOMIC_TYPE GDKlocksleepcnt = ATOMIC_VAR_INIT(0);
      55             : MT_Lock *volatile GDKlocklist = 0;
      56             : ATOMIC_FLAG GDKlocklistlock = ATOMIC_FLAG_INIT;
      57             : 
      58             : /* merge sort of linked list */
      59             : static MT_Lock *
      60             : sortlocklist(MT_Lock *l)
      61             : {
      62             :         MT_Lock *r, *t, *ll = NULL;
      63             : 
      64             :         if (l == NULL || l->next == NULL) {
      65             :                 /* list is trivially sorted (0 or 1 element) */
      66             :                 return l;
      67             :         }
      68             :         /* break list into two (almost) equal pieces:
      69             :         * l is start of "left" list, r of "right" list, ll last
      70             :         * element of "left" list */
      71             :         for (t = r = l; t && t->next; t = t->next->next) {
      72             :                 ll = r;
      73             :                 r = r->next;
      74             :         }
      75             :         ll->next = NULL;     /* break list into two */
      76             :         r->prev = NULL;
      77             :         /* recursively sort both sublists */
      78             :         l = sortlocklist(l);
      79             :         r = sortlocklist(r);
      80             :         /* merge
      81             :          * t is new list, ll is last element of new list, l and r are
      82             :          * start of unprocessed part of left and right lists */
      83             :         t = ll = NULL;
      84             :         while (l && r) {
      85             :                 if (ATOMIC_GET(&l->sleep) < ATOMIC_GET(&r->sleep) ||
      86             :                     (ATOMIC_GET(&l->sleep) == ATOMIC_GET(&r->sleep) &&
      87             :                      (ATOMIC_GET(&l->contention) < ATOMIC_GET(&r->contention) ||
      88             :                       (ATOMIC_GET(&l->contention) == ATOMIC_GET(&r->contention) &&
      89             :                        l->count <= r->count)))) {
      90             :                         /* l is smaller */
      91             :                         if (ll == NULL) {
      92             :                                 assert(t == NULL);
      93             :                                 t = ll = l;
      94             :                         } else {
      95             :                                 ll->next = l;
      96             :                                 l->prev = ll;
      97             :                                 ll = ll->next;
      98             :                         }
      99             :                         l = l->next;
     100             :                 } else {
     101             :                         /* r is smaller */
     102             :                         if (ll == NULL) {
     103             :                                 assert(t == NULL);
     104             :                                 t = ll = r;
     105             :                         } else {
     106             :                                 ll->next = r;
     107             :                                 r->prev = ll;
     108             :                                 ll = ll->next;
     109             :                         }
     110             :                         r = r->next;
     111             :                 }
     112             :         }
     113             :         /* append rest of remaining list */
     114             :         if (l) {
     115             :                 ll->next = l;
     116             :                 l->prev = ll;
     117             :         } else {
     118             :                 ll->next = r;
     119             :                 r->prev = ll;
     120             :         }
     121             :         return t;
     122             : }
     123             : 
     124             : static inline bool
     125             : lock_isset(MT_Lock *l)
     126             : {
     127             :         if (MT_lock_try(l)) {
     128             :                 MT_lock_unset(l);
     129             :                 return false;
     130             :         }
     131             :         return true;
     132             : }
     133             : 
     134             : /* function used for debugging */
     135             : void
     136             : GDKlockstatistics(int what)
     137             : {
     138             :         MT_Lock *l;
     139             :         int n = 0;
     140             : 
     141             :         if (ATOMIC_TAS(&GDKlocklistlock) != 0) {
     142             :                 fprintf(stderr, "GDKlocklistlock is set, so cannot access lock list\n");
     143             :                 return;
     144             :         }
     145             :         if (what == -1) {
     146             :                 for (l = GDKlocklist; l; l = l->next) {
     147             :                         l->count = 0;
     148             :                         ATOMIC_SET(&l->contention, 0);
     149             :                         ATOMIC_SET(&l->sleep, 0);
     150             :                 }
     151             :                 ATOMIC_CLEAR(&GDKlocklistlock);
     152             :                 return;
     153             :         }
     154             :         GDKlocklist = sortlocklist(GDKlocklist);
     155             :         fprintf(stderr, "%-18s\t%s\t%s\t%s\t%s\t%s\t%s\n",
     156             :                 "lock name", "count", "content", "sleep",
     157             :                 "locked", "locker", "thread");
     158             :         for (l = GDKlocklist; l; l = l->next) {
     159             :                 n++;
     160             :                 if (what == 0 ||
     161             :                     (what == 1 && l->count) ||
     162             :                     (what == 2 && ATOMIC_GET(&l->contention)) ||
     163             :                     (what == 3 && lock_isset(l)))
     164             :                         fprintf(stderr, "%-18s\t%zu\t%zu\t%zu\t%s\t%s\t%s\n",
     165             :                                 l->name, l->count,
     166             :                                 (size_t) ATOMIC_GET(&l->contention),
     167             :                                 (size_t) ATOMIC_GET(&l->sleep),
     168             :                                 lock_isset(l) ? "locked" : "",
     169             :                                 l->locker ? l->locker : "",
     170             :                                 l->thread ? l->thread : "");
     171             :         }
     172             :         fprintf(stderr, "Number of locks: %d\n", n);
     173             :         fprintf(stderr, "Total lock count: %zu\n", (size_t) ATOMIC_GET(&GDKlockcnt));
     174             :         fprintf(stderr, "Lock contention:  %zu\n", (size_t) ATOMIC_GET(&GDKlockcontentioncnt));
     175             :         fprintf(stderr, "Lock sleep count: %zu\n", (size_t) ATOMIC_GET(&GDKlocksleepcnt));
     176             :         ATOMIC_CLEAR(&GDKlocklistlock);
     177             : }
     178             : 
     179             : #endif  /* LOCK_STATS */
     180             : 
     181             : #if !defined(HAVE_PTHREAD_H) && defined(WIN32)
     182             : static struct winthread {
     183             :         struct winthread *next;
     184             :         HANDLE hdl;
     185             :         DWORD tid;
     186             :         void (*func) (void *);
     187             :         void *data;
     188             :         MT_Lock *lockwait;      /* lock we're waiting for */
     189             :         MT_Sema *semawait;      /* semaphore we're waiting for */
     190             :         struct winthread *joinwait; /* process we are joining with */
     191             :         const char *working;    /* what we're currently doing */
     192             :         char algorithm[512];    /* the algorithm used in the last operation */
     193             :         size_t algolen;         /* length of string in .algorithm */
     194             :         ATOMIC_TYPE exited;
     195             :         bool detached:1, waiting:1;
     196             :         char threadname[MT_NAME_LEN];
     197             : } *winthreads = NULL;
     198             : static struct winthread mainthread = {
     199             :         .threadname = "main thread",
     200             :         .exited = ATOMIC_VAR_INIT(0),
     201             : };
     202             : 
     203             : static CRITICAL_SECTION winthread_cs;
     204             : static DWORD threadslot = TLS_OUT_OF_INDEXES;
     205             : 
     206             : void
     207             : dump_threads(void)
     208             : {
     209             :         TRC_DEBUG_IF(THRD) {
     210             :                 EnterCriticalSection(&winthread_cs);
     211             :                 for (struct winthread *w = winthreads; w; w = w->next) {
     212             :                         TRC_DEBUG_ENDIF(THRD, "%s, waiting for %s, working on %.200s\n",
     213             :                                         w->threadname,
     214             :                                         w->lockwait ? w->lockwait->name :
     215             :                                         w->semawait ? w->semawait->name :
     216             :                                         w->joinwait ? w->joinwait->threadname :
     217             :                                         "nothing",
     218             :                                         ATOMIC_GET(&w->exited) ? "exiting" :
     219             :                                         w->working ? w->working : "nothing");
     220             :                 }
     221             :                 LeaveCriticalSection(&winthread_cs);
     222             :         }
     223             : }
     224             : 
     225             : bool
     226             : MT_thread_init(void)
     227             : {
     228             :         if (threadslot == TLS_OUT_OF_INDEXES) {
     229             :                 threadslot = TlsAlloc();
     230             :                 if (threadslot == TLS_OUT_OF_INDEXES) {
     231             :                         GDKwinerror("Creating thread-local slot for thread failed");
     232             :                         return false;
     233             :                 }
     234             :                 mainthread.tid = GetCurrentThreadId();
     235             :                 if (TlsSetValue(threadslot, &mainthread) == 0) {
     236             :                         GDKwinerror("Setting thread-local value failed");
     237             :                         TlsFree(threadslot);
     238             :                         threadslot = TLS_OUT_OF_INDEXES;
     239             :                         return false;
     240             :                 }
     241             :                 InitializeCriticalSection(&winthread_cs);
     242             :         }
     243             :         return true;
     244             : }
     245             : 
     246             : static struct winthread *
     247             : find_winthread(DWORD tid)
     248             : {
     249             :         struct winthread *w;
     250             : 
     251             :         EnterCriticalSection(&winthread_cs);
     252             :         for (w = winthreads; w && w->tid != tid; w = w->next)
     253             :                 ;
     254             :         LeaveCriticalSection(&winthread_cs);
     255             :         return w;
     256             : }
     257             : 
     258             : const char *
     259             : MT_thread_getname(void)
     260             : {
     261             :         if (threadslot == TLS_OUT_OF_INDEXES)
     262             :                 return mainthread.threadname;
     263             :         struct winthread *w = TlsGetValue(threadslot);
     264             :         return w ? w->threadname : UNKNOWN_THREAD;
     265             : }
     266             : 
     267             : void
     268             : MT_thread_setdata(void *data)
     269             : {
     270             :         if (threadslot == TLS_OUT_OF_INDEXES)
     271             :                 return;
     272             :         struct winthread *w = TlsGetValue(threadslot);
     273             : 
     274             :         if (w)
     275             :                 w->data = data;
     276             : }
     277             : 
     278             : void
     279             : MT_thread_setlockwait(MT_Lock *lock)
     280             : {
     281             :         if (threadslot == TLS_OUT_OF_INDEXES)
     282             :                 return;
     283             :         struct winthread *w = TlsGetValue(threadslot);
     284             : 
     285             :         if (w)
     286             :                 w->lockwait = lock;
     287             : }
     288             : 
     289             : void
     290             : MT_thread_setsemawait(MT_Sema *sema)
     291             : {
     292             :         if (threadslot == TLS_OUT_OF_INDEXES)
     293             :                 return;
     294             :         struct winthread *w = TlsGetValue(threadslot);
     295             : 
     296             :         if (w)
     297             :                 w->semawait = sema;
     298             : }
     299             : 
     300             : void
     301             : MT_thread_setworking(const char *work)
     302             : {
     303             :         if (threadslot == TLS_OUT_OF_INDEXES)
     304             :                 return;
     305             :         struct winthread *w = TlsGetValue(threadslot);
     306             : 
     307             :         if (w)
     308             :                 w->working = work;
     309             : }
     310             : 
     311             : void
     312             : MT_thread_setalgorithm(const char *algo)
     313             : {
     314             :         if (threadslot == TLS_OUT_OF_INDEXES)
     315             :                 return;
     316             :         struct winthread *w = TlsGetValue(threadslot);
     317             : 
     318             :         if (w) {
     319             :                 if (algo) {
     320             :                         if (w->algolen > 0) {
     321             :                                 if (w->algolen < sizeof(w->algorithm))
     322             :                                         w->algolen += strconcat_len(w->algorithm + w->algolen, sizeof(w->algorithm) - w->algolen, "; ", algo, NULL);
     323             :                         } else
     324             :                                 w->algolen = strcpy_len(w->algorithm, algo, sizeof(w->algorithm));
     325             :                 } else {
     326             :                         w->algorithm[0] = 0;
     327             :                         w->algolen = 0;
     328             :                 }
     329             :         }
     330             : }
     331             : 
     332             : const char *
     333             : MT_thread_getalgorithm(void)
     334             : {
     335             :         if (threadslot == TLS_OUT_OF_INDEXES)
     336             :                 return NULL;
     337             :         struct winthread *w = TlsGetValue(threadslot);
     338             : 
     339             :         return w && w->algorithm[0] ? w->algorithm : NULL;
     340             : }
     341             : 
     342             : bool
     343             : MT_thread_override_limits(void)
     344             : {
     345             :         if (threadslot == TLS_OUT_OF_INDEXES)
     346             :                 return false;
     347             :         struct winthread *w = TlsGetValue(threadslot);
     348             : 
     349             :         return w && w->working && strcmp(w->working, "store locked") == 0;
     350             : }
     351             : 
     352             : void *
     353             : MT_thread_getdata(void)
     354             : {
     355             :         if (threadslot == TLS_OUT_OF_INDEXES)
     356             :                 return NULL;
     357             :         struct winthread *w = TlsGetValue(threadslot);
     358             : 
     359             :         return w ? w->data : NULL;
     360             : }
     361             : 
     362             : static void
     363             : rm_winthread(struct winthread *w)
     364             : {
     365             :         struct winthread **wp;
     366             : 
     367             :         EnterCriticalSection(&winthread_cs);
     368             :         for (wp = &winthreads; *wp && *wp != w; wp = &(*wp)->next)
     369             :                 ;
     370             :         if (*wp)
     371             :                 *wp = w->next;
     372             :         LeaveCriticalSection(&winthread_cs);
     373             :         ATOMIC_DESTROY(&w->exited);
     374             :         free(w);
     375             : }
     376             : 
     377             : static DWORD WINAPI
     378             : thread_starter(LPVOID arg)
     379             : {
     380             :         struct winthread *w = (struct winthread *) arg;
     381             :         void *data = w->data;
     382             : 
     383             :         w->data = NULL;
     384             :         TlsSetValue(threadslot, w);
     385             :         (*w->func)(data);
     386             :         ATOMIC_SET(&w->exited, 1);
     387             :         TRC_DEBUG(THRD, "Exit: \"%s\"\n", w->threadname);
     388             :         return 0;
     389             : }
     390             : 
     391             : static void
     392             : join_threads(void)
     393             : {
     394             :         bool waited;
     395             : 
     396             :         struct winthread *self = TlsGetValue(threadslot);
     397             :         if (!self)
     398             :                 return;
     399             :         EnterCriticalSection(&winthread_cs);
     400             :         do {
     401             :                 waited = false;
     402             :                 for (struct winthread *w = winthreads; w; w = w->next) {
     403             :                         if (w->detached && !w->waiting && ATOMIC_GET(&w->exited)) {
     404             :                                 w->waiting = true;
     405             :                                 LeaveCriticalSection(&winthread_cs);
     406             :                                 TRC_DEBUG(THRD, "Join thread \"%s\"\n", w->threadname);
     407             :                                 self->joinwait = w;
     408             :                                 WaitForSingleObject(w->hdl, INFINITE);
     409             :                                 self->joinwait = NULL;
     410             :                                 CloseHandle(w->hdl);
     411             :                                 rm_winthread(w);
     412             :                                 waited = true;
     413             :                                 EnterCriticalSection(&winthread_cs);
     414             :                                 break;
     415             :                         }
     416             :                 }
     417             :         } while (waited);
     418             :         LeaveCriticalSection(&winthread_cs);
     419             : }
     420             : 
     421             : void
     422             : join_detached_threads(void)
     423             : {
     424             :         bool waited;
     425             : 
     426             :         struct winthread *self = TlsGetValue(threadslot);
     427             :         EnterCriticalSection(&winthread_cs);
     428             :         do {
     429             :                 waited = false;
     430             :                 for (struct winthread *w = winthreads; w; w = w->next) {
     431             :                         if (w->detached && !w->waiting) {
     432             :                                 w->waiting = true;
     433             :                                 LeaveCriticalSection(&winthread_cs);
     434             :                                 TRC_DEBUG(THRD, "Join thread \"%s\"\n", w->threadname);
     435             :                                 self->joinwait = w;
     436             :                                 WaitForSingleObject(w->hdl, INFINITE);
     437             :                                 self->joinwait = NULL;
     438             :                                 CloseHandle(w->hdl);
     439             :                                 rm_winthread(w);
     440             :                                 waited = true;
     441             :                                 EnterCriticalSection(&winthread_cs);
     442             :                                 break;
     443             :                         }
     444             :                 }
     445             :         } while (waited);
     446             :         LeaveCriticalSection(&winthread_cs);
     447             : }
     448             : 
     449             : int
     450             : MT_create_thread(MT_Id *t, void (*f) (void *), void *arg, enum MT_thr_detach d, const char *threadname)
     451             : {
     452             :         struct winthread *w;
     453             : 
     454             :         join_threads();
     455             :         if (threadname == NULL) {
     456             :                 TRC_CRITICAL(GDK, "Thread must have a name\n");
     457             :                 return -1;
     458             :         }
     459             :         if (strlen(threadname) >= sizeof(w->threadname)) {
     460             :                 TRC_CRITICAL(GDK, "Thread's name is too large\n");
     461             :                 return -1;
     462             :         }
     463             : 
     464             :         w = malloc(sizeof(*w));
     465             :         if (w == NULL) {
     466             :                 GDKsyserror("Cannot allocate memory\n");
     467             :                 return -1;
     468             :         }
     469             : 
     470             :         *w = (struct winthread) {
     471             :                 .func = f,
     472             :                 .data = arg,
     473             :                 .waiting = false,
     474             :                 .detached = (d == MT_THR_DETACHED),
     475             :         };
     476             :         ATOMIC_INIT(&w->exited, 0);
     477             :         strcpy_len(w->threadname, threadname, sizeof(w->threadname));
     478             :         TRC_DEBUG(THRD, "Create thread \"%s\"\n", threadname);
     479             :         EnterCriticalSection(&winthread_cs);
     480             :         w->hdl = CreateThread(NULL, THREAD_STACK_SIZE, thread_starter, w,
     481             :                               0, &w->tid);
     482             :         if (w->hdl == NULL) {
     483             :                 GDKwinerror("Failed to create thread");
     484             :                 LeaveCriticalSection(&winthread_cs);
     485             :                 free(w);
     486             :                 return -1;
     487             :         }
     488             :         /* must not fail after this: the thread has been started */
     489             :         w->next = winthreads;
     490             :         winthreads = w;
     491             :         LeaveCriticalSection(&winthread_cs);
     492             :         *t = (MT_Id) w->tid;
     493             :         return 0;
     494             : }
     495             : 
     496             : MT_Id
     497             : MT_getpid(void)
     498             : {
     499             :         return (MT_Id) GetCurrentThreadId();
     500             : }
     501             : 
     502             : void
     503             : MT_exiting_thread(void)
     504             : {
     505             :         if (threadslot == TLS_OUT_OF_INDEXES)
     506             :                 return;
     507             : 
     508             :         struct winthread *w = TlsGetValue(threadslot);
     509             : 
     510             :         if (w) {
     511             :                 ATOMIC_SET(&w->exited, 1);
     512             :                 w->working = NULL;
     513             :         }
     514             : }
     515             : 
     516             : int
     517             : MT_join_thread(MT_Id t)
     518             : {
     519             :         struct winthread *w;
     520             : 
     521             :         assert(t != mainthread.tid);
     522             :         join_threads();
     523             :         w = find_winthread((DWORD) t);
     524             :         if (w == NULL || w->hdl == NULL)
     525             :                 return -1;
     526             :         TRC_DEBUG(THRD, "Join thread \"%s\"\n", w->threadname);
     527             :         struct winthread *self = TlsGetValue(threadslot);
     528             :         self->joinwait = w;
     529             :         DWORD ret = WaitForSingleObject(w->hdl, INFINITE);
     530             :         self->joinwait = NULL;
     531             :         if (ret == WAIT_OBJECT_0 && CloseHandle(w->hdl)) {
     532             :                 rm_winthread(w);
     533             :                 return 0;
     534             :         }
     535             :         return -1;
     536             : }
     537             : 
     538             : int
     539             : MT_kill_thread(MT_Id t)
     540             : {
     541             :         struct winthread *w;
     542             : 
     543             :         assert(t != mainthread.tid);
     544             :         join_threads();
     545             :         w = find_winthread((DWORD) t);
     546             :         if (w == NULL)
     547             :                 return -1;
     548             :         if (w->hdl == NULL) {
     549             :                 /* detached thread */
     550             :                 HANDLE h;
     551             :                 int ret = 0;
     552             :                 h = OpenThread(THREAD_ALL_ACCESS, 0, (DWORD) t);
     553             :                 if (h == NULL)
     554             :                         return -1;
     555             :                 if (TerminateThread(h, -1))
     556             :                         ret = -1;
     557             :                 CloseHandle(h);
     558             :                 return ret;
     559             :         }
     560             :         if (TerminateThread(w->hdl, -1))
     561             :                 return 0;
     562             :         return -1;
     563             : }
     564             : 
     565             : #else  /* !defined(HAVE_PTHREAD_H) && defined(_MSC_VER) */
     566             : 
     567             : static struct posthread {
     568             :         struct posthread *next;
     569             :         void (*func)(void *);
     570             :         void *data;
     571             :         MT_Lock *lockwait;      /* lock we're waiting for */
     572             :         MT_Sema *semawait;      /* semaphore we're waiting for */
     573             :         struct posthread *joinwait; /* process we are joining with */
     574             :         const char *working;    /* what we're currently doing */
     575             :         char algorithm[512];    /* the algorithm used in the last operation */
     576             :         size_t algolen;         /* length of string in .algorithm */
     577             :         char threadname[MT_NAME_LEN];
     578             :         pthread_t tid;
     579             :         MT_Id mtid;
     580             :         ATOMIC_TYPE exited;
     581             :         bool detached:1, waiting:1;
     582             : } *posthreads = NULL;
     583             : static struct posthread mainthread = {
     584             :         .threadname = "main thread",
     585             :         .mtid = 1,
     586             :         .exited = ATOMIC_VAR_INIT(0),
     587             : };
     588             : static pthread_mutex_t posthread_lock = PTHREAD_MUTEX_INITIALIZER;
     589             : static MT_Id MT_thread_id = 1;
     590             : 
     591             : static pthread_key_t threadkey;
     592             : static bool thread_initialized = false;
     593             : 
     594             : void
     595           0 : dump_threads(void)
     596             : {
     597           0 :         TRC_DEBUG_IF(THRD) {
     598           0 :                 pthread_mutex_lock(&posthread_lock);
     599           0 :                 for (struct posthread *p = posthreads; p; p = p->next) {
     600           0 :                         TRC_DEBUG_ENDIF(THRD, "%s, waiting for %s, working on %.200s\n",
     601             :                                         p->threadname,
     602             :                                         p->lockwait ? p->lockwait->name :
     603             :                                         p->semawait ? p->semawait->name :
     604             :                                         p->joinwait ? p->joinwait->threadname :
     605             :                                         "nothing",
     606             :                                         ATOMIC_GET(&p->exited) ? "exiting" :
     607             :                                         p->working ? p->working : "nothing");
     608             :                 }
     609           0 :                 pthread_mutex_unlock(&posthread_lock);
     610             :         }
     611           0 : }
     612             : 
     613             : bool
     614         258 : MT_thread_init(void)
     615             : {
     616             :         int ret;
     617             : 
     618         258 :         if ((ret = pthread_key_create(&threadkey, NULL)) != 0) {
     619           0 :                 GDKsyserr(ret, "Creating specific key for thread failed");
     620             :                 return false;
     621             :         }
     622         258 :         thread_initialized = true;
     623         258 :         mainthread.tid = pthread_self();
     624         258 :         if ((ret = pthread_setspecific(threadkey, &mainthread)) != 0) {
     625           0 :                 GDKsyserr(ret, "Setting specific value failed");
     626             :                 return false;
     627             :         }
     628             :         return true;
     629             : }
     630             : 
     631             : static struct posthread *
     632      131103 : find_posthread(MT_Id tid)
     633             : {
     634             :         struct posthread *p;
     635             : 
     636      131103 :         pthread_mutex_lock(&posthread_lock);
     637      688039 :         for (p = posthreads; p && p->mtid != tid; p = p->next)
     638             :                 ;
     639      131103 :         pthread_mutex_unlock(&posthread_lock);
     640      131103 :         return p;
     641             : }
     642             : 
     643             : const char *
     644       17707 : MT_thread_getname(void)
     645             : {
     646             :         struct posthread *p;
     647             : 
     648       17707 :         if (!thread_initialized)
     649             :                 return mainthread.threadname;
     650       17707 :         p = pthread_getspecific(threadkey);
     651       17707 :         return p ? p->threadname : UNKNOWN_THREAD;
     652             : }
     653             : 
     654             : void
     655      271828 : MT_thread_setdata(void *data)
     656             : {
     657      271828 :         if (!thread_initialized)
     658             :                 return;
     659      271835 :         struct posthread *p = pthread_getspecific(threadkey);
     660             : 
     661      271837 :         if (p)
     662      271837 :                 p->data = data;
     663             : }
     664             : 
     665             : void *
     666   193627567 : MT_thread_getdata(void)
     667             : {
     668   193627567 :         if (!thread_initialized)
     669             :                 return NULL;
     670   193627567 :         struct posthread *p = pthread_getspecific(threadkey);
     671             : 
     672   193709254 :         return p ? p->data : NULL;
     673             : }
     674             : 
     675             : void
     676           0 : MT_thread_setlockwait(MT_Lock *lock)
     677             : {
     678           0 :         if (!thread_initialized)
     679             :                 return;
     680           0 :         struct posthread *p = pthread_getspecific(threadkey);
     681             : 
     682           0 :         if (p)
     683           0 :                 p->lockwait = lock;
     684             : }
     685             : 
     686             : void
     687    18988481 : MT_thread_setsemawait(MT_Sema *sema)
     688             : {
     689    18988481 :         if (!thread_initialized)
     690             :                 return;
     691    19028318 :         struct posthread *p = pthread_getspecific(threadkey);
     692             : 
     693    19012277 :         if (p)
     694    19012277 :                 p->semawait = sema;
     695             : }
     696             : 
     697             : void
     698    20563924 : MT_thread_setworking(const char *work)
     699             : {
     700    20563924 :         if (!thread_initialized)
     701             :                 return;
     702    20572959 :         struct posthread *p = pthread_getspecific(threadkey);
     703             : 
     704    20591150 :         if (p)
     705    20591150 :                 p->working = work;
     706             : }
     707             : 
     708             : void
     709    53468278 : MT_thread_setalgorithm(const char *algo)
     710             : {
     711    53468278 :         if (!thread_initialized)
     712             :                 return;
     713    53481056 :         struct posthread *p = pthread_getspecific(threadkey);
     714             : 
     715    53531169 :         if (p) {
     716    53531169 :                 if (algo) {
     717     4555282 :                         if (p->algolen > 0) {
     718     1786514 :                                 if (p->algolen < sizeof(p->algorithm))
     719     1207370 :                                         p->algolen += strconcat_len(p->algorithm + p->algolen, sizeof(p->algorithm) - p->algolen, "; ", algo, NULL);
     720             :                         } else
     721     2768768 :                                 p->algolen = strcpy_len(p->algorithm, algo, sizeof(p->algorithm));
     722             :                 } else {
     723    48975887 :                         p->algorithm[0] = 0;
     724    48975887 :                         p->algolen = 0;
     725             :                 }
     726             :         }
     727             : }
     728             : 
     729             : const char *
     730         921 : MT_thread_getalgorithm(void)
     731             : {
     732         921 :         if (!thread_initialized)
     733             :                 return NULL;
     734         921 :         struct posthread *p = pthread_getspecific(threadkey);
     735             : 
     736         921 :         return p && p->algorithm[0] ? p->algorithm : NULL;
     737             : }
     738             : 
     739             : bool
     740           0 : MT_thread_override_limits(void)
     741             : {
     742           0 :         if (!thread_initialized)
     743             :                 return false;
     744           0 :         struct posthread *p = pthread_getspecific(threadkey);
     745             : 
     746           0 :         return p && p->working && strcmp(p->working, "store locked") == 0;
     747             : }
     748             : 
     749             : #ifdef HAVE_PTHREAD_SIGMASK
     750             : static void
     751      290804 : MT_thread_sigmask(sigset_t *new_mask, sigset_t *orig_mask)
     752             : {
     753             :         /* do not check for errors! */
     754      290804 :         sigdelset(new_mask, SIGQUIT);
     755      290804 :         sigdelset(new_mask, SIGPROF);
     756      290804 :         pthread_sigmask(SIG_SETMASK, new_mask, orig_mask);
     757      290804 : }
     758             : #endif
     759             : 
     760             : static void
     761      145389 : rm_posthread_locked(struct posthread *p)
     762             : {
     763             :         struct posthread **pp;
     764             : 
     765      724915 :         for (pp = &posthreads; *pp && *pp != p; pp = &(*pp)->next)
     766             :                 ;
     767      145389 :         if (*pp)
     768      145389 :                 *pp = p->next;
     769             :         ATOMIC_DESTROY(&p->exited);
     770      145389 :         free(p);
     771      145389 : }
     772             : 
     773             : static void
     774      145389 : rm_posthread(struct posthread *p)
     775             : {
     776      145389 :         pthread_mutex_lock(&posthread_lock);
     777      145389 :         rm_posthread_locked(p);
     778      145389 :         pthread_mutex_unlock(&posthread_lock);
     779      145389 : }
     780             : 
     781             : static void *
     782      145402 : thread_starter(void *arg)
     783             : {
     784             :         struct posthread *p = (struct posthread *) arg;
     785      145402 :         void *data = p->data;
     786             : 
     787      145402 :         p->data = NULL;
     788      145402 :         pthread_setspecific(threadkey, p);
     789      145401 :         (*p->func)(data);
     790      145387 :         ATOMIC_SET(&p->exited, 1);
     791      145387 :         TRC_DEBUG(THRD, "Exit thread \"%s\"\n", p->threadname);
     792      145387 :         return NULL;
     793             : }
     794             : 
     795             : static void
     796      276505 : join_threads(void)
     797             : {
     798             :         bool waited;
     799             : 
     800      276505 :         struct posthread *self = pthread_getspecific(threadkey);
     801      276505 :         pthread_mutex_lock(&posthread_lock);
     802             :         do {
     803             :                 waited = false;
     804     3520572 :                 for (struct posthread *p = posthreads; p; p = p->next) {
     805     3244067 :                         if (p->detached && !p->waiting && ATOMIC_GET(&p->exited)) {
     806       13164 :                                 p->waiting = true;
     807       13164 :                                 pthread_mutex_unlock(&posthread_lock);
     808       13164 :                                 TRC_DEBUG(THRD, "Join thread \"%s\"\n", p->threadname);
     809       13164 :                                 if (self) self->joinwait = p;
     810       13164 :                                 pthread_join(p->tid, NULL);
     811       13164 :                                 if (self) self->joinwait = NULL;
     812       13164 :                                 rm_posthread(p);
     813             :                                 waited = true;
     814       13164 :                                 pthread_mutex_lock(&posthread_lock);
     815             :                                 break;
     816             :                         }
     817             :                 }
     818             :         } while (waited);
     819      276505 :         pthread_mutex_unlock(&posthread_lock);
     820      276505 : }
     821             : 
     822             : void
     823         528 : join_detached_threads(void)
     824             : {
     825             :         bool waited;
     826             : 
     827         528 :         struct posthread *self = pthread_getspecific(threadkey);
     828         528 :         pthread_mutex_lock(&posthread_lock);
     829             :         do {
     830             :                 waited = false;
     831       10853 :                 for (struct posthread *p = posthreads; p; p = p->next) {
     832       10325 :                         if (p->detached && !p->waiting) {
     833        1122 :                                 p->waiting = true;
     834        1122 :                                 pthread_mutex_unlock(&posthread_lock);
     835        1122 :                                 TRC_DEBUG(THRD, "Join thread \"%s\"\n", p->threadname);
     836        1122 :                                 if (self) self->joinwait = p;
     837        1122 :                                 pthread_join(p->tid, NULL);
     838        1122 :                                 if (self) self->joinwait = NULL;
     839        1122 :                                 rm_posthread(p);
     840             :                                 waited = true;
     841        1122 :                                 pthread_mutex_lock(&posthread_lock);
     842             :                                 break;
     843             :                         }
     844             :                 }
     845             :         } while (waited);
     846         528 :         pthread_mutex_unlock(&posthread_lock);
     847         528 : }
     848             : 
     849             : int
     850      145402 : MT_create_thread(MT_Id *t, void (*f) (void *), void *arg, enum MT_thr_detach d, const char *threadname)
     851             : {
     852             :         pthread_attr_t attr;
     853             :         int ret;
     854             :         struct posthread *p;
     855             : 
     856      145402 :         join_threads();
     857      145402 :         if (threadname == NULL) {
     858           0 :                 TRC_CRITICAL(GDK, "Thread must have a name\n");
     859           0 :                 return -1;
     860             :         }
     861      145402 :         if (strlen(threadname) >= sizeof(p->threadname)) {
     862           0 :                 TRC_CRITICAL(GDK, "Thread's name is too large\n");
     863           0 :                 return -1;
     864             :         }
     865      145402 :         if ((ret = pthread_attr_init(&attr)) != 0) {
     866           0 :                 GDKsyserr(ret, "Cannot init pthread attr");
     867             :                 return -1;
     868             :         }
     869      145402 :         if ((ret = pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE)) != 0) {
     870           0 :                 GDKsyserr(ret, "Cannot set stack size");
     871           0 :                 pthread_attr_destroy(&attr);
     872             :                 return -1;
     873             :         }
     874      145402 :         p = malloc(sizeof(struct posthread));
     875      145402 :         if (p == NULL) {
     876           0 :                 GDKsyserror("Cannot allocate memory\n");
     877           0 :                 pthread_attr_destroy(&attr);
     878             :                 return -1;
     879             :         }
     880      145402 :         *p = (struct posthread) {
     881             :                 .func = f,
     882             :                 .data = arg,
     883             :                 .waiting = false,
     884      145402 :                 .detached = (d == MT_THR_DETACHED),
     885             :         };
     886      145402 :         ATOMIC_INIT(&p->exited, 0);
     887             : 
     888      145402 :         strcpy_len(p->threadname, threadname, sizeof(p->threadname));
     889             : #ifdef HAVE_PTHREAD_SIGMASK
     890             :         sigset_t new_mask, orig_mask;
     891      145402 :         (void) sigfillset(&new_mask);
     892      145402 :         MT_thread_sigmask(&new_mask, &orig_mask);
     893             : #endif
     894      145402 :         TRC_DEBUG(THRD, "Create thread \"%s\"\n", threadname);
     895             :         /* protect posthreads during thread creation and only add to
     896             :          * it after the thread was created successfully */
     897      145402 :         pthread_mutex_lock(&posthread_lock);
     898      145402 :         *t = p->mtid = ++MT_thread_id;
     899      145402 :         ret = pthread_create(&p->tid, &attr, thread_starter, p);
     900      145402 :         if (ret != 0) {
     901           0 :                 pthread_mutex_unlock(&posthread_lock);
     902           0 :                 GDKsyserr(ret, "Cannot start thread");
     903           0 :                 free(p);
     904             :                 ret = -1;
     905             :         } else {
     906             :                 /* must not fail after this: the thread has been started */
     907      145402 :                 p->next = posthreads;
     908      145402 :                 posthreads = p;
     909      145402 :                 pthread_mutex_unlock(&posthread_lock);
     910             :         }
     911      145402 :         (void) pthread_attr_destroy(&attr); /* not interested in errors */
     912             : #ifdef HAVE_PTHREAD_SIGMASK
     913      145402 :         MT_thread_sigmask(&orig_mask, NULL);
     914             : #endif
     915      145402 :         return ret;
     916             : }
     917             : 
     918             : MT_Id
     919   102687151 : MT_getpid(void)
     920             : {
     921             :         struct posthread *p;
     922             : 
     923   102687151 :         if (!thread_initialized)
     924         258 :                 return mainthread.mtid;
     925   102686893 :         p = pthread_getspecific(threadkey);
     926   102909457 :         return p ? p->mtid : 0;
     927             : }
     928             : 
     929             : void
     930        4292 : MT_exiting_thread(void)
     931             : {
     932             :         struct posthread *p;
     933             : 
     934        4292 :         if (!thread_initialized)
     935             :                 return;
     936        4292 :         p = pthread_getspecific(threadkey);
     937        4292 :         if (p) {
     938        4292 :                 ATOMIC_SET(&p->exited, 1);
     939        4292 :                 p->working = NULL;
     940             :         }
     941             : }
     942             : 
     943             : int
     944      131103 : MT_join_thread(MT_Id t)
     945             : {
     946             :         struct posthread *p;
     947             :         int ret;
     948             : 
     949      131103 :         assert(t > 1);
     950      131103 :         join_threads();
     951      131103 :         p = find_posthread(t);
     952      131103 :         if (p == NULL)
     953             :                 return -1;
     954      131103 :         TRC_DEBUG(THRD, "Join thread \"%s\"\n", p->threadname);
     955      131103 :         struct posthread *self = pthread_getspecific(threadkey);
     956      131103 :         if (self) self->joinwait = p;
     957      131103 :         ret = pthread_join(p->tid, NULL);
     958      131103 :         if (self) self->joinwait = NULL;
     959      131103 :         if (ret != 0) {
     960           0 :                 GDKsyserr(ret, "Joining thread failed");
     961             :                 return -1;
     962             :         }
     963      131103 :         rm_posthread(p);
     964      131103 :         return 0;
     965             : }
     966             : 
     967             : int
     968           0 : MT_kill_thread(MT_Id t)
     969             : {
     970           0 :         assert(t > 1);
     971             : #ifdef HAVE_PTHREAD_KILL
     972             :         struct posthread *p;
     973             : 
     974           0 :         join_threads();
     975           0 :         p = find_posthread(t);
     976           0 :         if (p)
     977           0 :                 return pthread_kill(p->tid, SIGHUP);
     978             : #else
     979             :         (void) t;
     980             :         join_threads();
     981             : #endif
     982             :         return -1;
     983             : }
     984             : #endif
     985             : 
     986             : int
     987         266 : MT_check_nr_cores(void)
     988             : {
     989             :         int ncpus = -1;
     990             : 
     991             : #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
     992             :         /* this works on Linux, Solaris and AIX */
     993         266 :         ncpus = sysconf(_SC_NPROCESSORS_ONLN);
     994             : #elif defined(HW_NCPU)   /* BSD */
     995             :         size_t len = sizeof(int);
     996             :         int mib[3];
     997             : 
     998             :         /* Everyone should have permission to make this call,
     999             :          * if we get a failure something is really wrong. */
    1000             :         mib[0] = CTL_HW;
    1001             :         mib[1] = HW_NCPU;
    1002             :         mib[2] = -1;
    1003             :         sysctl(mib, 3, &ncpus, &len, NULL, 0);
    1004             : #elif defined(WIN32)
    1005             :         SYSTEM_INFO sysinfo;
    1006             : 
    1007             :         GetSystemInfo(&sysinfo);
    1008             :         ncpus = sysinfo.dwNumberOfProcessors;
    1009             : #endif
    1010             : 
    1011             :         /* if we ever need HPUX or OSF/1 (hope not), see
    1012             :          * http://ndevilla.free.fr/threads/ */
    1013             : 
    1014             :         if (ncpus <= 0)
    1015             :                 ncpus = 1;
    1016             : #if SIZEOF_SIZE_T == SIZEOF_INT
    1017             :         /* On 32-bits systems with large numbers of cpus/cores, we
    1018             :          * quickly run out of space due to the number of threads in
    1019             :          * use.  Since it is questionable whether many cores on a
    1020             :          * 32-bits system are going to be beneficial due to this, we
    1021             :          * simply limit the auto-detected cores to 16 on 32-bits
    1022             :          * systems.  The user can always override this via
    1023             :          * gdk_nr_threads. */
    1024             :         if (ncpus > 16)
    1025             :                 ncpus = 16;
    1026             : #endif
    1027             : 
    1028             : #ifndef WIN32
    1029             :         /* get the number of allocated cpus from the cgroup settings */
    1030         266 :         FILE *f = fopen("/sys/fs/cgroup/cpuset/cpuset.cpus", "r");
    1031         266 :         if (f != NULL) {
    1032             :                 char buf[512];
    1033           0 :                 char *p = fgets(buf, 512, f);
    1034           0 :                 fclose(f);
    1035           0 :                 if (p != NULL) {
    1036             :                         /* syntax is: ranges of CPU numbers separated
    1037             :                          * by comma; a range is either a single CPU
    1038             :                          * id, or two IDs separated by a minus; any
    1039             :                          * deviation causes the file to be ignored */
    1040             :                         int ncpu = 0;
    1041           0 :                         for (;;) {
    1042             :                                 char *q;
    1043           0 :                                 unsigned fst = strtoul(p, &q, 10);
    1044           0 :                                 if (q == p)
    1045           0 :                                         return ncpus;
    1046           0 :                                 ncpu++;
    1047           0 :                                 if (*q == '-') {
    1048           0 :                                         p = q + 1;
    1049           0 :                                         unsigned lst = strtoul(p, &q, 10);
    1050           0 :                                         if (q == p || lst <= fst)
    1051             :                                                 return ncpus;
    1052           0 :                                         ncpu += lst - fst;
    1053             :                                 }
    1054           0 :                                 if (*q == '\n')
    1055             :                                         break;
    1056           0 :                                 if (*q != ',')
    1057             :                                         return ncpus;
    1058           0 :                                 p = q + 1;
    1059             :                         }
    1060           0 :                         if (ncpu < ncpus)
    1061             :                                 return ncpu;
    1062             :                 }
    1063             :         }
    1064             : #endif
    1065             : 
    1066             :         return ncpus;
    1067             : }

Generated by: LCOV version 1.14