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