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