13b6c3722Schristos /**
23b6c3722Schristos * util/locks.c - unbound locking primitives
33b6c3722Schristos *
43b6c3722Schristos * Copyright (c) 2007, NLnet Labs. All rights reserved.
53b6c3722Schristos *
63b6c3722Schristos * This software is open source.
73b6c3722Schristos *
83b6c3722Schristos * Redistribution and use in source and binary forms, with or without
93b6c3722Schristos * modification, are permitted provided that the following conditions
103b6c3722Schristos * are met:
113b6c3722Schristos *
123b6c3722Schristos * Redistributions of source code must retain the above copyright notice,
133b6c3722Schristos * this list of conditions and the following disclaimer.
143b6c3722Schristos *
153b6c3722Schristos * Redistributions in binary form must reproduce the above copyright notice,
163b6c3722Schristos * this list of conditions and the following disclaimer in the documentation
173b6c3722Schristos * and/or other materials provided with the distribution.
183b6c3722Schristos *
193b6c3722Schristos * Neither the name of the NLNET LABS nor the names of its contributors may
203b6c3722Schristos * be used to endorse or promote products derived from this software without
213b6c3722Schristos * specific prior written permission.
223b6c3722Schristos *
233b6c3722Schristos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
243b6c3722Schristos * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
253b6c3722Schristos * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
263b6c3722Schristos * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
273b6c3722Schristos * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
283b6c3722Schristos * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
293b6c3722Schristos * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
303b6c3722Schristos * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
313b6c3722Schristos * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
323b6c3722Schristos * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
333b6c3722Schristos * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
343b6c3722Schristos */
353b6c3722Schristos
363b6c3722Schristos /**
373b6c3722Schristos * \file
383b6c3722Schristos * Implementation of locking and threading support.
393b6c3722Schristos * A place for locking debug code since most locking functions are macros.
403b6c3722Schristos */
413b6c3722Schristos
423b6c3722Schristos #include "config.h"
433b6c3722Schristos #include "util/locks.h"
443b6c3722Schristos #include <signal.h>
453b6c3722Schristos #ifdef HAVE_SYS_WAIT_H
463b6c3722Schristos #include <sys/wait.h>
473b6c3722Schristos #endif
483b6c3722Schristos
493b6c3722Schristos /** block all signals, masks them away. */
503b6c3722Schristos void
ub_thread_blocksigs(void)513b6c3722Schristos ub_thread_blocksigs(void)
523b6c3722Schristos {
533b6c3722Schristos #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
543b6c3722Schristos # if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
553b6c3722Schristos int err;
563b6c3722Schristos # endif
573b6c3722Schristos sigset_t sigset;
583b6c3722Schristos sigfillset(&sigset);
593b6c3722Schristos #ifdef HAVE_PTHREAD
603b6c3722Schristos if((err=pthread_sigmask(SIG_SETMASK, &sigset, NULL)))
613b6c3722Schristos fatal_exit("pthread_sigmask: %s", strerror(err));
623b6c3722Schristos #else
633b6c3722Schristos # ifdef HAVE_SOLARIS_THREADS
643b6c3722Schristos if((err=thr_sigsetmask(SIG_SETMASK, &sigset, NULL)))
653b6c3722Schristos fatal_exit("thr_sigsetmask: %s", strerror(err));
663b6c3722Schristos # else
673b6c3722Schristos /* have nothing, do single process signal mask */
683b6c3722Schristos if(sigprocmask(SIG_SETMASK, &sigset, NULL))
693b6c3722Schristos fatal_exit("sigprocmask: %s", strerror(errno));
703b6c3722Schristos # endif /* HAVE_SOLARIS_THREADS */
713b6c3722Schristos #endif /* HAVE_PTHREAD */
723b6c3722Schristos #endif /* have signal stuff */
733b6c3722Schristos }
743b6c3722Schristos
753b6c3722Schristos /** unblock one signal, so we can catch it */
ub_thread_sig_unblock(int sig)763b6c3722Schristos void ub_thread_sig_unblock(int sig)
773b6c3722Schristos {
783b6c3722Schristos #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
793b6c3722Schristos # if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
803b6c3722Schristos int err;
813b6c3722Schristos # endif
823b6c3722Schristos sigset_t sigset;
833b6c3722Schristos sigemptyset(&sigset);
843b6c3722Schristos sigaddset(&sigset, sig);
853b6c3722Schristos #ifdef HAVE_PTHREAD
863b6c3722Schristos if((err=pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)))
873b6c3722Schristos fatal_exit("pthread_sigmask: %s", strerror(err));
883b6c3722Schristos #else
893b6c3722Schristos # ifdef HAVE_SOLARIS_THREADS
903b6c3722Schristos if((err=thr_sigsetmask(SIG_UNBLOCK, &sigset, NULL)))
913b6c3722Schristos fatal_exit("thr_sigsetmask: %s", strerror(err));
923b6c3722Schristos # else
933b6c3722Schristos /* have nothing, do single thread case */
943b6c3722Schristos if(sigprocmask(SIG_UNBLOCK, &sigset, NULL))
953b6c3722Schristos fatal_exit("sigprocmask: %s", strerror(errno));
963b6c3722Schristos # endif /* HAVE_SOLARIS_THREADS */
973b6c3722Schristos #endif /* HAVE_PTHREAD */
983b6c3722Schristos #else
993b6c3722Schristos (void)sig;
1003b6c3722Schristos #endif /* have signal stuff */
1013b6c3722Schristos }
1023b6c3722Schristos
1033b6c3722Schristos #if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS)
1043b6c3722Schristos /**
1053b6c3722Schristos * No threading available: fork a new process.
1063b6c3722Schristos * This means no shared data structure, and no locking.
1073b6c3722Schristos * Only the main thread ever returns. Exits on errors.
1083b6c3722Schristos * @param thr: the location where to store the thread-id.
1093b6c3722Schristos * @param func: function body of the thread. Return value of func is lost.
1103b6c3722Schristos * @param arg: user argument to func.
1113b6c3722Schristos */
1123b6c3722Schristos void
ub_thr_fork_create(ub_thread_type * thr,void * (* func)(void *),void * arg)113*0cd9f4ecSchristos ub_thr_fork_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
1143b6c3722Schristos {
1153b6c3722Schristos pid_t pid = fork();
1163b6c3722Schristos switch(pid) {
1173b6c3722Schristos default: /* main */
118*0cd9f4ecSchristos *thr = (ub_thread_type)pid;
1193b6c3722Schristos return;
1203b6c3722Schristos case 0: /* child */
121*0cd9f4ecSchristos *thr = (ub_thread_type)getpid();
1223b6c3722Schristos (void)(*func)(arg);
1233b6c3722Schristos exit(0);
1243b6c3722Schristos case -1: /* error */
1253b6c3722Schristos fatal_exit("could not fork: %s", strerror(errno));
1263b6c3722Schristos }
1273b6c3722Schristos }
1283b6c3722Schristos
1293b6c3722Schristos /**
1303b6c3722Schristos * There is no threading. Wait for a process to terminate.
131*0cd9f4ecSchristos * Note that ub_thread_type is defined as pid_t.
1323b6c3722Schristos * @param thread: the process id to wait for.
1333b6c3722Schristos */
ub_thr_fork_wait(ub_thread_type thread)134*0cd9f4ecSchristos void ub_thr_fork_wait(ub_thread_type thread)
1353b6c3722Schristos {
1363b6c3722Schristos int status = 0;
1373b6c3722Schristos if(waitpid((pid_t)thread, &status, 0) == -1)
1383b6c3722Schristos log_err("waitpid(%d): %s", (int)thread, strerror(errno));
1393b6c3722Schristos if(status != 0)
1403b6c3722Schristos log_warn("process %d abnormal exit with status %d",
1413b6c3722Schristos (int)thread, status);
1423b6c3722Schristos }
1433b6c3722Schristos #endif /* !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS) */
1443b6c3722Schristos
1453b6c3722Schristos #ifdef HAVE_SOLARIS_THREADS
ub_thread_key_get(ub_thread_key_type key)146*0cd9f4ecSchristos void* ub_thread_key_get(ub_thread_key_type key)
1473b6c3722Schristos {
1483b6c3722Schristos void* ret=NULL;
1493b6c3722Schristos LOCKRET(thr_getspecific(key, &ret));
1503b6c3722Schristos return ret;
1513b6c3722Schristos }
1523b6c3722Schristos #endif
1533b6c3722Schristos
1543b6c3722Schristos #ifdef HAVE_WINDOWS_THREADS
1553b6c3722Schristos /** log a windows GetLastError message */
log_win_err(const char * str,DWORD err)1563b6c3722Schristos static void log_win_err(const char* str, DWORD err)
1573b6c3722Schristos {
1583b6c3722Schristos LPTSTR buf;
1593b6c3722Schristos if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1603b6c3722Schristos FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
1613b6c3722Schristos NULL, err, 0, (LPTSTR)&buf, 0, NULL) == 0) {
1623b6c3722Schristos /* could not format error message */
1633b6c3722Schristos log_err("%s, GetLastError=%d", str, (int)err);
1643b6c3722Schristos return;
1653b6c3722Schristos }
1663b6c3722Schristos log_err("%s, (err=%d): %s", str, (int)err, buf);
1673b6c3722Schristos LocalFree(buf);
1683b6c3722Schristos }
1693b6c3722Schristos
lock_basic_init(lock_basic_type * lock)170*0cd9f4ecSchristos void lock_basic_init(lock_basic_type* lock)
1713b6c3722Schristos {
1723b6c3722Schristos /* implement own lock, because windows HANDLE as Mutex usage
1733b6c3722Schristos * uses too many handles and would bog down the whole system. */
1743b6c3722Schristos (void)InterlockedExchange(lock, 0);
1753b6c3722Schristos }
1763b6c3722Schristos
lock_basic_destroy(lock_basic_type * lock)177*0cd9f4ecSchristos void lock_basic_destroy(lock_basic_type* lock)
1783b6c3722Schristos {
1793b6c3722Schristos (void)InterlockedExchange(lock, 0);
1803b6c3722Schristos }
1813b6c3722Schristos
lock_basic_lock(lock_basic_type * lock)182*0cd9f4ecSchristos void lock_basic_lock(lock_basic_type* lock)
1833b6c3722Schristos {
1843b6c3722Schristos LONG wait = 1; /* wait 1 msec at first */
1853b6c3722Schristos
1863b6c3722Schristos while(InterlockedExchange(lock, 1)) {
1873b6c3722Schristos /* if the old value was 1 then if was already locked */
1883b6c3722Schristos Sleep(wait); /* wait with sleep */
1893b6c3722Schristos wait *= 2; /* exponential backoff for waiting */
1903b6c3722Schristos }
1913b6c3722Schristos /* the old value was 0, but we inserted 1, we locked it! */
1923b6c3722Schristos }
1933b6c3722Schristos
lock_basic_unlock(lock_basic_type * lock)194*0cd9f4ecSchristos void lock_basic_unlock(lock_basic_type* lock)
1953b6c3722Schristos {
1963b6c3722Schristos /* unlock it by inserting the value of 0. xchg for cache coherency. */
1973b6c3722Schristos (void)InterlockedExchange(lock, 0);
1983b6c3722Schristos }
1993b6c3722Schristos
ub_thread_key_create(ub_thread_key_type * key,void * f)200*0cd9f4ecSchristos void ub_thread_key_create(ub_thread_key_type* key, void* f)
2013b6c3722Schristos {
2023b6c3722Schristos *key = TlsAlloc();
2033b6c3722Schristos if(*key == TLS_OUT_OF_INDEXES) {
2043b6c3722Schristos *key = 0;
2053b6c3722Schristos log_win_err("TlsAlloc Failed(OUT_OF_INDEXES)", GetLastError());
2063b6c3722Schristos }
2073b6c3722Schristos else ub_thread_key_set(*key, f);
2083b6c3722Schristos }
2093b6c3722Schristos
ub_thread_key_set(ub_thread_key_type key,void * v)210*0cd9f4ecSchristos void ub_thread_key_set(ub_thread_key_type key, void* v)
2113b6c3722Schristos {
2123b6c3722Schristos if(!TlsSetValue(key, v)) {
2133b6c3722Schristos log_win_err("TlsSetValue failed", GetLastError());
2143b6c3722Schristos }
2153b6c3722Schristos }
2163b6c3722Schristos
ub_thread_key_get(ub_thread_key_type key)217*0cd9f4ecSchristos void* ub_thread_key_get(ub_thread_key_type key)
2183b6c3722Schristos {
2193b6c3722Schristos void* ret = (void*)TlsGetValue(key);
2203b6c3722Schristos if(ret == NULL && GetLastError() != ERROR_SUCCESS) {
2213b6c3722Schristos log_win_err("TlsGetValue failed", GetLastError());
2223b6c3722Schristos }
2233b6c3722Schristos return ret;
2243b6c3722Schristos }
2253b6c3722Schristos
ub_thread_create(ub_thread_type * thr,void * (* func)(void *),void * arg)226*0cd9f4ecSchristos void ub_thread_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
2273b6c3722Schristos {
2283b6c3722Schristos #ifndef HAVE__BEGINTHREADEX
2293b6c3722Schristos *thr = CreateThread(NULL, /* default security (no inherit handle) */
2303b6c3722Schristos 0, /* default stack size */
2313b6c3722Schristos (LPTHREAD_START_ROUTINE)func, arg,
2323b6c3722Schristos 0, /* default flags, run immediately */
2333b6c3722Schristos NULL); /* do not store thread identifier anywhere */
2343b6c3722Schristos #else
2353b6c3722Schristos /* the beginthreadex routine setups for the C lib; aligns stack */
236*0cd9f4ecSchristos *thr=(ub_thread_type)_beginthreadex(NULL, 0, (void*)func, arg, 0, NULL);
2373b6c3722Schristos #endif
2383b6c3722Schristos if(*thr == NULL) {
2393b6c3722Schristos log_win_err("CreateThread failed", GetLastError());
2403b6c3722Schristos fatal_exit("thread create failed");
2413b6c3722Schristos }
2423b6c3722Schristos }
2433b6c3722Schristos
ub_thread_self(void)244*0cd9f4ecSchristos ub_thread_type ub_thread_self(void)
2453b6c3722Schristos {
2463b6c3722Schristos return GetCurrentThread();
2473b6c3722Schristos }
2483b6c3722Schristos
ub_thread_join(ub_thread_type thr)249*0cd9f4ecSchristos void ub_thread_join(ub_thread_type thr)
2503b6c3722Schristos {
2513b6c3722Schristos DWORD ret = WaitForSingleObject(thr, INFINITE);
2523b6c3722Schristos if(ret == WAIT_FAILED) {
2533b6c3722Schristos log_win_err("WaitForSingleObject(Thread):WAIT_FAILED",
2543b6c3722Schristos GetLastError());
2553b6c3722Schristos } else if(ret == WAIT_TIMEOUT) {
2563b6c3722Schristos log_win_err("WaitForSingleObject(Thread):WAIT_TIMEOUT",
2573b6c3722Schristos GetLastError());
2583b6c3722Schristos }
2593b6c3722Schristos /* and close the handle to the thread */
2603b6c3722Schristos if(!CloseHandle(thr)) {
2613b6c3722Schristos log_win_err("CloseHandle(Thread) failed", GetLastError());
2623b6c3722Schristos }
2633b6c3722Schristos }
2643b6c3722Schristos #endif /* HAVE_WINDOWS_THREADS */
265