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