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

NAME

6       libtalloc_threads - Chapter 8: Using threads with talloc
7

and thread safety

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