1238106Sdes/**
2238106Sdes * util/locks.c - unbound locking primitives
3238106Sdes *
4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved.
5238106Sdes *
6238106Sdes * This software is open source.
7238106Sdes *
8238106Sdes * Redistribution and use in source and binary forms, with or without
9238106Sdes * modification, are permitted provided that the following conditions
10238106Sdes * are met:
11238106Sdes *
12238106Sdes * Redistributions of source code must retain the above copyright notice,
13238106Sdes * this list of conditions and the following disclaimer.
14238106Sdes *
15238106Sdes * Redistributions in binary form must reproduce the above copyright notice,
16238106Sdes * this list of conditions and the following disclaimer in the documentation
17238106Sdes * and/or other materials provided with the distribution.
18238106Sdes *
19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may
20238106Sdes * be used to endorse or promote products derived from this software without
21238106Sdes * specific prior written permission.
22238106Sdes *
23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24238106Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25238106Sdes * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26238106Sdes * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27238106Sdes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28238106Sdes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29238106Sdes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30238106Sdes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31238106Sdes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32238106Sdes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33238106Sdes * POSSIBILITY OF SUCH DAMAGE.
34238106Sdes */
35238106Sdes
36238106Sdes/**
37238106Sdes * \file
38238106Sdes * Implementation of locking and threading support.
39238106Sdes * A place for locking debug code since most locking functions are macros.
40238106Sdes */
41238106Sdes
42238106Sdes#include "config.h"
43238106Sdes#include "util/locks.h"
44238106Sdes#include <signal.h>
45238106Sdes#ifdef HAVE_SYS_WAIT_H
46238106Sdes#include <sys/wait.h>
47238106Sdes#endif
48238106Sdes
49238106Sdes/** block all signals, masks them away. */
50238106Sdesvoid
51238106Sdesub_thread_blocksigs(void)
52238106Sdes{
53238106Sdes#if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
54238106Sdes#  if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
55238106Sdes	int err;
56238106Sdes#  endif
57238106Sdes	sigset_t sigset;
58238106Sdes	sigfillset(&sigset);
59238106Sdes#ifdef HAVE_PTHREAD
60238106Sdes	if((err=pthread_sigmask(SIG_SETMASK, &sigset, NULL)))
61238106Sdes		fatal_exit("pthread_sigmask: %s", strerror(err));
62238106Sdes#else
63238106Sdes#  ifdef HAVE_SOLARIS_THREADS
64238106Sdes	if((err=thr_sigsetmask(SIG_SETMASK, &sigset, NULL)))
65238106Sdes		fatal_exit("thr_sigsetmask: %s", strerror(err));
66238106Sdes#  else
67238106Sdes	/* have nothing, do single process signal mask */
68238106Sdes	if(sigprocmask(SIG_SETMASK, &sigset, NULL))
69238106Sdes		fatal_exit("sigprocmask: %s", strerror(errno));
70238106Sdes#  endif /* HAVE_SOLARIS_THREADS */
71238106Sdes#endif /* HAVE_PTHREAD */
72238106Sdes#endif /* have signal stuff */
73238106Sdes}
74238106Sdes
75238106Sdes/** unblock one signal, so we can catch it */
76238106Sdesvoid ub_thread_sig_unblock(int sig)
77238106Sdes{
78238106Sdes#if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
79238106Sdes#  if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
80238106Sdes	int err;
81238106Sdes#  endif
82238106Sdes	sigset_t sigset;
83238106Sdes	sigemptyset(&sigset);
84238106Sdes	sigaddset(&sigset, sig);
85238106Sdes#ifdef HAVE_PTHREAD
86238106Sdes	if((err=pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)))
87238106Sdes		fatal_exit("pthread_sigmask: %s", strerror(err));
88238106Sdes#else
89238106Sdes#  ifdef HAVE_SOLARIS_THREADS
90238106Sdes	if((err=thr_sigsetmask(SIG_UNBLOCK, &sigset, NULL)))
91238106Sdes		fatal_exit("thr_sigsetmask: %s", strerror(err));
92238106Sdes#  else
93238106Sdes	/* have nothing, do single thread case */
94238106Sdes	if(sigprocmask(SIG_UNBLOCK, &sigset, NULL))
95238106Sdes		fatal_exit("sigprocmask: %s", strerror(errno));
96238106Sdes#  endif /* HAVE_SOLARIS_THREADS */
97238106Sdes#endif /* HAVE_PTHREAD */
98238106Sdes#else
99238106Sdes	(void)sig;
100238106Sdes#endif /* have signal stuff */
101238106Sdes}
102238106Sdes
103238106Sdes#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS)
104238106Sdes/**
105238106Sdes * No threading available: fork a new process.
106238106Sdes * This means no shared data structure, and no locking.
107238106Sdes * Only the main thread ever returns. Exits on errors.
108238106Sdes * @param thr: the location where to store the thread-id.
109238106Sdes * @param func: function body of the thread. Return value of func is lost.
110238106Sdes * @param arg: user argument to func.
111238106Sdes */
112238106Sdesvoid
113238106Sdesub_thr_fork_create(ub_thread_t* thr, void* (*func)(void*), void* arg)
114238106Sdes{
115238106Sdes	pid_t pid = fork();
116238106Sdes	switch(pid) {
117238106Sdes	default:	/* main */
118238106Sdes			*thr = (ub_thread_t)pid;
119238106Sdes			return;
120238106Sdes	case 0: 	/* child */
121238106Sdes			*thr = (ub_thread_t)getpid();
122238106Sdes			(void)(*func)(arg);
123238106Sdes			exit(0);
124238106Sdes	case -1:	/* error */
125238106Sdes			fatal_exit("could not fork: %s", strerror(errno));
126238106Sdes	}
127238106Sdes}
128238106Sdes
129238106Sdes/**
130238106Sdes * There is no threading. Wait for a process to terminate.
131238106Sdes * Note that ub_thread_t is defined as pid_t.
132238106Sdes * @param thread: the process id to wait for.
133238106Sdes */
134238106Sdesvoid ub_thr_fork_wait(ub_thread_t thread)
135238106Sdes{
136238106Sdes	int status = 0;
137238106Sdes	if(waitpid((pid_t)thread, &status, 0) == -1)
138238106Sdes		log_err("waitpid(%d): %s", (int)thread, strerror(errno));
139238106Sdes	if(status != 0)
140238106Sdes		log_warn("process %d abnormal exit with status %d",
141238106Sdes			(int)thread, status);
142238106Sdes}
143238106Sdes#endif /* !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS) */
144238106Sdes
145238106Sdes#ifdef HAVE_SOLARIS_THREADS
146238106Sdesvoid* ub_thread_key_get(ub_thread_key_t key)
147238106Sdes{
148238106Sdes	void* ret=NULL;
149238106Sdes	LOCKRET(thr_getspecific(key, &ret));
150238106Sdes	return ret;
151238106Sdes}
152238106Sdes#endif
153238106Sdes
154238106Sdes#ifdef HAVE_WINDOWS_THREADS
155238106Sdes/** log a windows GetLastError message */
156238106Sdesstatic void log_win_err(const char* str, DWORD err)
157238106Sdes{
158238106Sdes	LPTSTR buf;
159238106Sdes	if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
160238106Sdes		FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
161238106Sdes		NULL, err, 0, (LPTSTR)&buf, 0, NULL) == 0) {
162238106Sdes		/* could not format error message */
163238106Sdes		log_err("%s, GetLastError=%d", str, (int)err);
164238106Sdes		return;
165238106Sdes	}
166238106Sdes	log_err("%s, (err=%d): %s", str, (int)err, buf);
167238106Sdes	LocalFree(buf);
168238106Sdes}
169238106Sdes
170238106Sdesvoid lock_basic_init(lock_basic_t* lock)
171238106Sdes{
172238106Sdes	/* implement own lock, because windows HANDLE as Mutex usage
173238106Sdes	 * uses too many handles and would bog down the whole system. */
174238106Sdes	(void)InterlockedExchange(lock, 0);
175238106Sdes}
176238106Sdes
177238106Sdesvoid lock_basic_destroy(lock_basic_t* lock)
178238106Sdes{
179238106Sdes	(void)InterlockedExchange(lock, 0);
180238106Sdes}
181238106Sdes
182238106Sdesvoid lock_basic_lock(lock_basic_t* lock)
183238106Sdes{
184238106Sdes	LONG wait = 1; /* wait 1 msec at first */
185238106Sdes
186238106Sdes	while(InterlockedExchange(lock, 1)) {
187238106Sdes		/* if the old value was 1 then if was already locked */
188238106Sdes		Sleep(wait); /* wait with sleep */
189238106Sdes		wait *= 2;   /* exponential backoff for waiting */
190238106Sdes	}
191238106Sdes	/* the old value was 0, but we inserted 1, we locked it! */
192238106Sdes}
193238106Sdes
194238106Sdesvoid lock_basic_unlock(lock_basic_t* lock)
195238106Sdes{
196238106Sdes	/* unlock it by inserting the value of 0. xchg for cache coherency. */
197238106Sdes	(void)InterlockedExchange(lock, 0);
198238106Sdes}
199238106Sdes
200238106Sdesvoid ub_thread_key_create(ub_thread_key_t* key, void* f)
201238106Sdes{
202238106Sdes	*key = TlsAlloc();
203238106Sdes	if(*key == TLS_OUT_OF_INDEXES) {
204238106Sdes		*key = 0;
205238106Sdes		log_win_err("TlsAlloc Failed(OUT_OF_INDEXES)", GetLastError());
206238106Sdes	}
207238106Sdes	else ub_thread_key_set(*key, f);
208238106Sdes}
209238106Sdes
210238106Sdesvoid ub_thread_key_set(ub_thread_key_t key, void* v)
211238106Sdes{
212238106Sdes	if(!TlsSetValue(key, v)) {
213238106Sdes		log_win_err("TlsSetValue failed", GetLastError());
214238106Sdes	}
215238106Sdes}
216238106Sdes
217238106Sdesvoid* ub_thread_key_get(ub_thread_key_t key)
218238106Sdes{
219238106Sdes	void* ret = (void*)TlsGetValue(key);
220238106Sdes	if(ret == NULL && GetLastError() != ERROR_SUCCESS) {
221238106Sdes		log_win_err("TlsGetValue failed", GetLastError());
222238106Sdes	}
223238106Sdes	return ret;
224238106Sdes}
225238106Sdes
226238106Sdesvoid ub_thread_create(ub_thread_t* thr, void* (*func)(void*), void* arg)
227238106Sdes{
228238106Sdes#ifndef HAVE__BEGINTHREADEX
229238106Sdes	*thr = CreateThread(NULL, /* default security (no inherit handle) */
230238106Sdes		0, /* default stack size */
231238106Sdes		(LPTHREAD_START_ROUTINE)func, arg,
232238106Sdes		0, /* default flags, run immediately */
233238106Sdes		NULL); /* do not store thread identifier anywhere */
234238106Sdes#else
235238106Sdes	/* the begintheadex routine setups for the C lib; aligns stack */
236238106Sdes	*thr=(ub_thread_t)_beginthreadex(NULL, 0, (void*)func, arg, 0, NULL);
237238106Sdes#endif
238238106Sdes	if(*thr == NULL) {
239238106Sdes		log_win_err("CreateThread failed", GetLastError());
240238106Sdes		fatal_exit("thread create failed");
241238106Sdes	}
242238106Sdes}
243238106Sdes
244238106Sdesub_thread_t ub_thread_self(void)
245238106Sdes{
246238106Sdes	return GetCurrentThread();
247238106Sdes}
248238106Sdes
249238106Sdesvoid ub_thread_join(ub_thread_t thr)
250238106Sdes{
251238106Sdes	DWORD ret = WaitForSingleObject(thr, INFINITE);
252238106Sdes	if(ret == WAIT_FAILED) {
253238106Sdes		log_win_err("WaitForSingleObject(Thread):WAIT_FAILED",
254238106Sdes			GetLastError());
255238106Sdes	} else if(ret == WAIT_TIMEOUT) {
256238106Sdes		log_win_err("WaitForSingleObject(Thread):WAIT_TIMEOUT",
257238106Sdes			GetLastError());
258238106Sdes	}
259238106Sdes	/* and close the handle to the thread */
260238106Sdes	if(!CloseHandle(thr)) {
261238106Sdes		log_win_err("CloseHandle(Thread) failed", GetLastError());
262238106Sdes	}
263238106Sdes}
264238106Sdes#endif /* HAVE_WINDOWS_THREADS */
265