file: Add an internal "mode"
[nbdkit.git] / server / threadlocal.c
blob0cac11e548441accc110d41714814ffff081eaff
1 /* nbdkit
2 * Copyright (C) 2013-2021 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <assert.h>
40 #include <errno.h>
42 #include <pthread.h>
44 #include "internal.h"
46 /* Note that most thread-local storage data is informational, used for
47 * smart error and debug messages on the server side. However, error
48 * tracking can be used to influence which error is sent to the client
49 * in a reply.
51 * The main thread does not have any associated Thread Local Storage,
52 * *unless* it is serving a request (the '-s' option).
55 struct threadlocal {
56 char *name; /* Can be NULL. */
57 size_t instance_num; /* Can be 0. */
58 int err;
59 void *buffer; /* Can be NULL. */
60 size_t buffer_size;
61 struct connection *conn; /* Can be NULL. */
62 struct context *ctx; /* Can be NULL. */
65 static pthread_key_t threadlocal_key;
67 static void
68 free_threadlocal (void *threadlocalv)
70 struct threadlocal *threadlocal = threadlocalv;
72 free (threadlocal->name);
73 free (threadlocal->buffer);
74 free (threadlocal);
77 void
78 threadlocal_init (void)
80 int err;
82 err = pthread_key_create (&threadlocal_key, free_threadlocal);
83 if (err != 0) {
84 fprintf (stderr, "%s: pthread_key_create: %s\n",
85 program_name, strerror (err));
86 exit (EXIT_FAILURE);
90 void
91 threadlocal_new_server_thread (void)
93 struct threadlocal *threadlocal;
94 int err;
96 threadlocal = calloc (1, sizeof *threadlocal);
97 if (threadlocal == NULL) {
98 perror ("malloc");
99 exit (EXIT_FAILURE);
101 err = pthread_setspecific (threadlocal_key, threadlocal);
102 if (err) {
103 errno = err;
104 perror ("pthread_setspecific");
105 exit (EXIT_FAILURE);
109 void
110 threadlocal_set_name (const char *name)
112 struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
114 /* Copy name, as the original may be residing in a module, but we
115 * want our thread name to persist even after unload. */
116 if (threadlocal) {
117 free (threadlocal->name);
118 threadlocal->name = strdup (name);
119 /* Best effort; logging a NULL name is better than exiting. */
120 if (threadlocal->name == NULL)
121 perror ("malloc");
125 void
126 threadlocal_set_instance_num (size_t instance_num)
128 struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
130 if (threadlocal)
131 threadlocal->instance_num = instance_num;
134 const char *
135 threadlocal_get_name (void)
137 struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
139 if (!threadlocal)
140 return NULL;
142 return threadlocal->name;
145 size_t
146 threadlocal_get_instance_num (void)
148 struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
150 if (!threadlocal)
151 return 0;
153 return threadlocal->instance_num;
156 void
157 threadlocal_set_error (int err)
159 struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
161 if (threadlocal)
162 threadlocal->err = err;
163 else
164 errno = err;
167 /* This preserves errno, for convenience.
170 threadlocal_get_error (void)
172 int err = errno;
173 struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
175 errno = err;
176 return threadlocal ? threadlocal->err : 0;
179 /* Return the single pread/pwrite buffer for this thread. The buffer
180 * size is increased to ‘size’ bytes if required.
182 * The buffer starts out as zeroes but after use may contain data from
183 * previous requests. This is fine because: (a) Correctly written
184 * plugins should overwrite the whole buffer on each request so no
185 * leak should occur. (b) The aim of this buffer is to avoid leaking
186 * random heap data from the core server; previous request data from
187 * the plugin is not considered sensitive.
189 extern void *
190 threadlocal_buffer (size_t size)
192 struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
194 if (!threadlocal)
195 abort ();
197 if (threadlocal->buffer_size < size) {
198 void *ptr;
200 ptr = realloc (threadlocal->buffer, size);
201 if (ptr == NULL) {
202 nbdkit_error ("threadlocal_buffer: realloc: %m");
203 return NULL;
205 memset (ptr, 0, size);
206 threadlocal->buffer = ptr;
207 threadlocal->buffer_size = size;
210 return threadlocal->buffer;
213 /* Set (or clear) the connection that is using the current thread */
214 void
215 threadlocal_set_conn (struct connection *conn)
217 struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
219 if (threadlocal)
220 threadlocal->conn = conn;
223 /* Get the connection associated with this thread, if available */
224 struct connection *
225 threadlocal_get_conn (void)
227 struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
229 return threadlocal ? threadlocal->conn : NULL;
232 /* Get the current context associated with this thread, if available */
233 struct context *
234 threadlocal_get_context (void)
236 struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
238 return threadlocal ? threadlocal->ctx : NULL;
241 /* Set (or clear) the context using the current thread. This function
242 * should generally not be used directly, instead see the macro
243 * PUSH_CONTEXT_FOR_SCOPE.
245 struct context *
246 threadlocal_push_context (struct context *ctx)
248 struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
249 struct context *ret = NULL;
251 if (threadlocal) {
252 ret = threadlocal->ctx;
253 threadlocal->ctx = ctx;
255 return ret;
258 void
259 threadlocal_pop_context (struct context **ctx)
261 threadlocal_push_context (*ctx);