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