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 { 53 #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK) 54 # if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) 55 int err; 56 # endif 57 sigset_t sigset; 58 sigfillset(&sigset); 59 #ifdef HAVE_PTHREAD 60 if((err=pthread_sigmask(SIG_SETMASK, &sigset, NULL))) 61 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 #endif /* have signal stuff */ 73 } 74 75 /** unblock one signal, so we can catch it */ 76 void ub_thread_sig_unblock(int sig) 77 { 78 #if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK) 79 # if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) 80 int err; 81 # endif 82 sigset_t sigset; 83 sigemptyset(&sigset); 84 sigaddset(&sigset, sig); 85 #ifdef HAVE_PTHREAD 86 if((err=pthread_sigmask(SIG_UNBLOCK, &sigset, NULL))) 87 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 } 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_t* thr, void* (*func)(void*), void* arg) 114 { 115 pid_t pid = fork(); 116 switch(pid) { 117 default: /* main */ 118 *thr = (ub_thread_t)pid; 119 return; 120 case 0: /* child */ 121 *thr = (ub_thread_t)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_t is defined as pid_t. 132 * @param thread: the process id to wait for. 133 */ 134 void ub_thr_fork_wait(ub_thread_t 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_t 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_t* 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_t* lock) 178 { 179 (void)InterlockedExchange(lock, 0); 180 } 181 182 void lock_basic_lock(lock_basic_t* 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_t* 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_t* 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_t 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_t 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_t* 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 begintheadex routine setups for the C lib; aligns stack */ 236 *thr=(ub_thread_t)_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_t ub_thread_self(void) 245 { 246 return GetCurrentThread(); 247 } 248 249 void ub_thread_join(ub_thread_t 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 */ 265