]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/unbound/util/locks.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / unbound / util / locks.c
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 LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * 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 */