1tevent_context(3)                   tevent                   tevent_context(3)
2
3
4

NAME

6       tevent_context - Chapter 1: Tevent context
7

Tevent context

9       Tevent context is an essential logical unit of tevent library. For
10       working with events at least one such context has to be created -
11       allocated, initialized. Then, events which are meant to be caught and
12       handled have to be registered within this specific context. Reason for
13       subordinating events to a tevent context structure rises from the fact
14       that several context can be created and each of them is processed at
15       different time. So, there can be 1 context containing just file
16       descriptor events, another one taking care of signal and time events
17       and the third one which keeps information about the rest.
18
19       Tevent loops are the part of the library which represents the mechanism
20       where noticing events and triggering handlers actually happens. They
21       accept just one argument - tevent context structure. Therefore if
22       theoretically an infinity loop (tevent_loop_wait) was called, only
23       those arguments which belong to the passed tevent context structure can
24       be caught and invoked within this call. Although some more signal
25       events were registered (but within some other context) they will not be
26       noticed.
27
28   Example
29       First lines which handle mem_ctx belong to talloc library knowledge but
30       because of the fact that tevent uses the talloc library for its
31       mechanisms it is necessary to understand a bit talloc as well. For more
32       information about working with talloc, please visit talloc website
33       where tutorial and documentation are located.
34
35       Tevent context structure *event_ctx represents the unit which will
36       further contain information about registered events. It is created via
37       calling tevent_context_init().
38
39       TALLOC_CTX *mem_ctx = talloc_new(NULL);
40       if (mem_ctx == NULL) {
41           // error handling
42       }
43
44       struct tevent_context *ev_ctx = tevent_context_init(mem_ctx);
45       if(ev_ctx == NULL) {
46           // error handling
47       }
48
49       Tevent context has a structure containing lots of information. It
50       include lists of all events which are divided according their type and
51       are in order showing the sequence as they came.
52
53       In addition to the lists shown in the diagram, the tevent context also
54       contains many other data (e.g. information about the available system
55       mechanism for triggering callbacks).
56

Tevent loops

58       Tevent loops are the dispatcher for events. They catch them and trigger
59       the handlers. In the case of longer processes, the program spends most
60       of its time at this point waiting for events, invoking handlers and
61       waiting for another event again. There are 2 types of loop available
62       for use in tevent library:
63
64       · int tevent_loop_wait()
65       · int tevent_loop_once()
66       Both of functions accept just one parametr (tevent context) and the
67       only difference lies in the fact that the first loop can theoretically
68       last for ever but the second one will wait just for a single one event
69       to catch and then the loop breaks and the program continue.

Tevent context

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