Home | History | Annotate | Line # | Download | only in util
locks.c revision 1.1.1.1.2.2
      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 beginthreadex 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