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