1933707f3Ssthen /**
2933707f3Ssthen * util/locks.c - unbound locking primitives
3933707f3Ssthen *
4933707f3Ssthen * Copyright (c) 2007, NLnet Labs. All rights reserved.
5933707f3Ssthen *
6933707f3Ssthen * This software is open source.
7933707f3Ssthen *
8933707f3Ssthen * Redistribution and use in source and binary forms, with or without
9933707f3Ssthen * modification, are permitted provided that the following conditions
10933707f3Ssthen * are met:
11933707f3Ssthen *
12933707f3Ssthen * Redistributions of source code must retain the above copyright notice,
13933707f3Ssthen * this list of conditions and the following disclaimer.
14933707f3Ssthen *
15933707f3Ssthen * Redistributions in binary form must reproduce the above copyright notice,
16933707f3Ssthen * this list of conditions and the following disclaimer in the documentation
17933707f3Ssthen * and/or other materials provided with the distribution.
18933707f3Ssthen *
19933707f3Ssthen * Neither the name of the NLNET LABS nor the names of its contributors may
20933707f3Ssthen * be used to endorse or promote products derived from this software without
21933707f3Ssthen * specific prior written permission.
22933707f3Ssthen *
23933707f3Ssthen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
245d76a658Ssthen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
255d76a658Ssthen * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
265d76a658Ssthen * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
275d76a658Ssthen * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
285d76a658Ssthen * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
295d76a658Ssthen * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
305d76a658Ssthen * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
315d76a658Ssthen * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
325d76a658Ssthen * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
335d76a658Ssthen * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34933707f3Ssthen */
35933707f3Ssthen
36933707f3Ssthen /**
37933707f3Ssthen * \file
38933707f3Ssthen * Implementation of locking and threading support.
39933707f3Ssthen * A place for locking debug code since most locking functions are macros.
40933707f3Ssthen */
41933707f3Ssthen
42933707f3Ssthen #include "config.h"
43933707f3Ssthen #include "util/locks.h"
44933707f3Ssthen #include <signal.h>
45933707f3Ssthen #ifdef HAVE_SYS_WAIT_H
46933707f3Ssthen #include <sys/wait.h>
47933707f3Ssthen #endif
48933707f3Ssthen
49933707f3Ssthen /** block all signals, masks them away. */
50933707f3Ssthen void
ub_thread_blocksigs(void)51933707f3Ssthen ub_thread_blocksigs(void)
52933707f3Ssthen {
53933707f3Ssthen #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
54933707f3Ssthen # if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
55933707f3Ssthen int err;
56933707f3Ssthen # endif
57933707f3Ssthen sigset_t sigset;
58933707f3Ssthen sigfillset(&sigset);
59933707f3Ssthen #ifdef HAVE_PTHREAD
60933707f3Ssthen if((err=pthread_sigmask(SIG_SETMASK, &sigset, NULL)))
61933707f3Ssthen fatal_exit("pthread_sigmask: %s", strerror(err));
62933707f3Ssthen #else
63933707f3Ssthen # ifdef HAVE_SOLARIS_THREADS
64933707f3Ssthen if((err=thr_sigsetmask(SIG_SETMASK, &sigset, NULL)))
65933707f3Ssthen fatal_exit("thr_sigsetmask: %s", strerror(err));
66933707f3Ssthen # else
67933707f3Ssthen /* have nothing, do single process signal mask */
68933707f3Ssthen if(sigprocmask(SIG_SETMASK, &sigset, NULL))
69933707f3Ssthen fatal_exit("sigprocmask: %s", strerror(errno));
70933707f3Ssthen # endif /* HAVE_SOLARIS_THREADS */
71933707f3Ssthen #endif /* HAVE_PTHREAD */
72933707f3Ssthen #endif /* have signal stuff */
73933707f3Ssthen }
74933707f3Ssthen
75933707f3Ssthen /** unblock one signal, so we can catch it */
ub_thread_sig_unblock(int sig)76933707f3Ssthen void ub_thread_sig_unblock(int sig)
77933707f3Ssthen {
78933707f3Ssthen #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
79933707f3Ssthen # if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
80933707f3Ssthen int err;
81933707f3Ssthen # endif
82933707f3Ssthen sigset_t sigset;
83933707f3Ssthen sigemptyset(&sigset);
84933707f3Ssthen sigaddset(&sigset, sig);
85933707f3Ssthen #ifdef HAVE_PTHREAD
86933707f3Ssthen if((err=pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)))
87933707f3Ssthen fatal_exit("pthread_sigmask: %s", strerror(err));
88933707f3Ssthen #else
89933707f3Ssthen # ifdef HAVE_SOLARIS_THREADS
90933707f3Ssthen if((err=thr_sigsetmask(SIG_UNBLOCK, &sigset, NULL)))
91933707f3Ssthen fatal_exit("thr_sigsetmask: %s", strerror(err));
92933707f3Ssthen # else
93933707f3Ssthen /* have nothing, do single thread case */
94933707f3Ssthen if(sigprocmask(SIG_UNBLOCK, &sigset, NULL))
95933707f3Ssthen fatal_exit("sigprocmask: %s", strerror(errno));
96933707f3Ssthen # endif /* HAVE_SOLARIS_THREADS */
97933707f3Ssthen #endif /* HAVE_PTHREAD */
98933707f3Ssthen #else
99933707f3Ssthen (void)sig;
100933707f3Ssthen #endif /* have signal stuff */
101933707f3Ssthen }
102933707f3Ssthen
103933707f3Ssthen #if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS)
104933707f3Ssthen /**
105933707f3Ssthen * No threading available: fork a new process.
106933707f3Ssthen * This means no shared data structure, and no locking.
107933707f3Ssthen * Only the main thread ever returns. Exits on errors.
108933707f3Ssthen * @param thr: the location where to store the thread-id.
109933707f3Ssthen * @param func: function body of the thread. Return value of func is lost.
110933707f3Ssthen * @param arg: user argument to func.
111933707f3Ssthen */
112933707f3Ssthen void
ub_thr_fork_create(ub_thread_type * thr,void * (* func)(void *),void * arg)113*77079be7Ssthen ub_thr_fork_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
114933707f3Ssthen {
115933707f3Ssthen pid_t pid = fork();
116933707f3Ssthen switch(pid) {
117933707f3Ssthen default: /* main */
118*77079be7Ssthen *thr = (ub_thread_type)pid;
119933707f3Ssthen return;
120933707f3Ssthen case 0: /* child */
121*77079be7Ssthen *thr = (ub_thread_type)getpid();
122933707f3Ssthen (void)(*func)(arg);
123933707f3Ssthen exit(0);
124933707f3Ssthen case -1: /* error */
125933707f3Ssthen fatal_exit("could not fork: %s", strerror(errno));
126933707f3Ssthen }
127933707f3Ssthen }
128933707f3Ssthen
129933707f3Ssthen /**
130933707f3Ssthen * There is no threading. Wait for a process to terminate.
131*77079be7Ssthen * Note that ub_thread_type is defined as pid_t.
132933707f3Ssthen * @param thread: the process id to wait for.
133933707f3Ssthen */
ub_thr_fork_wait(ub_thread_type thread)134*77079be7Ssthen void ub_thr_fork_wait(ub_thread_type thread)
135933707f3Ssthen {
136933707f3Ssthen int status = 0;
137933707f3Ssthen if(waitpid((pid_t)thread, &status, 0) == -1)
138933707f3Ssthen log_err("waitpid(%d): %s", (int)thread, strerror(errno));
139933707f3Ssthen if(status != 0)
140933707f3Ssthen log_warn("process %d abnormal exit with status %d",
141933707f3Ssthen (int)thread, status);
142933707f3Ssthen }
143933707f3Ssthen #endif /* !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS) */
144933707f3Ssthen
145933707f3Ssthen #ifdef HAVE_SOLARIS_THREADS
ub_thread_key_get(ub_thread_key_type key)146*77079be7Ssthen void* ub_thread_key_get(ub_thread_key_type key)
147933707f3Ssthen {
148933707f3Ssthen void* ret=NULL;
149933707f3Ssthen LOCKRET(thr_getspecific(key, &ret));
150933707f3Ssthen return ret;
151933707f3Ssthen }
152933707f3Ssthen #endif
153933707f3Ssthen
154933707f3Ssthen #ifdef HAVE_WINDOWS_THREADS
155933707f3Ssthen /** log a windows GetLastError message */
log_win_err(const char * str,DWORD err)156933707f3Ssthen static void log_win_err(const char* str, DWORD err)
157933707f3Ssthen {
158933707f3Ssthen LPTSTR buf;
159933707f3Ssthen if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
160933707f3Ssthen FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
161933707f3Ssthen NULL, err, 0, (LPTSTR)&buf, 0, NULL) == 0) {
162933707f3Ssthen /* could not format error message */
163933707f3Ssthen log_err("%s, GetLastError=%d", str, (int)err);
164933707f3Ssthen return;
165933707f3Ssthen }
166933707f3Ssthen log_err("%s, (err=%d): %s", str, (int)err, buf);
167933707f3Ssthen LocalFree(buf);
168933707f3Ssthen }
169933707f3Ssthen
lock_basic_init(lock_basic_type * lock)170*77079be7Ssthen void lock_basic_init(lock_basic_type* lock)
171933707f3Ssthen {
172933707f3Ssthen /* implement own lock, because windows HANDLE as Mutex usage
173933707f3Ssthen * uses too many handles and would bog down the whole system. */
174933707f3Ssthen (void)InterlockedExchange(lock, 0);
175933707f3Ssthen }
176933707f3Ssthen
lock_basic_destroy(lock_basic_type * lock)177*77079be7Ssthen void lock_basic_destroy(lock_basic_type* lock)
178933707f3Ssthen {
179933707f3Ssthen (void)InterlockedExchange(lock, 0);
180933707f3Ssthen }
181933707f3Ssthen
lock_basic_lock(lock_basic_type * lock)182*77079be7Ssthen void lock_basic_lock(lock_basic_type* lock)
183933707f3Ssthen {
184933707f3Ssthen LONG wait = 1; /* wait 1 msec at first */
185933707f3Ssthen
186933707f3Ssthen while(InterlockedExchange(lock, 1)) {
187933707f3Ssthen /* if the old value was 1 then if was already locked */
188933707f3Ssthen Sleep(wait); /* wait with sleep */
189933707f3Ssthen wait *= 2; /* exponential backoff for waiting */
190933707f3Ssthen }
191933707f3Ssthen /* the old value was 0, but we inserted 1, we locked it! */
192933707f3Ssthen }
193933707f3Ssthen
lock_basic_unlock(lock_basic_type * lock)194*77079be7Ssthen void lock_basic_unlock(lock_basic_type* lock)
195933707f3Ssthen {
196933707f3Ssthen /* unlock it by inserting the value of 0. xchg for cache coherency. */
197933707f3Ssthen (void)InterlockedExchange(lock, 0);
198933707f3Ssthen }
199933707f3Ssthen
ub_thread_key_create(ub_thread_key_type * key,void * f)200*77079be7Ssthen void ub_thread_key_create(ub_thread_key_type* key, void* f)
201933707f3Ssthen {
202933707f3Ssthen *key = TlsAlloc();
203933707f3Ssthen if(*key == TLS_OUT_OF_INDEXES) {
204933707f3Ssthen *key = 0;
205933707f3Ssthen log_win_err("TlsAlloc Failed(OUT_OF_INDEXES)", GetLastError());
206933707f3Ssthen }
207933707f3Ssthen else ub_thread_key_set(*key, f);
208933707f3Ssthen }
209933707f3Ssthen
ub_thread_key_set(ub_thread_key_type key,void * v)210*77079be7Ssthen void ub_thread_key_set(ub_thread_key_type key, void* v)
211933707f3Ssthen {
212933707f3Ssthen if(!TlsSetValue(key, v)) {
213933707f3Ssthen log_win_err("TlsSetValue failed", GetLastError());
214933707f3Ssthen }
215933707f3Ssthen }
216933707f3Ssthen
ub_thread_key_get(ub_thread_key_type key)217*77079be7Ssthen void* ub_thread_key_get(ub_thread_key_type key)
218933707f3Ssthen {
219933707f3Ssthen void* ret = (void*)TlsGetValue(key);
220933707f3Ssthen if(ret == NULL && GetLastError() != ERROR_SUCCESS) {
221933707f3Ssthen log_win_err("TlsGetValue failed", GetLastError());
222933707f3Ssthen }
223933707f3Ssthen return ret;
224933707f3Ssthen }
225933707f3Ssthen
ub_thread_create(ub_thread_type * thr,void * (* func)(void *),void * arg)226*77079be7Ssthen void ub_thread_create(ub_thread_type* thr, void* (*func)(void*), void* arg)
227933707f3Ssthen {
228933707f3Ssthen #ifndef HAVE__BEGINTHREADEX
229933707f3Ssthen *thr = CreateThread(NULL, /* default security (no inherit handle) */
230933707f3Ssthen 0, /* default stack size */
231933707f3Ssthen (LPTHREAD_START_ROUTINE)func, arg,
232933707f3Ssthen 0, /* default flags, run immediately */
233933707f3Ssthen NULL); /* do not store thread identifier anywhere */
234933707f3Ssthen #else
2354bfc71b0Ssthen /* the beginthreadex routine setups for the C lib; aligns stack */
236*77079be7Ssthen *thr=(ub_thread_type)_beginthreadex(NULL, 0, (void*)func, arg, 0, NULL);
237933707f3Ssthen #endif
238933707f3Ssthen if(*thr == NULL) {
239933707f3Ssthen log_win_err("CreateThread failed", GetLastError());
240933707f3Ssthen fatal_exit("thread create failed");
241933707f3Ssthen }
242933707f3Ssthen }
243933707f3Ssthen
ub_thread_self(void)244*77079be7Ssthen ub_thread_type ub_thread_self(void)
245933707f3Ssthen {
246933707f3Ssthen return GetCurrentThread();
247933707f3Ssthen }
248933707f3Ssthen
ub_thread_join(ub_thread_type thr)249*77079be7Ssthen void ub_thread_join(ub_thread_type thr)
250933707f3Ssthen {
251933707f3Ssthen DWORD ret = WaitForSingleObject(thr, INFINITE);
252933707f3Ssthen if(ret == WAIT_FAILED) {
253933707f3Ssthen log_win_err("WaitForSingleObject(Thread):WAIT_FAILED",
254933707f3Ssthen GetLastError());
255933707f3Ssthen } else if(ret == WAIT_TIMEOUT) {
256933707f3Ssthen log_win_err("WaitForSingleObject(Thread):WAIT_TIMEOUT",
257933707f3Ssthen GetLastError());
258933707f3Ssthen }
259933707f3Ssthen /* and close the handle to the thread */
260933707f3Ssthen if(!CloseHandle(thr)) {
261933707f3Ssthen log_win_err("CloseHandle(Thread) failed", GetLastError());
262933707f3Ssthen }
263933707f3Ssthen }
264933707f3Ssthen #endif /* HAVE_WINDOWS_THREADS */
265