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