aboutsummaryrefslogtreecommitdiffstats
path: root/include/asterisk/threadstorage.h
blob: e204748ba4b9463e634ddea8eec75c5e47c22ed7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
 * Asterisk -- An open source telephony toolkit.
 *
 * Copyright (C) 2006, Digium, Inc.
 *
 * Russell Bryant <russell@digium.com>
 *
 * See http://www.asterisk.org for more information about
 * the Asterisk project. Please do not directly contact
 * any of the maintainers of this project for assistance;
 * the project provides a web site, mailing lists and IRC
 * channels for your use.
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License Version 2. See the LICENSE file
 * at the top of the source tree.
 */

/*!
 * \file threadstorage.h
 * \author Russell Bryant <russell@digium.com>
 * \brief Definitions to aid in the use of thread local storage
 *
 * \arg \ref AstThreadStorage
 */

/*!
 * \page AstThreadStorage The Asterisk Thread Storage API
 *
 *
 * The POSIX threads (pthreads) API provides the ability to define thread
 * specific data.  The functions and structures defined here are intended
 * to centralize the code that is commonly used when using thread local
 * storage.
 *
 * The motivation for using this code in Asterisk is for situations where
 * storing data on a thread-specific basis can provide some amount of
 * performance benefit.  For example, there are some call types in Asterisk
 * where ast_frame structures must be allocated very rapidly (easily 50, 100,
 * 200 times a second).  Instead of doing the equivalent of that many calls
 * to malloc() and free() per second, thread local storage is used to keep a
 * list of unused frame structures so that they can be continuously reused.
 *
 * - \ref threadstorage.h
 */

#ifndef ASTERISK_THREADSTORAGE_H
#define ASTERISK_THREADSTORAGE_H

#include "asterisk/utils.h"
#include "asterisk/inline_api.h"

/*!
 * \brief data for a thread locally stored variable
 */
struct ast_threadstorage {
	pthread_once_t once;	/*!< Ensure that the key is only initialized by one thread */
	pthread_key_t key;	/*!< The key used to retrieve this thread's data */
	void (*key_init)(void);	/*!< The function that initializes the key */
	int (*custom_init)(void *); /*!< Custom initialization function specific to the object */
};

#if defined(DEBUG_THREADLOCALS)
void __ast_threadstorage_object_add(void *key, size_t len, const char *file, const char *function, unsigned int line);
void __ast_threadstorage_object_remove(void *key);
void __ast_threadstorage_object_replace(void *key_old, void *key_new, size_t len);
#endif /* defined(DEBUG_THREADLOCALS) */

/*!
 * \brief Define a thread storage variable
 *
 * \param name The name of the thread storage object
 *
 * This macro would be used to declare an instance of thread storage in a file.
 *
 * Example usage:
 * \code
 * AST_THREADSTORAGE(my_buf);
 * \endcode
 */
#define AST_THREADSTORAGE(name) \
	AST_THREADSTORAGE_CUSTOM_SCOPE(name, NULL, ast_free_ptr, static) 
#define AST_THREADSTORAGE_PUBLIC(name) \
	AST_THREADSTORAGE_CUSTOM_SCOPE(name, NULL, ast_free_ptr,) 
#define AST_THREADSTORAGE_EXTERNAL(name) \
	extern struct ast_threadstorage name

/*!
 * \brief Define a thread storage variable, with custom initialization and cleanup
 *
 * \param a The name of the thread storage object
 * \param b This is a custom function that will be called after each thread specific
 *           object is allocated, with the allocated block of memory passed
 *           as the argument.
 * \param c This is a custom function that will be called instead of ast_free
 *              when the thread goes away.  Note that if this is used, it *MUST*
 *              call free on the allocated memory.
 *
 * Example usage:
 * \code
 * AST_THREADSTORAGE_CUSTOM(my_buf, my_init, my_cleanup);
 * \endcode
 */
#define AST_THREADSTORAGE_CUSTOM(a,b,c)	AST_THREADSTORAGE_CUSTOM_SCOPE(a,b,c,static)

