1libtalloc_threads(3) talloc libtalloc_threads(3)
2
3
4
6 libtalloc_threads - Chapter 8: Using threads with talloc
7
8
10 The talloc library is not internally thread-safe, in that accesses to
11 variables on a talloc context are not controlled by mutexes or other
12 thread-safe primitives.
13
14 However, so long as talloc_disable_null_tracking() is called from the
15 main thread to disable global variable access within talloc, then each
16 thread can safely use its own top level talloc context allocated off
17 the NULL context.
18
19 For example:
20
21 static void *thread_fn(void *arg)
22 {
23 const char *ctx_name = (const char *)arg;
24 /*
25 * Create a new top level talloc hierarchy in
26 * this thread.
27 */
28 void *top_ctx = talloc_named_const(NULL, 0, "top");
29 if (top_ctx == NULL) {
30 return NULL;
31 }
32 sub_ctx = talloc_named_const(top_ctx, 100, ctx_name);
33 if (sub_ctx == NULL) {
34 return NULL;
35 }
36
37 /*
38 * Do more processing/talloc calls on top_ctx
39 * and its children.
40 */
41 ......
42
43 talloc_free(top_ctx);
44 return value;
45 }
46
47 is a perfectly safe use of talloc within a thread.
48
49 The problem comes when one thread wishes to move some memory allocated
50 on its local top level talloc context to another thread. Care must be
51 taken to add data access exclusion to prevent memory corruption. One
52 method would be to lock a mutex before any talloc call on each thread,
53 but this would push the burden of total talloc thread-safety on the
54 poor user of the library.
55
56 A much easier way to transfer talloced memory between threads is by the
57 use of an intermediate, mutex locked, intermediate variable.
58
59 An example of this is below - taken from test code inside the talloc
60 testsuite.
61
62 The main thread creates 1000 sub-threads, and then accepts the transfer
63 of some thread-talloc'ed memory onto its top level context from each
64 thread in turn.
65
66 A pthread mutex and condition variable are used to synchronize the
67 transfer via the intermediate_ptr variable.
68
69 /* Required sync variables. */
70 static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
71 static pthread_cond_t condvar = PTHREAD_COND_INITIALIZER;
72
73 /* Intermediate talloc pointer for transfer. */
74 static void *intermediate_ptr;
75
76 /* Subthread. */
77 static void *thread_fn(void *arg)
78 {
79 int ret;
80 const char *ctx_name = (const char *)arg;
81 void *sub_ctx = NULL;
82 /*
83 * Do stuff that creates a new talloc hierarchy in
84 * this thread.
85 */
86 void *top_ctx = talloc_named_const(NULL, 0, "top");
87 if (top_ctx == NULL) {
88 return NULL;
89 }
90 sub_ctx = talloc_named_const(top_ctx, 100, ctx_name);
91 if (sub_ctx == NULL) {
92 return NULL;
93 }
94
95 /*
96 * Now transfer a pointer from our hierarchy
97 * onto the intermediate ptr.
98 */
99 ret = pthread_mutex_lock(&mtx);
100 if (ret != 0) {
101 talloc_free(top_ctx);
102 return NULL;
103 }
104
105 /* Wait for intermediate_ptr to be free. */
106 while (intermediate_ptr != NULL) {
107 ret = pthread_cond_wait(&condvar, &mtx);
108 if (ret != 0) {
109 talloc_free(top_ctx);
110 return NULL;
111 }
112 }
113
114 /* and move our memory onto it from our toplevel hierarchy. */
115 intermediate_ptr = talloc_move(NULL, &sub_ctx);
116
117 /* Tell the main thread it's ready for pickup. */
118 pthread_cond_broadcast(&condvar);
119 pthread_mutex_unlock(&mtx);
120
121 talloc_free(top_ctx);
122 return NULL;
123 }
124
125 /* Main thread. */
126
127 #define NUM_THREADS 1000
128
129 static bool test_pthread_talloc_passing(void)
130 {
131 int i;
132 int ret;
133 char str_array[NUM_THREADS][20];
134 pthread_t thread_id;
135 void *mem_ctx;
136
137 /*
138 * Important ! Null tracking breaks threaded talloc.
139 * It *must* be turned off.
140 */
141 talloc_disable_null_tracking();
142
143 /* Main thread toplevel context. */
144 mem_ctx = talloc_named_const(NULL, 0, "toplevel");
145 if (mem_ctx == NULL) {
146 return false;
147 }
148
149 /*
150 * Spin off NUM_THREADS threads.
151 * They will use their own toplevel contexts.
152 */
153 for (i = 0; i < NUM_THREADS; i++) {
154 (void)snprintf(str_array[i],
155 20,
156 "thread:%d",
157 i);
158 if (str_array[i] == NULL) {
159 return false;
160 }
161 ret = pthread_create(&thread_id,
162 NULL,
163 thread_fn,
164 str_array[i]);
165 if (ret != 0) {
166 return false;
167 }
168 }
169
170 /* Now wait for NUM_THREADS transfers of the talloc'ed memory. */
171 for (i = 0; i < NUM_THREADS; i++) {
172 ret = pthread_mutex_lock(&mtx);
173 if (ret != 0) {
174 talloc_free(mem_ctx);
175 return false;
176 }
177
178 /* Wait for intermediate_ptr to have our data. */
179 while (intermediate_ptr == NULL) {
180 ret = pthread_cond_wait(&condvar, &mtx);
181 if (ret != 0) {
182 talloc_free(mem_ctx);
183 return false;
184 }
185 }
186
187 /* and move it onto our toplevel hierarchy. */
188 (void)talloc_move(mem_ctx, &intermediate_ptr);
189
190 /* Tell the sub-threads we're ready for another. */
191 pthread_cond_broadcast(&condvar);
192 pthread_mutex_unlock(&mtx);
193 }
194
195 /* Dump the hierarchy. */
196 talloc_report(mem_ctx, stdout);
197 talloc_free(mem_ctx);
198 return true;
199 }
200
201Version 2.0 Thu Jul 22 2021 libtalloc_threads(3)