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