#if defined(PTHREAD_ONCE_INIT_NEEDS_BRACES)
# define AST_PTHREAD_ONCE_INIT { PTHREAD_ONCE_INIT }
#else
# define AST_PTHREAD_ONCE_INIT PTHREAD_ONCE_INIT
#endif

#if !defined(DEBUG_THREADLOCALS)
#define AST_THREADSTORAGE_CUSTOM_SCOPE(name, c_init, c_cleanup, scope)	\
static void __init_##name(void);                \
scope struct ast_threadstorage name = {         \
	.once = AST_PTHREAD_ONCE_INIT,              \
	.key_init = __init_##name,                  \
	.custom_init = c_init,                      \
};                                              \
static void __init_##name(void)                 \
{                                               \
	pthread_key_create(&(name).key, c_cleanup); \
}
#else /* defined(DEBUG_THREADLOCALS) */
#define AST_THREADSTORAGE_CUSTOM_SCOPE(name, c_init, c_cleanup, scope) \
static void __init_##name(void);                \
scope struct ast_threadstorage name = {         \
	.once = AST_PTHREAD_ONCE_INIT,              \
	.key_init = __init_##name,                  \
	.custom_init = c_init,                      \
};                                              \
static void __cleanup_##name(void *data)        \
{                                               \
	__ast_threadstorage_object_remove(data);    \
	c_cleanup(data);                            \
}                                               \
static void __init_##name(void)                 \
{                                               \
	pthread_key_create(&(name).key, __cleanup_##name); \
}
#endif /* defined(DEBUG_THREADLOCALS) */

/*!
 * \brief Retrieve thread storage
 *
 * \param ts This is a pointer to the thread storage structure declared by using
 *      the AST_THREADSTORAGE macro.  If declared with 
 *      AST_THREADSTORAGE(my_buf), then this argument would be (&my_buf).
 * \param init_size This is the amount of space to be allocated the first time
 *      this thread requests its data. Thus, this should be the size that the
 *      code accessing this thread storage is assuming the size to be.
 *
 * \return This function will return the thread local storage associated with
 *         the thread storage management variable passed as the first argument.
 *         The result will be NULL in the case of a memory allocation error.
 *
 * Example usage:
 * \code
 * AST_THREADSTORAGE(my_buf);
 * #define MY_BUF_SIZE   128
 * ...
 * void my_func(const char *fmt, ...)
 * {
 *      void *buf;
 *
 *      if (!(buf = ast_threadstorage_get(&my_buf, MY_BUF_SIZE)))
 *           return;
 *      ...
 * }
 * \endcode
 */
#if !defined(DEBUG_THREADLOCALS)
AST_INLINE_API(
void *ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size),
{
	void *buf;

	pthread_once(&ts->once, ts->key_init);
	if (!(buf = pthread_getspecific(ts->key))) {
		if (!(buf = ast_calloc(1, init_size)))
			return NULL;
		if (ts->custom_init && ts->custom_init(buf)) {
			free(buf);
			return NULL;
		}
		pthread_setspecific(ts->key, buf);
	}

	return buf;
}
)
#else /* defined(DEBUG_THREADLOCALS) */
AST_INLINE_API(
void *__ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size, const char *file, const char *function, unsigned int line),
{
	void *buf;

	pthread_once(&ts->once, ts->key_init);
	if (!(buf = pthread_getspecific(ts->key))) {
		if (!(buf = ast_calloc(1, init_size)))
			return NULL;
		if (ts->custom_init && ts->custom_init(buf)) {
			free(buf);
			return NULL;
		}
		pthread_setspecific(ts->key, buf);
		__ast_threadstorage_object_add(buf, init_size, file, function, line);
	}

	return buf;
}
)

#define ast_threadstorage_get(ts, init_size) __ast_threadstorage_get(ts, init_size, __FILE__, __PRETTY_FUNCTION__, __LINE__)
#endif /* defined(DEBUG_THREADLOCALS) */

#endif /* ASTERISK_THREADSTORAGE_H */