1tevent_thread(3)                    tevent                    tevent_thread(3)
2
3
4

NAME

6       tevent_thread - Chapter 6: Tevent with threads
7
8

Tevent with threads

10       In order to use tevent with threads, you must first understand how to
11       use the talloc library in threaded programs. For more information about
12       working with talloc, please visit talloc website where tutorial and
13       documentation are located.
14
15       If a tevent context structure is talloced from a NULL, thread-safe
16       talloc context, then it can be safe to use in a threaded program. The
17       function talloc_disable_null_tracking() must be called from the initial
18       program thread before any talloc calls are made to ensure talloc is
19       thread-safe.
20
21       Each thread must create it's own tevent context structure as follows
22       tevent_context_init(NULL) and no talloc memory contexts can be shared
23       between threads.
24
25       Separate threads using tevent in this way can communicate by writing
26       data into file descriptors that are being monitored by a tevent context
27       on another thread. For example (simplified with no error handling):
28
29       Main thread:
30
31       main()
32       {
33               talloc_disable_null_tracking();
34
35               struct tevent_context *master_ev = tevent_context_init(NULL);
36               void *mem_ctx = talloc_new(master_ev);
37
38               // Create file descriptor to monitor.
39               int pipefds[2];
40
41               pipe(pipefds);
42
43               struct tevent_fd *fde = tevent_add_fd(master_ev,
44                                       mem_ctx,
45                                       pipefds[0], // read side of pipe
46                                       TEVENT_FD_READ,
47                                       pipe_read_handler, // callback function
48                                       private_data_pointer);
49
50               // Create sub thread, pass pipefds[1] write side of pipe to it.
51               // The above code not shown here..
52
53               // Process events.
54               tevent_loop_wait(master_ev);
55
56               // Cleanup if loop exits.
57               talloc_free(master_ev);
58       }
59
60       When the subthread writes to pipefds[1], the function
61       pipe_read_handler() will be called in the main thread.
62
63   sophisticated use
64       A popular way to use an event library within threaded programs is to
65       allow a sub-thread to asynchronously schedule a tevent_immediate
66       function call from the event loop of another thread. This can be built
67       out of the basic functions and isolation mechanisms of tevent, but
68       tevent also comes with some utility functions that make this easier, so
69       long as you understand the limitations that using threads with talloc
70       and tevent impose.
71
72       To allow a tevent context to receive an asynchronous tevent_immediate
73       function callback from another thread, create a struct
74       tevent_thread_proxy * by calling
75
76       struct tevent_thread_proxy *tevent_thread_proxy_create(
77                       struct tevent_context *dest_ev_ctx);
78
79
80       This function allocates the internal data structures to allow
81       asynchronous callbacks as a talloc child of the struct tevent_context
82       *, and returns a struct tevent_thread_proxy * that can be passed to
83       another thread.
84
85       When you have finished receiving asynchronous callbacks, simply
86       talloc_free the struct tevent_thread_proxy *, or talloc_free the struct
87       tevent_context *, which will deallocate the resources used.
88
89       To schedule an asynchronous tevent_immediate function call from one
90       thread on the tevent loop of another thread, use
91
92       void tevent_thread_proxy_schedule(struct tevent_thread_proxy *tp,
93                                       struct tevent_immediate **pp_im,
94                                       tevent_immediate_handler_t handler,
95                                       void **pp_private_data);
96
97
98       This function causes the function handler() to be invoked as a
99       tevent_immediate callback from the event loop of the thread that
100       created the struct tevent_thread_proxy * (so the owning struct
101       tevent_context * should be long-lived and not in the process of being
102       torn down).
103
104       The struct tevent_thread_proxy object being used here is a child of the
105       event context of the target thread. So external synchronization
106       mechanisms must be used to ensure that the target object is still in
107       use at the time of the tevent_thread_proxy_schedule() call. In the
108       example below, the request/response nature of the communication ensures
109       this.
110
111       The struct tevent_immediate **pp_im passed into this function should be
112       a struct tevent_immediate * allocated on a talloc context local to this
113       thread, and will be reparented via talloc_move to be owned by struct
114       tevent_thread_proxy *tp. *pp_im will be set to NULL on successful
115       scheduling of the tevent_immediate call.
116
117       handler() will be called as a normal tevent_immediate callback from the
118       struct tevent_context * of the destination event loop that created the
119       struct tevent_thread_proxy *
120
121       Returning from this functions does not mean that the handler has been
122       invoked, merely that it has been scheduled to be called in the
123       destination event loop.
124
125       Because the calling thread does not wait for the callback to be
126       scheduled and run on the destination thread, this is a fire-and-forget
127       call. If you wish confirmation of the handler() being successfully
128       invoked, you must ensure it replies to the caller in some way.
129
130       Because of asynchronous nature of this call, the nature of the
131       parameter passed to the destination thread has some restructions. If
132       you don't need parameters, merely pass NULL as the value of void
133       **pp_private_data.
134
135       If you wish to pass a pointer to data between the threads, it MUST be a
136       pointer to a talloced pointer, which is not part of a talloc-pool, and
137       it must not have a destructor attached. The ownership of the memory
138       pointed to will be passed from the calling thread to the tevent
139       library, and if the receiving thread does not talloc-reparent it to its
140       own contexts, it will be freed once the handler is called.
141
142       On success, *pp_private will be NULL to signify the talloc memory
143       ownership has been moved.
144
145       In practice for message passing between threads in event loops these
146       restrictions are not very onerous.
147
148       The easiest way to to a request-reply pair between tevent loops on
149       different threads is to pass the parameter block of memory back and
150       forth using a reply tevent_thread_proxy_schedule() call.
151
152       Here is an example (without error checking for simplicity):
153
154       ------------------------------------------------
155       // Master thread.
156
157       main()
158       {
159               // Make talloc thread-safe.
160
161               talloc_disable_null_tracking();
162
163               // Create the master event context.
164
165               struct tevent_context *master_ev = tevent_context_init(NULL);
166
167               // Create the master thread proxy to allow it to receive
168               // async callbacks from other threads.
169
170               struct tevent_thread_proxy *master_tp =
171                               tevent_thread_proxy_create(master_ev);
172
173               // Create sub-threads, passing master_tp in
174               // some way to them.
175               // This code not shown..
176
177               // Process events.
178               // Function master_callback() below
179               // will be invoked on this thread on
180               // master_ev event context.
181
182               tevent_loop_wait(master_ev);
183
184               // Cleanup if loop exits.
185
186               talloc_free(master_ev);
187       }
188
189       // Data passed between threads.
190       struct reply_state {
191               struct tevent_thread_proxy *reply_tp;
192               pthread_t thread_id;
193               bool *p_finished;
194       };
195
196       // Callback Called in child thread context.
197
198       static void thread_callback(struct tevent_context *ev,
199                                       struct tevent_immediate *im,
200                                       void *private_ptr)
201       {
202               // Move the ownership of what private_ptr
203               // points to from the tevent library back to this thread.
204
205               struct reply_state *rsp =
206                       talloc_get_type_abort(private_ptr, struct reply_state);
207
208               talloc_steal(ev, rsp);
209
210                *rsp->p_finished = true;
211
212               // im will be talloc_freed on return from this call.
213               // but rsp will not.
214       }
215
216       // Callback Called in master thread context.
217
218       static void master_callback(struct tevent_context *ev,
219                                       struct tevent_immediate *im,
220                                       void *private_ptr)
221       {
222               // Move the ownership of what private_ptr
223               // points to from the tevent library to this thread.
224
225               struct reply_state *rsp =
226                       talloc_get_type_abort(private_ptr, struct reply_state);
227
228               talloc_steal(ev, rsp);
229
230               printf("Callback from thread %s0, thread_id_to_string(rsp->thread_id));
231
232               /* Now reply to the thread ! */
233               tevent_thread_proxy_schedule(rsp->reply_tp,
234                                       &im,
235                                       thread_callback,
236                                       &rsp);
237
238               // Note - rsp and im are now NULL as the tevent library
239               // owns the memory.
240       }
241
242       // Child thread.
243
244       static void *thread_fn(void *private_ptr)
245       {
246               struct tevent_thread_proxy *master_tp =
247                       talloc_get_type_abort(private_ptr, struct tevent_thread_proxy);
248               bool finished = false;
249               int ret;
250
251               // Create our own event context.
252
253               struct tevent_context *ev = tevent_context_init(NULL);
254
255               // Create the local thread proxy to allow us to receive
256               // async callbacks from other threads.
257
258               struct tevent_thread_proxy *local_tp =
259                               tevent_thread_proxy_create(master_ev);
260
261               // Setup the data to send.
262
263               struct reply_state *rsp = talloc(ev, struct reply_state);
264
265               rsp->reply_tp = local_tp;
266               rsp->thread_id = pthread_self();
267               rsp->p_finished = &finished;
268
269               // Create the immediate event to use.
270
271               struct tevent_immediate *im = tevent_create_immediate(ev);
272
273               // Call the master thread.
274
275               tevent_thread_proxy_schedule(master_tp,
276                                       &im,
277                                       master_callback,
278                                       &rsp);
279
280               // Note - rsp and im are now NULL as the tevent library
281               // owns the memory.
282
283               // Wait for the reply.
284
285               while (!finished) {
286                       tevent_loop_once(ev);
287               }
288
289               // Cleanup.
290
291               talloc_free(ev);
292               return NULL;
293       }
294
295       Note this doesn't have to be a master-subthread communication. Any
296       thread that has access to the struct tevent_thread_proxy * pointer of
297       another thread that has called tevent_thread_proxy_create()  can send
298       an async tevent_immediate request.
299
300       But remember the caveat that external synchronization must be used to
301       ensure the target struct tevent_thread_proxy * object exists at the
302       time of the tevent_thread_proxy_schedule() call or unreproducible
303       crashes will result.
304
305
306
307Version 0.9.8                   Tue Jan 26 2021               tevent_thread(3)
Impressum