1libtalloc_threads(3)                talloc                libtalloc_threads(3)
2
3
4

NAME

6       libtalloc_threads - Chapter 8: Using threads with talloc
7
8

and thread safety

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)
Impressum