/** @page tevent_request Chapter 4: Tevent request @section request Tevent request A specific feature of the library is the tevent request API that provides for asynchronous computation and allows much more interconnected working and cooperation among func- tions and events. When working with tevent request it is possible to nest one event under another and handle them bit by bit. This enables the creation of sequences of steps, and provides an opportunity to prepare for all problems which may unexpectedly happen within the different phases. One way or another, subrequests split bigger tasks into smaller ones which allow a clearer view of each task as a whole. @subsection name Naming conventions There is a naming convention which is not obligatory but it is followed in this tutorial: - Functions triggered before the event happens. These establish a request. - \b foo_send(...) - this function is called first and it includes the creation of tevent request - tevent req structure. It does not block anything, it simply creates a request, sets a callback (foo done) and lets the program continue - Functions as a result of event. - \b foo_done(...) - this function contains code providing for handling itself and based upon its results, the request is set either as a done or, if an error occurs, the request is set as a failure. - \b foo_recv(...) - this function contains code which should, if demanded, access the result data and make them further visible. The foo state should be deallocated from memory when the request’s processing is over and therefore all computed data up to this point would be lost. As was already mentioned, specific naming subsumes not only functions but also the data themselves: - \b foo_state - this is a structure. It contains all the data necessary for the asynchronous task. @subsection cr_req Creating a New Asynchronous Request The first step for working asynchronously is the allocation of memory requirements. As in previous cases, the talloc context is required, upon which the asynchronous request will be tied. The next step is the creation of the request itself. @code struct tevent_req* tevent_req_create (TALLOC_CTX *mem_ctx, void **pstate, #type) @endcode The pstate is the pointer to the private data. The necessary amount of memory (based on data type) is allocated during this call. Within this same memory area all the data from the asynchronous request that need to be preserved for some time should be kept. Dealing with a lack of memory The verification of the returned pointer against NULL is necessary in order to identify a potential lack of memory. There is a special function which helps with this check tevent_req_nomem(). It handles verification both of the talloc memory allocation and of the associated tevent request, and is therefore a very useful function for avoiding unexpected situations. It can easily be used when checking the availability of further memory resources that are required for a tevent request. Imagine an example where additional memory needs arise although no memory resources are currently available. @code bar = talloc(mem_ctx, struct foo); if(tevent_req_nomem (bar, req)) { // handling a problem } @endcode This code ensures that the variable bar, which contains NULL as a result of the unsuccessful satisfaction of its memory requirements, is noticed, and also that the tevent request req declares it exceeds memory capacity, which implies the impossibility of finishing the request as originally programmed. @subsection fini_req Finishing a Request Marking each request as finished is an essential principle of the tevent library. Without marking the request as completed - either successfully or with an error - the tevent loop could not let the appropriate callback be triggered. It is important to understand that this would be a significant threat, because it is not usually a question of one single function which prints some text on a screen, but rather the request is itself probably just a link in a series of other requests. Stopping one request would stop the others, memory resources would not be freed, file descriptors might remain open, communication via socket could be interrupted, and so on. Therefore it is important to think about finishing requests, either successfully or not, and also to prepare functions for all possible scenarios, so that the the callbacks do not process data that are actually invalid or, even worse, in fact non-existent meaning that a segmentation fault may arise.