1*ae8c6e27Sflorian /**
2*ae8c6e27Sflorian * util/locks.c - unbound locking primitives
3*ae8c6e27Sflorian *
4*ae8c6e27Sflorian * Copyright (c) 2007, NLnet Labs. All rights reserved.
5*ae8c6e27Sflorian *
6*ae8c6e27Sflorian * This software is open source.
7*ae8c6e27Sflorian *
8*ae8c6e27Sflorian * Redistribution and use in source and binary forms, with or without
9*ae8c6e27Sflorian * modification, are permitted provided that the following conditions
10*ae8c6e27Sflorian * are met:
11*ae8c6e27Sflorian *
12*ae8c6e27Sflorian * Redistributions of source code must retain the above copyright notice,
13*ae8c6e27Sflorian * this list of conditions and the following disclaimer.
14*ae8c6e27Sflorian *
15*ae8c6e27Sflorian * Redistributions in binary form must reproduce the above copyright notice,
16*ae8c6e27Sflorian * this list of conditions and the following disclaimer in the documentation
17*ae8c6e27Sflorian * and/or other materials provided with the distribution.
18*ae8c6e27Sflorian *
19*ae8c6e27Sflorian * Neither the name of the NLNET LABS nor the names of its contributors may
20*ae8c6e27Sflorian * be used to endorse or promote products derived from this software without
21*ae8c6e27Sflorian * specific prior written permission.
22*ae8c6e27Sflorian *
23*ae8c6e27Sflorian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24*ae8c6e27Sflorian * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25*ae8c6e27Sflorian * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26*ae8c6e27Sflorian * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27*ae8c6e27Sflorian * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28*ae8c6e27Sflorian * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29*ae8c6e27Sflorian * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30*ae8c6e27Sflorian * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31*ae8c6e27Sflorian * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32*ae8c6e27Sflorian * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33*ae8c6e27Sflorian * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34*ae8c6e27Sflorian */
35*ae8c6e27Sflorian
36*ae8c6e27Sflorian /**
37*ae8c6e27Sflorian * \file
38*ae8c6e27Sflorian * Implementation of locking and threading support.
39*ae8c6e27Sflorian * A place for locking debug code since most locking functions are macros.
40*ae8c6e27Sflorian */
41*ae8c6e27Sflorian
42*ae8c6e27Sflorian #include "config.h"
43*ae8c6e27Sflorian #include "util/locks.h"
44*ae8c6e27Sflorian #include <signal.h>
45*ae8c6e27Sflorian #ifdef HAVE_SYS_WAIT_H
46*ae8c6e27Sflorian #include <sys/wait.h>
47*ae8c6e27Sflorian #endif
48*ae8c6e27Sflorian
49*ae8c6e27Sflorian /** block all signals, masks them away. */
50*ae8c6e27Sflorian void
ub_thread_blocksigs(void)51*ae8c6e27Sflorian ub_thread_blocksigs(void)
52*ae8c6e27Sflorian {
53*ae8c6e27Sflorian #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
54*ae8c6e27Sflorian # if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
55*ae8c6e27Sflorian int err;
56*ae8c6e27Sflorian # endif
57*ae8c6e27Sflorian sigset_t sigset;
58*ae8c6e27Sflorian sigfillset(&sigset);
59*ae8c6e27Sflorian #ifdef HAVE_PTHREAD
60*ae8c6e27Sflorian if((err=pthread_sigmask(SIG_SETMASK, &sigset, NULL)))
61*ae8c6e27Sflorian fatal_exit("pthread_sigmask: %s", strerror(err));
62*ae8c6e27Sflorian #else
63*ae8c6e27Sflorian # ifdef HAVE_SOLARIS_THREADS
64*ae8c6e27Sflorian if((err=thr_sigsetmask(SIG_SETMASK, &sigset, NULL)))
65*ae8c6e27Sflorian fatal_exit("thr_sigsetmask: %s", strerror(err));
66*ae8c6e27Sflorian # else
67*ae8c6e27Sflorian /* have nothing, do single process signal mask */
68*ae8c6e27Sflorian if(sigprocmask(SIG_SETMASK, &sigset, NULL))
69*ae8c6e27Sflorian fatal_exit("sigprocmask: %s", strerror(errno));
70*ae8c6e27Sflorian # endif /* HAVE_SOLARIS_THREADS */
71*ae8c6e27Sflorian #endif /* HAVE_PTHREAD */
72*ae8c6e27Sflorian #endif /* have signal stuff */
73*ae8c6e27Sflorian }
74*ae8c6e27Sflorian
75*ae8c6e27Sflorian /** unblock one signal, so we can catch it */
ub_thread_sig_unblock(int sig)76*ae8c6e27Sflorian void ub_thread_sig_unblock(int sig)
77*ae8c6e27Sflorian {
78*ae8c6e27Sflorian #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
79*ae8c6e27Sflorian # if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
80*ae8c6e27Sflorian int err;
81*ae8c6e27Sflorian # endif
82*ae8c6e27Sflorian sigset_t sigset;
83*ae8c6e27Sflorian sigemptyset(&sigset);
84*ae8c6e27Sflorian sigaddset(&sigset, sig);
85*ae8c6e27Sflorian #ifdef HAVE_PTHREAD
86*ae8c6e27Sflorian if((err=pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)))
87*ae8c6e27Sflorian fatal_exit("pthread_sigmask: %s", strerror(err));
88*ae8c6e27Sflorian #else
89*ae8c6e27Sflorian # ifdef HAVE_SOLARIS_THREADS
90*ae8c6e27Sflorian if((err=thr_sigsetmask(SIG_UNBLOCK, &sigset, NULL)))
91*ae8c6e27Sflorian fatal_exit("thr_sigsetmask: %s", strerror(err));
92*ae8c6e27Sflorian # else
93*ae8c6e27Sflorian /* have nothing, do single thread case */
94*ae8c6e27Sflorian if(sigprocmask(SIG_UNBLOCK, &sigset, NULL))
95*ae8c6e27Sflorian fatal_exit("sigprocmask: %s", strerror(errno));
96*ae8c6e27Sflorian # endif /* HAVE_SOLARIS_THREADS */
97*ae8c6e27Sflorian #endif /* HAVE_PTHREAD */
98*ae8c6e27Sflorian #else
99*ae8c6e27Sflorian (void)sig;
100*ae8c6e27Sflorian #endif /* have signal stuff */
101*ae8c6e27Sflorian }
102*ae8c6e27Sflorian
103*ae8c6e27Sflorian #if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS)
104*ae8c6e27Sflorian /**
105*ae8c6e27Sflorian * No threading available: fork a new process.
106*ae8c6e27Sflorian * This means no shared data structure, and no locking.
107*ae8c6e27Sflorian * Only the main thread ever returns. Exits on errors.
108*ae8c6e27Sflorian * @param thr: the location where to store the thread-id.
109*ae8c6e27Sflorian * @param func: function body of the thread. Return value of func is lost.
110*ae8c6e27Sflorian * @param arg: user argument to func.
111*ae8c6e27Sflorian */
112*ae8c6e27Sflorian void
ub_thr_fork_create(ub_thread_type * thr,void * (* func)(void *),void * arg)113*ae8c6e27Sflorian ub_thr_fork_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
114*ae8c6e27Sflorian {
115*ae8c6e27Sflorian pid_t pid = fork();
116*ae8c6e27Sflorian switch(pid) {
117*ae8c6e27Sflorian default: /* main */
118*ae8c6e27Sflorian *thr = (ub_thread_type)pid;
119*ae8c6e27Sflorian return;
120*ae8c6e27Sflorian case 0: /* child */
121*ae8c6e27Sflorian *thr = (ub_thread_type)getpid();
122*ae8c6e27Sflorian (void)(*func)(arg);
123*ae8c6e27Sflorian exit(0);
124*ae8c6e27Sflorian case -1: /* error */
125*ae8c6e27Sflorian fatal_exit("could not fork: %s", strerror(errno));
126*ae8c6e27Sflorian }
127*ae8c6e27Sflorian }
128*ae8c6e27Sflorian
129*ae8c6e27Sflorian /**
130*ae8c6e27Sflorian * There is no threading. Wait for a process to terminate.
131*ae8c6e27Sflorian * Note that ub_thread_type is defined as pid_t.
132*ae8c6e27Sflorian * @param thread: the process id to wait for.
133*ae8c6e27Sflorian */
ub_thr_fork_wait(ub_thread_type thread)134*ae8c6e27Sflorian void ub_thr_fork_wait(ub_thread_type thread)
135*ae8c6e27Sflorian {
136*ae8c6e27Sflorian int status = 0;
137*ae8c6e27Sflorian if(waitpid((pid_t)thread, &status, 0) == -1)
138*ae8c6e27Sflorian log_err("waitpid(%d): %s", (int)thread, strerror(errno));
139*ae8c6e27Sflorian if(status != 0)
140*ae8c6e27Sflorian log_warn("process %d abnormal exit with status %d",
141*ae8c6e27Sflorian (int)thread, status);
142*ae8c6e27Sflorian }
143*ae8c6e27Sflorian #endif /* !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS) */
144*ae8c6e27Sflorian
145*ae8c6e27Sflorian #ifdef HAVE_SOLARIS_THREADS
ub_thread_key_get(ub_thread_key_type key)146*ae8c6e27Sflorian void* ub_thread_key_get(ub_thread_key_type key)
147*ae8c6e27Sflorian {
148*ae8c6e27Sflorian void* ret=NULL;
149*ae8c6e27Sflorian LOCKRET(thr_getspecific(key, &ret));
150*ae8c6e27Sflorian return ret;
151*ae8c6e27Sflorian }
152*ae8c6e27Sflorian #endif
153*ae8c6e27Sflorian
154*ae8c6e27Sflorian #ifdef HAVE_WINDOWS_THREADS
155*ae8c6e27Sflorian /** log a windows GetLastError message */
log_win_err(const char * str,DWORD err)156*ae8c6e27Sflorian static void log_win_err(const char* str, DWORD err)
157*ae8c6e27Sflorian {
158*ae8c6e27Sflorian LPTSTR buf;
159*ae8c6e27Sflorian if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
160*ae8c6e27Sflorian FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
161*ae8c6e27Sflorian NULL, err, 0, (LPTSTR)&buf, 0, NULL) == 0) {
162*ae8c6e27Sflorian /* could not format error message */
163*ae8c6e27Sflorian log_err("%s, GetLastError=%d", str, (int)err);
164*ae8c6e27Sflorian return;
165*ae8c6e27Sflorian }
166*ae8c6e27Sflorian log_err("%s, (err=%d): %s", str, (int)err, buf);
167*ae8c6e27Sflorian LocalFree(buf);
168*ae8c6e27Sflorian }
169*ae8c6e27Sflorian
lock_basic_init(lock_basic_type * lock)170*ae8c6e27Sflorian void lock_basic_init(lock_basic_type* lock)
171*ae8c6e27Sflorian {
172*ae8c6e27Sflorian /* implement own lock, because windows HANDLE as Mutex usage
173*ae8c6e27Sflorian * uses too many handles and would bog down the whole system. */
174*ae8c6e27Sflorian (void)InterlockedExchange(lock, 0);
175*ae8c6e27Sflorian }
176*ae8c6e27Sflorian
lock_basic_destroy(lock_basic_type * lock)177*ae8c6e27Sflorian void lock_basic_destroy(lock_basic_type* lock)
178*ae8c6e27Sflorian {
179*ae8c6e27Sflorian (void)InterlockedExchange(lock, 0);
180*ae8c6e27Sflorian }
181*ae8c6e27Sflorian
lock_basic_lock(lock_basic_type * lock)182*ae8c6e27Sflorian void lock_basic_lock(lock_basic_type* lock)
183*ae8c6e27Sflorian {
184*ae8c6e27Sflorian LONG wait = 1; /* wait 1 msec at first */
185*ae8c6e27Sflorian
186*ae8c6e27Sflorian while(InterlockedExchange(lock, 1)) {
187*ae8c6e27Sflorian /* if the old value was 1 then if was already locked */
188*ae8c6e27Sflorian Sleep(wait); /* wait with sleep */
189*ae8c6e27Sflorian wait *= 2; /* exponential backoff for waiting */
190*ae8c6e27Sflorian }
191*ae8c6e27Sflorian /* the old value was 0, but we inserted 1, we locked it! */
192*ae8c6e27Sflorian }
193*ae8c6e27Sflorian
lock_basic_unlock(lock_basic_type * lock)194*ae8c6e27Sflorian void lock_basic_unlock(lock_basic_type* lock)
195*ae8c6e27Sflorian {
196*ae8c6e27Sflorian /* unlock it by inserting the value of 0. xchg for cache coherency. */
197*ae8c6e27Sflorian (void)InterlockedExchange(lock, 0);
198*ae8c6e27Sflorian }
199*ae8c6e27Sflorian
ub_thread_key_create(ub_thread_key_type * key,void * f)200*ae8c6e27Sflorian void ub_thread_key_create(ub_thread_key_type* key, void* f)
201*ae8c6e27Sflorian {
202*ae8c6e27Sflorian *key = TlsAlloc();
203*ae8c6e27Sflorian if(*key == TLS_OUT_OF_INDEXES) {
204*ae8c6e27Sflorian *key = 0;
205*ae8c6e27Sflorian log_win_err("TlsAlloc Failed(OUT_OF_INDEXES)", GetLastError());
206*ae8c6e27Sflorian }
207*ae8c6e27Sflorian else ub_thread_key_set(*key, f);
208*ae8c6e27Sflorian }
209*ae8c6e27Sflorian
ub_thread_key_set(ub_thread_key_type key,void * v)210*ae8c6e27Sflorian void ub_thread_key_set(ub_thread_key_type key, void* v)
211*ae8c6e27Sflorian {
212*ae8c6e27Sflorian if(!TlsSetValue(key, v)) {
213*ae8c6e27Sflorian log_win_err("TlsSetValue failed", GetLastError());
214*ae8c6e27Sflorian }
215*ae8c6e27Sflorian }
216*ae8c6e27Sflorian
ub_thread_key_get(ub_thread_key_type key)217*ae8c6e27Sflorian void* ub_thread_key_get(ub_thread_key_type key)
218*ae8c6e27Sflorian {
219*ae8c6e27Sflorian void* ret = (void*)TlsGetValue(key);
220*ae8c6e27Sflorian if(ret == NULL && GetLastError() != ERROR_SUCCESS) {
221*ae8c6e27Sflorian log_win_err("TlsGetValue failed", GetLastError());
222*ae8c6e27Sflorian }
223*ae8c6e27Sflorian return ret;
224*ae8c6e27Sflorian }
225*ae8c6e27Sflorian
ub_thread_create(ub_thread_type * thr,void * (* func)(void *),void * arg)226*ae8c6e27Sflorian void ub_thread_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
227*ae8c6e27Sflorian {
228*ae8c6e27Sflorian #ifndef HAVE__BEGINTHREADEX
229*ae8c6e27Sflorian *thr = CreateThread(NULL, /* default security (no inherit handle) */
230*ae8c6e27Sflorian 0, /* default stack size */
231*ae8c6e27Sflorian (LPTHREAD_START_ROUTINE)func, arg,
232*ae8c6e27Sflorian 0, /* default flags, run immediately */
233*ae8c6e27Sflorian NULL); /* do not store thread identifier anywhere */
234*ae8c6e27Sflorian #else
235*ae8c6e27Sflorian /* the beginthreadex routine setups for the C lib; aligns stack */
236*ae8c6e27Sflorian *thr=(ub_thread_type)_beginthreadex(NULL, 0, (void*)func, arg, 0, NULL);
237*ae8c6e27Sflorian #endif
238*ae8c6e27Sflorian if(*thr == NULL) {
239*ae8c6e27Sflorian log_win_err("CreateThread failed", GetLastError());
240*ae8c6e27Sflorian fatal_exit("thread create failed");
241*ae8c6e27Sflorian }
242*ae8c6e27Sflorian }
243*ae8c6e27Sflorian
ub_thread_self(void)244*ae8c6e27Sflorian ub_thread_type ub_thread_self(void)
245*ae8c6e27Sflorian {
246*ae8c6e27Sflorian return GetCurrentThread();
247*ae8c6e27Sflorian }
248*ae8c6e27Sflorian
ub_thread_join(ub_thread_type thr)249*ae8c6e27Sflorian void ub_thread_join(ub_thread_type thr)
250*ae8c6e27Sflorian {
251*ae8c6e27Sflorian DWORD ret = WaitForSingleObject(thr, INFINITE);
252*ae8c6e27Sflorian if(ret == WAIT_FAILED) {
253*ae8c6e27Sflorian log_win_err("WaitForSingleObject(Thread):WAIT_FAILED",
254*ae8c6e27Sflorian GetLastError());
255*ae8c6e27Sflorian } else if(ret == WAIT_TIMEOUT) {
256*ae8c6e27Sflorian log_win_err("WaitForSingleObject(Thread):WAIT_TIMEOUT",
257*ae8c6e27Sflorian GetLastError());
258*ae8c6e27Sflorian }
259*ae8c6e27Sflorian /* and close the handle to the thread */
260*ae8c6e27Sflorian if(!CloseHandle(thr)) {
261*ae8c6e27Sflorian log_win_err("CloseHandle(Thread) failed", GetLastError());
262*ae8c6e27Sflorian }
263*ae8c6e27Sflorian }
264*ae8c6e27Sflorian #endif /* HAVE_WINDOWS_THREADS */
265