LCOV - code coverage report
Current view: top level - gdk - gdk_system.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 181 227 79.7 %
Date: 2020-06-29 20:00:14 Functions: 18 22 81.8 %

          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 - 2020 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             :         /* recursively sort both sublists */
      77             :         l = sortlocklist(l);
      78             :         r = sortlocklist(r);
      79             :         /* merge
      80             :          * t is new list, ll is last element of new list, l and r are
      81             :          * start of unprocessed part of left and right lists */
      82             :         t = ll = NULL;
      83             :         while (l && r) {
      84             :                 if (ATOMIC_GET(&l->sleep) < ATOMIC_GET(&r->sleep) ||
      85             :                     (ATOMIC_GET(&l->sleep) == ATOMIC_GET(&r->sleep) &&
      86             :                      (ATOMIC_GET(&l->contention) < ATOMIC_GET(&r->contention) ||
      87             :                       (ATOMIC_GET(&l->contention) == ATOMIC_GET(&r->contention) &&
      88             :                        l->count <= r->count)))) {
      89             :                         /* l is smaller */
      90             :                         if (ll == NULL) {
      91             :                                 assert(t == NULL);
      92             :                                 t = ll = l;
      93             :                         } else {
      94             :                                 ll->next = l;
      95             :                                 ll = ll->next;
      96             :                         }
      97             :                         l = l->next;
      98             :                 } else {
      99             :                         /* r is smaller */
     100             :                         if (ll == NULL) {
     101             :                                 assert(t == NULL);
     102             :                                 t = ll = r;
     103             :                         } else {
     104             :                                 ll->next = r;
     105             :                                 ll = ll->next;
     106             :                         }
     107             :                         r = r->next;
     108             :                 }
     109             :         }
     110             :         /* append rest of remaining list */
     111             :         ll->next = l ? l : r;
     112             :         return t;
     113             : }
     114             : 
     115             : static inline bool
     116             : lock_isset(MT_Lock *l)
     117             : {
     118             :         if (MT_lock_try(l)) {
     119             :                 MT_lock_unset(l);
     120             :                 return false;
     121             :         }
     122             :         return true;
     123             : }
     124             : 
     125             : /* function used for debugging */
     126             : void
     127             : GDKlockstatistics(int what)
     128             : {
     129             :         MT_Lock *l;
     130             :         int n = 0;
     131             : 
     132             :         if (ATOMIC_TAS(&GDKlocklistlock) != 0) {
     133             :                 fprintf(stderr, "GDKlocklistlock is set, so cannot access lock list\n");
     134             :                 return;
     135             :         }
     136             :         if (what == -1) {
     137             :                 for (l = GDKlocklist; l; l = l->next) {
     138             :                         l->count = 0;
     139             :                         ATOMIC_SET(&l->contention, 0);
     140             :                         ATOMIC_SET(&l->sleep, 0);
     141             :                 }
     142             :                 ATOMIC_CLEAR(&GDKlocklistlock);
     143             :                 return;
     144             :         }
     145             :         GDKlocklist = sortlocklist(GDKlocklist);
     146             :         fprintf(stderr, "lock name\tcount\tcontention\tsleep\tlocked\t(un)locker\tthread\n");
     147             :         for (l = GDKlocklist; l; l = l->next) {
     148             :                 n++;
     149             :                 if (what == 0 ||
     150             :                     (what == 1 && l->count) ||
     151             :                     (what == 2 && ATOMIC_GET(&l->contention)) ||
     152             :                     (what == 3 && lock_isset(l)))
     153             :                         fprintf(stderr, "%-18s\t%zu\t%zu\t%zu\t%s\t%s\t%s\n",
     154             :                                 l->name, l->count,
     155             :                                 (size_t) ATOMIC_GET(&l->contention),
     156             :                                 (size_t) ATOMIC_GET(&l->sleep),
     157             :                                 lock_isset(l) ? "locked" : "",
     158             :                                 l->locker ? l->locker : "",
     159             :                                 l->thread ? l->thread : "");
     160             :         }
     161             :         fprintf(stderr, "Number of locks: %d\n", n);
     162             :         fprintf(stderr, "Total lock count: %zu\n", (size_t) ATOMIC_GET(&GDKlockcnt));
     163             :         fprintf(stderr, "Lock contention:  %zu\n", (size_t) ATOMIC_GET(&GDKlockcontentioncnt));
     164             :         fprintf(stderr, "Lock sleep count: %zu\n", (size_t) ATOMIC_GET(&GDKlocksleepcnt));
     165             :         ATOMIC_CLEAR(&GDKlocklistlock);
     166             : }
     167             : 
     168             : #endif  /* LOCK_STATS */
     169             : 
     170             : #if !defined(HAVE_PTHREAD_H) && defined(WIN32)
     171             : static struct winthread {
     172             :         struct winthread *next;
     173             :         HANDLE hdl;
     174             :         DWORD tid;
     175             :         void (*func) (void *);
     176             :         void *data;
     177             :         MT_Lock *lockwait;      /* lock we're waiting for */
     178             :         MT_Sema *semawait;      /* semaphore we're waiting for */
     179             :         struct winthread *joinwait; /* process we are joining with */
     180             :         const char *working;    /* what we're currently doing */
     181             :         ATOMIC_TYPE exited;
     182             :         bool detached:1, waiting:1;
     183             :         char threadname[MT_NAME_LEN];
     184             : } *winthreads = NULL;
     185             : static struct winthread mainthread = {
     186             :         .threadname = "main thread",
     187             :         .exited = ATOMIC_VAR_INIT(0),
     188             : };
     189             : 
     190             : static CRITICAL_SECTION winthread_cs;
     191             : static DWORD threadslot = TLS_OUT_OF_INDEXES;
     192             : 
     193             : void
     194             : dump_threads(void)
     195             : {
     196             :         TRC_DEBUG_IF(THRD) {
     197             :                 EnterCriticalSection(&winthread_cs);
     198             :                 for (struct winthread *w = winthreads; w; w = w->next) {
     199             :                         TRC_DEBUG_ENDIF(THRD, "%s, waiting for %s, working on %.200s\n",
     200             :                                         w->threadname,
     201             :                                         w->lockwait ? w->lockwait->name :
     202             :                                         w->semawait ? w->semawait->name :
     203             :                                         w->joinwait ? w->joinwait->threadname :
     204             :                                         "nothing",
     205             :                                         ATOMIC_GET(&w->exited) ? "exiting" :
     206             :                                         w->working ? w->working : "nothing");
     207             :                 }
     208             :                 LeaveCriticalSection(&winthread_cs);
     209             :         }
     210             : }
     211             : 
     212             : bool
     213             : MT_thread_init(void)
     214             : {
     215             :         if (threadslot == TLS_OUT_OF_INDEXES) {
     216             :                 threadslot = TlsAlloc();
     217             :                 if (threadslot == TLS_OUT_OF_INDEXES) {
     218             :                         GDKwinerror("Creating thread-local slot for thread failed");
     219             :                         return false;
     220             :                 }
     221             :                 mainthread.tid = GetCurrentThreadId();
     222             :                 if (TlsSetValue(threadslot, &mainthread) == 0) {
     223             :                         GDKwinerror("Setting thread-local value failed");
     224             :                         TlsFree(threadslot);
     225             :                         threadslot = TLS_OUT_OF_INDEXES;
     226             :                         return false;
     227             :                 }
     228             :                 InitializeCriticalSection(&winthread_cs);
     229             :         }
     230             :         return true;
     231             : }
     232             : 
     233             : static struct winthread *
     234             : find_winthread(DWORD tid)
     235             : {
     236             :         struct winthread *w;
     237             : 
     238             :         EnterCriticalSection(&winthread_cs);
     239             :         for (w = winthreads; w && w->tid != tid; w = w->next)
     240             :                 ;
     241             :         LeaveCriticalSection(&winthread_cs);
     242             :         return w;
     243             : }
     244             : 
     245             : const char *
     246             : MT_thread_getname(void)
     247             : {
     248             :         struct winthread *w = TlsGetValue(threadslot);
     249             :         return w ? w->threadname : "unknown thread";
     250             : }
     251             : 
     252             : void
     253             : MT_thread_setdata(void *data)
     254             : {
     255             :         struct winthread *w = TlsGetValue(threadslot);
     256             : 
     257             :         if (w)
     258             :                 w->data = data;
     259             : }
     260             : 
     261             : void
     262             : MT_thread_setlockwait(MT_Lock *lock)
     263             : {
     264             :         struct winthread *w = TlsGetValue(threadslot);
     265             : 
     266             :         if (w)
     267             :                 w->lockwait = lock;
     268             : }
     269             : 
     270             : void
     271             : MT_thread_setsemawait(MT_Sema *sema)
     272             : {
     273             :         struct winthread *w = TlsGetValue(threadslot);
     274             : 
     275             :         if (w)
     276             :                 w->semawait = sema;
     277             : }
     278             : 
     279             : void
     280             : MT_thread_setworking(const char *work)
     281             : {
     282             :         struct winthread *w = TlsGetValue(threadslot);
     283             : 
     284             :         if (w)
     285             :                 w->working = work;
     286             : }
     287             : 
     288             : bool
     289             : MT_thread_override_limits(void)
     290             : {
     291             :         struct winthread *w = TlsGetValue(threadslot);
     292             : 
     293             :         return w && w->working && strcmp(w->working, "store locked") == 0;
     294             : }
     295             : 
     296             : void *
     297             : MT_thread_getdata(void)
     298             : {
     299             :         struct winthread *w = TlsGetValue(threadslot);
     300             : 
     301             :         return w ? w->data : NULL;
     302             : }
     303             : 
     304             : static void
     305             : rm_winthread(struct winthread *w)
     306             : {
     307             :         struct winthread **wp;
     308             : 
     309             :         EnterCriticalSection(&winthread_cs);
     310             :         for (wp = &winthreads; *wp && *wp != w; wp = &(*wp)->next)
     311             :                 ;
     312             :         if (*wp)
     313             :                 *wp = w->next;
     314             :         LeaveCriticalSection(&winthread_cs);
     315             :         ATOMIC_DESTROY(&w->exited);
     316             :         free(w);
     317             : }
     318             : 
     319             : static DWORD WINAPI
     320             : thread_starter(LPVOID arg)
     321             : {
     322             :         struct winthread *w = (struct winthread *) arg;
     323             :         void *data = w->data;
     324             : 
     325             :         w->data = NULL;
     326             :         TlsSetValue(threadslot, w);
     327             :         (*w->func)(data);
     328             :         ATOMIC_SET(&w->exited, 1);
     329             :         TRC_DEBUG(THRD, "Exit: \"%s\"\n", w->threadname);
     330             :         return 0;
     331             : }
     332             : 
     333             : static void
     334             : join_threads(void)
     335             : {
     336             :         bool waited;
     337             : 
     338             :         struct winthread *self = TlsGetValue(threadslot);
     339             :         EnterCriticalSection(&winthread_cs);
     340             :         do {
     341             :                 waited = false;
     342             :                 for (struct winthread *w = winthreads; w; w = w->next) {
     343             :                         if (w->detached && !w->waiting && ATOMIC_GET(&w->exited)) {
     344             :                                 w->waiting = true;
     345             :                                 LeaveCriticalSection(&winthread_cs);
     346             :                                 TRC_DEBUG(THRD, "Join thread \"%s\"\n", w->threadname);
     347             :                                 self->joinwait = w;
     348             :                                 WaitForSingleObject(w->hdl, INFINITE);
     349             :                                 self->joinwait = NULL;
     350             :                                 CloseHandle(w->hdl);
     351             :                                 rm_winthread(w);
     352             :                                 waited = true;
     353             :                                 EnterCriticalSection(&winthread_cs);
     354             :                                 break;
     355             :                         }
     356             :                 }
     357             :         } while (waited);
     358             :         LeaveCriticalSection(&winthread_cs);
     359             : }
     360             : 
     361             : void
     362             : join_detached_threads(void)
     363             : {
     364             :         bool waited;
     365             : 
     366             :         struct winthread *self = TlsGetValue(threadslot);
     367             :         EnterCriticalSection(&winthread_cs);
     368             :         do {
     369             :                 waited = false;
     370             :                 for (struct winthread *w = winthreads; w; w = w->next) {
     371             :                         if (w->detached && !w->waiting) {
     372             :                                 w->waiting = true;
     373             :                                 LeaveCriticalSection(&winthread_cs);
     374             :                                 TRC_DEBUG(THRD, "Join thread \"%s\"\n", w->threadname);
     375             :                                 self->joinwait = w;
     376             :                                 WaitForSingleObject(w->hdl, INFINITE);
     377             :                                 self->joinwait = NULL;
     378             :                                 CloseHandle(w->hdl);
     379             :                                 rm_winthread(w);
     380             :                                 waited = true;
     381             :                                 EnterCriticalSection(&winthread_cs);
     382             :                                 break;
     383             :                         }
     384             :                 }
     385             :         } while (waited);
     386             :         LeaveCriticalSection(&winthread_cs);
     387             : }
     388             : 
     389             : int
     390             : MT_create_thread(MT_Id *t, void (*f) (void *), void *arg, enum MT_thr_detach d, const char *threadname)
     391             : {
     392             :         struct winthread *w;
     393             : 
     394             :         join_threads();
     395             :         if (threadname == NULL) {
     396             :                 TRC_CRITICAL(GDK, "Thread must have a name\n");
     397             :                 return -1;
     398             :         }
     399             :         if (strlen(threadname) >= sizeof(w->threadname)) {
     400             :                 TRC_CRITICAL(GDK, "Thread's name is too large\n");
     401             :                 return -1;
     402             :         }
     403             : 
     404             :         w = malloc(sizeof(*w));
     405             :         if (w == NULL) {
     406             :                 GDKsyserror("Cannot allocate memory\n");
     407             :                 return -1;
     408             :         }
     409             : 
     410             :         *w = (struct winthread) {
     411             :                 .func = f,
     412             :                 .data = arg,
     413             :                 .waiting = false,
     414             :                 .detached = (d == MT_THR_DETACHED),
     415             :         };
     416             :         ATOMIC_INIT(&w->exited, 0);
     417             :         strcpy_len(w->threadname, threadname, sizeof(w->threadname));
     418             :         TRC_DEBUG(THRD, "Create thread \"%s\"\n", threadname);
     419             :         EnterCriticalSection(&winthread_cs);
     420             :         w->hdl = CreateThread(NULL, THREAD_STACK_SIZE, thread_starter, w,
     421             :                               0, &w->tid);
     422             :         if (w->hdl == NULL) {
     423             :                 GDKwinerror("Failed to create thread");
     424             :                 LeaveCriticalSection(&winthread_cs);
     425             :                 free(w);
     426             :                 return -1;
     427             :         }
     428             :         /* must not fail after this: the thread has been started */
     429             :         w->next = winthreads;
     430             :         winthreads = w;
     431             :         LeaveCriticalSection(&winthread_cs);
     432             :         *t = (MT_Id) w->tid;
     433             :         return 0;
     434             : }
     435             : 
     436             : MT_Id
     437             : MT_getpid(void)
     438             : {
     439             :         return (MT_Id) GetCurrentThreadId();
     440             : }
     441             : 
     442             : void
     443             : MT_exiting_thread(void)
     444             : {
     445             :         struct winthread *w = TlsGetValue(threadslot);
     446             : 
     447             :         if (w) {
     448             :                 ATOMIC_SET(&w->exited, 1);
     449             :                 w->working = NULL;
     450             :         }
     451             : }
     452             : 
     453             : int
     454             : MT_join_thread(MT_Id t)
     455             : {
     456             :         struct winthread *w;
     457             : 
     458             :         assert(t != mainthread.tid);
     459             :         join_threads();
     460             :         w = find_winthread((DWORD) t);
     461             :         if (w == NULL || w->hdl == NULL)
     462             :                 return -1;
     463             :         TRC_DEBUG(THRD, "Join thread \"%s\"\n", w->threadname);
     464             :         struct winthread *self = TlsGetValue(threadslot);
     465             :         self->joinwait = w;
     466             :         DWORD ret = WaitForSingleObject(w->hdl, INFINITE);
     467             :         self->joinwait = NULL;
     468             :         if (ret == WAIT_OBJECT_0 && CloseHandle(w->hdl)) {
     469             :                 rm_winthread(w);
     470             :                 return 0;
     471             :         }
     472             :         return -1;
     473             : }
     474             : 
     475             : int
     476             : MT_kill_thread(MT_Id t)
     477             : {
     478             :         struct winthread *w;
     479             : 
     480             :         assert(t != mainthread.tid);
     481             :         join_threads();
     482             :         w = find_winthread((DWORD) t);
     483             :         if (w == NULL)
     484             :                 return -1;
     485             :         if (w->hdl == NULL) {
     486             :                 /* detached thread */
     487             :                 HANDLE h;
     488             :                 int ret = 0;
     489             :                 h = OpenThread(THREAD_ALL_ACCESS, 0, (DWORD) t);
     490             :                 if (h == NULL)
     491             :                         return -1;
     492             :                 if (TerminateThread(h, -1))
     493             :                         ret = -1;
     494             :                 CloseHandle(h);
     495             :                 return ret;
     496             :         }
     497             :         if (TerminateThread(w->hdl, -1))
     498             :                 return 0;
     499             :         return -1;
     500             : }
     501             : 
     502             : #else  /* !defined(HAVE_PTHREAD_H) && defined(_MSC_VER) */
     503             : 
     504             : static struct posthread {
     505             :         struct posthread *next;
     506             :         void (*func)(void *);
     507             :         void *data;
     508             :         MT_Lock *lockwait;      /* lock we're waiting for */
     509             :         MT_Sema *semawait;      /* semaphore we're waiting for */
     510             :         struct posthread *joinwait; /* process we are joining with */
     511             :         const char *working;    /* what we're currently doing */
     512             :         char threadname[MT_NAME_LEN];
     513             :         pthread_t tid;
     514             :         MT_Id mtid;
     515             :         ATOMIC_TYPE exited;
     516             :         bool detached:1, waiting:1;
     517             : } *posthreads = NULL;
     518             : static struct posthread mainthread = {
     519             :         .threadname = "main thread",
     520             :         .mtid = 1,
     521             :         .exited = ATOMIC_VAR_INIT(0),
     522             : };
     523             : static pthread_mutex_t posthread_lock = PTHREAD_MUTEX_INITIALIZER;
     524             : static MT_Id MT_thread_id = 1;
     525             : 
     526             : static pthread_key_t threadkey;
     527             : 
     528             : void
     529           0 : dump_threads(void)
     530             : {
     531           0 :         TRC_DEBUG_IF(THRD) {
     532           0 :                 pthread_mutex_lock(&posthread_lock);
     533           0 :                 for (struct posthread *p = posthreads; p; p = p->next) {
     534           0 :                         TRC_DEBUG_ENDIF(THRD, "%s, waiting for %s, working on %.200s\n",
     535             :                                         p->threadname,
     536             :                                         p->lockwait ? p->lockwait->name :
     537             :                                         p->semawait ? p->semawait->name :
     538             :                                         p->joinwait ? p->joinwait->threadname :
     539             :                                         "nothing",
     540             :                                         ATOMIC_GET(&p->exited) ? "exiting" :
     541             :                                         p->working ? p->working : "nothing");
     542             :                 }
     543           0 :                 pthread_mutex_unlock(&posthread_lock);
     544             :         }
     545           0 : }
     546             : 
     547             : bool
     548         244 : MT_thread_init(void)
     549             : {
     550         244 :         int ret;
     551             : 
     552         244 :         if ((ret = pthread_key_create(&threadkey, NULL)) != 0) {
     553           0 :                 GDKsyserr(ret, "Creating specific key for thread failed");
     554           0 :                 return false;
     555             :         }
     556         244 :         mainthread.tid = pthread_self();
     557         244 :         if ((ret = pthread_setspecific(threadkey, &mainthread)) != 0) {
     558           0 :                 GDKsyserr(ret, "Setting specific value failed");
     559           0 :                 return false;
     560             :         }
     561             :         return true;
     562             : }
     563             : 
     564             : static struct posthread *
     565       29965 : find_posthread(MT_Id tid)
     566             : {
     567       29965 :         struct posthread *p;
     568             : 
     569       29965 :         pthread_mutex_lock(&posthread_lock);
     570       92635 :         for (p = posthreads; p && p->mtid != tid; p = p->next)
     571       62670 :                 ;
     572       29965 :         pthread_mutex_unlock(&posthread_lock);
     573       29965 :         return p;
     574             : }
     575             : 
     576             : const char *
     577       14124 : MT_thread_getname(void)
     578             : {
     579       14124 :         struct posthread *p;
     580             : 
     581       14124 :         p = pthread_getspecific(threadkey);
     582       14124 :         return p ? p->threadname : "unknown thread";
     583             : }
     584             : 
     585             : void
     586       69888 : MT_thread_setdata(void *data)
     587             : {
     588       69888 :         struct posthread *p = pthread_getspecific(threadkey);
     589             : 
     590       69892 :         if (p)
     591       69892 :                 p->data = data;
     592       69892 : }
     593             : 
     594             : void *
     595   626370000 : MT_thread_getdata(void)
     596             : {
     597   626370000 :         struct posthread *p = pthread_getspecific(threadkey);
     598             : 
     599   626358000 :         return p ? p->data : NULL;
     600             : }
     601             : 
     602             : void
     603           0 : MT_thread_setlockwait(MT_Lock *lock)
     604             : {
     605           0 :         struct posthread *p = pthread_getspecific(threadkey);
     606             : 
     607           0 :         if (p)
     608           0 :                 p->lockwait = lock;
     609           0 : }
     610             : 
     611             : void
     612    17967800 : MT_thread_setsemawait(MT_Sema *sema)
     613             : {
     614    17967800 :         struct posthread *p = pthread_getspecific(threadkey);
     615             : 
     616    17964900 :         if (p)
     617    17964900 :                 p->semawait = sema;
     618    17964900 : }
     619             : 
     620             : void
     621     8479160 : MT_thread_setworking(const char *work)
     622             : {
     623     8479160 :         struct posthread *p = pthread_getspecific(threadkey);
     624             : 
     625     8478290 :         if (p)
     626     8478290 :                 p->working = work;
     627     8478290 : }
     628             : 
     629             : bool
     630           0 : MT_thread_override_limits(void)
     631             : {
     632           0 :         struct posthread *p = pthread_getspecific(threadkey);
     633             : 
     634           0 :         return p && p->working && strcmp(p->working, "store locked") == 0;
     635             : }
     636             : 
     637             : #ifdef HAVE_PTHREAD_SIGMASK
     638             : static void
     639       77460 : MT_thread_sigmask(sigset_t *new_mask, sigset_t *orig_mask)
     640             : {
     641             :         /* do not check for errors! */
     642       77460 :         sigdelset(new_mask, SIGQUIT);
     643       77460 :         sigdelset(new_mask, SIGPROF);
     644       77460 :         pthread_sigmask(SIG_SETMASK, new_mask, orig_mask);
     645       77460 : }
     646             : #endif
     647             : 
     648             : static void
     649       38720 : rm_posthread_locked(struct posthread *p)
     650             : {
     651       38720 :         struct posthread **pp;
     652             : 
     653      107840 :         for (pp = &posthreads; *pp && *pp != p; pp = &(*pp)->next)
     654       69120 :                 ;
     655       38720 :         if (*pp)
     656       38720 :                 *pp = p->next;
     657       38720 :         ATOMIC_DESTROY(&p->exited);
     658       38720 :         free(p);
     659       38720 : }
     660             : 
     661             : static void
     662       38720 : rm_posthread(struct posthread *p)
     663             : {
     664       38720 :         pthread_mutex_lock(&posthread_lock);
     665       38720 :         rm_posthread_locked(p);
     666       38720 :         pthread_mutex_unlock(&posthread_lock);
     667       38720 : }
     668             : 
     669             : static void *
     670       38730 : thread_starter(void *arg)
     671             : {
     672       38730 :         struct posthread *p = (struct posthread *) arg;
     673       38730 :         void *data = p->data;
     674             : 
     675       38730 :         p->data = NULL;
     676       38730 :         pthread_setspecific(threadkey, p);
     677       38730 :         (*p->func)(data);
     678       38725 :         ATOMIC_SET(&p->exited, 1);
     679       38725 :         TRC_DEBUG(THRD, "Exit thread \"%s\"\n", p->threadname);
     680       38725 :         return NULL;
     681             : }
     682             : 
     683             : static void
     684       68695 : join_threads(void)
     685             : {
     686       68695 :         bool waited;
     687             : 
     688       68695 :         struct posthread *self = pthread_getspecific(threadkey);
     689       68695 :         pthread_mutex_lock(&posthread_lock);
     690       76361 :         do {
     691       76361 :                 waited = false;
     692      646572 :                 for (struct posthread *p = posthreads; p; p = p->next) {
     693      577877 :                         if (p->detached && !p->waiting && ATOMIC_GET(&p->exited)) {
     694        7666 :                                 p->waiting = true;
     695        7666 :                                 pthread_mutex_unlock(&posthread_lock);
     696        7666 :                                 TRC_DEBUG(THRD, "Join thread \"%s\"\n", p->threadname);
     697        7666 :                                 self->joinwait = p;
     698        7666 :                                 pthread_join(p->tid, NULL);
     699        7666 :                                 self->joinwait = NULL;
     700        7666 :                                 rm_posthread(p);
     701        7666 :                                 waited = true;
     702        7666 :                                 pthread_mutex_lock(&posthread_lock);
     703        7666 :                                 break;
     704             :                         }
     705             :                 }
     706        7666 :         } while (waited);
     707       68695 :         pthread_mutex_unlock(&posthread_lock);
     708       68695 : }
     709             : 
     710             : void
     711         491 : join_detached_threads(void)
     712             : {
     713         491 :         bool waited;
     714             : 
     715         491 :         struct posthread *self = pthread_getspecific(threadkey);
     716         491 :         pthread_mutex_lock(&posthread_lock);
     717        1580 :         do {
     718        1580 :                 waited = false;
     719        5101 :                 for (struct posthread *p = posthreads; p; p = p->next) {
     720        4610 :                         if (p->detached && !p->waiting) {
     721        1089 :                                 p->waiting = true;
     722        1089 :                                 pthread_mutex_unlock(&posthread_lock);
     723        1089 :                                 TRC_DEBUG(THRD, "Join thread \"%s\"\n", p->threadname);
     724        1089 :                                 self->joinwait = p;
     725        1089 :                                 pthread_join(p->tid, NULL);
     726        1089 :                                 self->joinwait = NULL;
     727        1089 :                                 rm_posthread(p);
     728        1089 :                                 waited = true;
     729        1089 :                                 pthread_mutex_lock(&posthread_lock);
     730        1089 :                                 break;
     731             :                         }
     732             :                 }
     733        1089 :         } while (waited);
     734         491 :         pthread_mutex_unlock(&posthread_lock);
     735         491 : }
     736             : 
     737             : int
     738       38730 : MT_create_thread(MT_Id *t, void (*f) (void *), void *arg, enum MT_thr_detach d, const char *threadname)
     739             : {
     740       38730 :         pthread_attr_t attr;
     741       38730 :         int ret;
     742       38730 :         struct posthread *p;
     743             : 
     744       38730 :         join_threads();
     745       38730 :         if (threadname == NULL) {
     746           0 :                 TRC_CRITICAL(GDK, "Thread must have a name\n");
     747           0 :                 return -1;
     748             :         }
     749       38730 :         if (strlen(threadname) >= sizeof(p->threadname)) {
     750           0 :                 TRC_CRITICAL(GDK, "Thread's name is too large\n");
     751           0 :                 return -1;
     752             :         }
     753       38730 :         if ((ret = pthread_attr_init(&attr)) != 0) {
     754           0 :                 GDKsyserr(ret, "Cannot init pthread attr");
     755           0 :                 return -1;
     756             :         }
     757       38730 :         if ((ret = pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE)) != 0) {
     758           0 :                 GDKsyserr(ret, "Cannot set stack size");
     759           0 :                 pthread_attr_destroy(&attr);
     760           0 :                 return -1;
     761             :         }
     762       38730 :         p = malloc(sizeof(struct posthread));
     763       38730 :         if (p == NULL) {
     764           0 :                 GDKsyserror("Cannot allocate memory\n");
     765           0 :                 pthread_attr_destroy(&attr);
     766           0 :                 return -1;
     767             :         }
     768       38730 :         *p = (struct posthread) {
     769             :                 .func = f,
     770             :                 .data = arg,
     771             :                 .waiting = false,
     772       38730 :                 .detached = (d == MT_THR_DETACHED),
     773             :         };
     774       38730 :         ATOMIC_INIT(&p->exited, 0);
     775             : 
     776       38730 :         strcpy_len(p->threadname, threadname, sizeof(p->threadname));
     777             : #ifdef HAVE_PTHREAD_SIGMASK
     778       38730 :         sigset_t new_mask, orig_mask;
     779       38730 :         (void) sigfillset(&new_mask);
     780       38730 :         MT_thread_sigmask(&new_mask, &orig_mask);
     781             : #endif
     782       38730 :         TRC_DEBUG(THRD, "Create thread \"%s\"\n", threadname);
     783             :         /* protect posthreads during thread creation and only add to
     784             :          * it after the thread was created successfully */
     785       38730 :         pthread_mutex_lock(&posthread_lock);
     786       38730 :         *t = p->mtid = ++MT_thread_id;
     787       38730 :         ret = pthread_create(&p->tid, &attr, thread_starter, p);
     788       38730 :         if (ret != 0) {
     789           0 :                 GDKsyserr(ret, "Cannot start thread");
     790           0 :                 free(p);
     791           0 :                 ret = -1;
     792             :         } else {
     793             :                 /* must not fail after this: the thread has been started */
     794       38730 :                 p->next = posthreads;
     795       38730 :                 posthreads = p;
     796             :         }
     797       38730 :         pthread_mutex_unlock(&posthread_lock);
     798       38730 :         (void) pthread_attr_destroy(&attr); /* not interested in errors */
     799             : #ifdef HAVE_PTHREAD_SIGMASK
     800       38730 :         MT_thread_sigmask(&orig_mask, NULL);
     801             : #endif
     802       38730 :         return ret;
     803             : }
     804             : 
     805             : MT_Id
     806    20394200 : MT_getpid(void)
     807             : {
     808    20394200 :         struct posthread *p;
     809             : 
     810    20394200 :         p = pthread_getspecific(threadkey);
     811    20388400 :         return p ? p->mtid : 0;
     812             : }
     813             : 
     814             : void
     815        4471 : MT_exiting_thread(void)
     816             : {
     817        4471 :         struct posthread *p;
     818             : 
     819        4471 :         p = pthread_getspecific(threadkey);
     820        4471 :         if (p) {
     821        4471 :                 ATOMIC_SET(&p->exited, 1);
     822        4471 :                 p->working = NULL;
     823             :         }
     824        4471 : }
     825             : 
     826             : int
     827       29965 : MT_join_thread(MT_Id t)
     828             : {
     829       29965 :         struct posthread *p;
     830       29965 :         int ret;
     831             : 
     832       29965 :         assert(t > 1);
     833       29965 :         join_threads();
     834       29965 :         p = find_posthread(t);
     835       29965 :         if (p == NULL)
     836             :                 return -1;
     837       29965 :         TRC_DEBUG(THRD, "Join thread \"%s\"\n", p->threadname);
     838       29965 :         struct posthread *self = pthread_getspecific(threadkey);
     839       29965 :         self->joinwait = p;
     840       29965 :         ret = pthread_join(p->tid, NULL);
     841       29965 :         self->joinwait = NULL;
     842       29965 :         if (ret != 0) {
     843           0 :                 GDKsyserr(ret, "Joining thread failed");
     844           0 :                 return -1;
     845             :         }
     846       29965 :         rm_posthread(p);
     847       29965 :         return 0;
     848             : }
     849             : 
     850             : int
     851           0 : MT_kill_thread(MT_Id t)
     852             : {
     853           0 :         assert(t > 1);
     854             : #ifdef HAVE_PTHREAD_KILL
     855           0 :         struct posthread *p;
     856             : 
     857           0 :         join_threads();
     858           0 :         p = find_posthread(t);
     859           0 :         if (p)
     860           0 :                 return pthread_kill(p->tid, SIGHUP);
     861             : #else
     862             :         (void) t;
     863             :         join_threads();
     864             : #endif
     865             :         return -1;
     866             : }
     867             : #endif
     868             : 
     869             : int
     870         251 : MT_check_nr_cores(void)
     871             : {
     872         251 :         int ncpus = -1;
     873             : 
     874             : #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
     875             :         /* this works on Linux, Solaris and AIX */
     876         251 :         ncpus = sysconf(_SC_NPROCESSORS_ONLN);
     877             : #elif defined(HW_NCPU)   /* BSD */
     878             :         size_t len = sizeof(int);
     879             :         int mib[3];
     880             : 
     881             :         /* Everyone should have permission to make this call,
     882             :          * if we get a failure something is really wrong. */
     883             :         mib[0] = CTL_HW;
     884             :         mib[1] = HW_NCPU;
     885             :         mib[2] = -1;
     886             :         sysctl(mib, 3, &ncpus, &len, NULL, 0);
     887             : #elif defined(WIN32)
     888             :         SYSTEM_INFO sysinfo;
     889             : 
     890             :         GetSystemInfo(&sysinfo);
     891             :         ncpus = sysinfo.dwNumberOfProcessors;
     892             : #endif
     893             : 
     894             :         /* if we ever need HPUX or OSF/1 (hope not), see
     895             :          * http://ndevilla.free.fr/threads/ */
     896             : 
     897         251 :         if (ncpus <= 0)
     898             :                 ncpus = 1;
     899             : #if SIZEOF_SIZE_T == SIZEOF_INT
     900             :         /* On 32-bits systems with large numbers of cpus/cores, we
     901             :          * quickly run out of space due to the number of threads in
     902             :          * use.  Since it is questionable whether many cores on a
     903             :          * 32-bits system are going to be beneficial due to this, we
     904             :          * simply limit the auto-detected cores to 16 on 32-bits
     905             :          * systems.  The user can always override this via
     906             :          * gdk_nr_threads. */
     907             :         if (ncpus > 16)
     908             :                 ncpus = 16;
     909             : #endif
     910             : 
     911             : #ifndef WIN32
     912             :         /* get the number of allocated cpus from the cgroup settings */
     913         251 :         FILE *f = fopen("/sys/fs/cgroup/cpuset/cpuset.cpus", "r");
     914         251 :         if (f != NULL) {
     915         251 :                 char buf[512];
     916         251 :                 char *p = fgets(buf, 512, f);
     917         251 :                 fclose(f);
     918         251 :                 if (p != NULL) {
     919             :                         /* syntax is: ranges of CPU numbers separated
     920             :                          * by comma; a range is either a single CPU
     921             :                          * id, or two IDs separated by a minus; any
     922             :                          * deviation causes the file to be ignored */
     923             :                         int ncpu = 0;
     924         251 :                         for (;;) {
     925         251 :                                 char *q;
     926         251 :                                 unsigned fst = strtoul(p, &q, 10);
     927         251 :                                 if (q == p)
     928           0 :                                         return ncpus;
     929         251 :                                 ncpu++;
     930         251 :                                 if (*q == '-') {
     931         251 :                                         p = q + 1;
     932         251 :                                         unsigned lst = strtoul(p, &q, 10);
     933         251 :                                         if (q == p || lst <= fst)
     934             :                                                 return ncpus;
     935         251 :                                         ncpu += lst - fst;
     936             :                                 }
     937         251 :                                 if (*q == '\n')
     938             :                                         break;
     939           0 :                                 if (*q != ',')
     940             :                                         return ncpus;
     941           0 :                                 p = q + 1;
     942             :                         }
     943         251 :                         if (ncpu < ncpus)
     944             :                                 return ncpu;
     945             :                 }
     946             :         }
     947             : #endif
     948             : 
     949             :         return ncpus;
     950             : }

Generated by: LCOV version 1.14