Coverage Report

Created: 2025-07-11 06:08

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