190 lines
8.6 KiB
Plaintext
190 lines
8.6 KiB
Plaintext
|
|
/**
|
|||
|
|
@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 functions 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.
|
|||
|
|
|
|||
|
|
<b>Dealing with a lack of memory</b>
|
|||
|
|
|
|||
|
|
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.
|
|||
|
|
|
|||
|
|
<ul>
|
|||
|
|
<li>\b Manually - This is the most common type of finishing request. Calling
|
|||
|
|
this function sets the request as a TEVENT_REQ_DONE. This is the only purpose
|
|||
|
|
of this function and it should be used when everything went well. Typically it
|
|||
|
|
is used within the done functions.
|
|||
|
|
|
|||
|
|
@code
|
|||
|
|
void tevent_req_done (struct tevent_req *req)
|
|||
|
|
@endcode
|
|||
|
|
Alternatively, the request can end up being unsuccessful.
|
|||
|
|
@code
|
|||
|
|
bool tevent_req_error (struct tevent_req *req, uint64_t error)
|
|||
|
|
@endcode
|
|||
|
|
|
|||
|
|
The second argument takes the number of an error (declared by the programmer,
|
|||
|
|
for example in an enumerated variable). The function tevent_req_error() sets
|
|||
|
|
the status of the request as a TEVENT_REQ_USER_ERROR and also stores the code
|
|||
|
|
of error within the structure so it can be used, for example for debugging. The
|
|||
|
|
function returns true, if marking the request as an error was processed with no
|
|||
|
|
problem - value error passed to this function is not equal to 1.</li>
|
|||
|
|
|
|||
|
|
<li>
|
|||
|
|
<b>Setting up a timeout for request</b> - A request can be finished virtually,
|
|||
|
|
or if the process takes too much time, it can be timed out. This is considered
|
|||
|
|
as an error of the request and it leads to calling callback. In the
|
|||
|
|
background, this timeout is set through a time event (described in
|
|||
|
|
@subpage tevent_events ) which eventually triggers an operation marking the
|
|||
|
|
request as a TEVENT_REQ_TIMED_OUT (can not be considered as successfully
|
|||
|
|
finished). In case a time out was already set, this operation will overwrite it
|
|||
|
|
with a new time value (so the timeout may be lengthened) and if everything is
|
|||
|
|
set properly, it returns true.
|
|||
|
|
|
|||
|
|
@code
|
|||
|
|
bool tevent_req_set_endtime(struct tevent_req *req,
|
|||
|
|
struct tevent_context *ev,
|
|||
|
|
struct timeval endtime);
|
|||
|
|
@endcode
|
|||
|
|
</li>
|
|||
|
|
|
|||
|
|
|
|||
|
|
<li><b>Premature Triggering</b> - Imagine a situation in which some part of a
|
|||
|
|
nested subrequest ended up with a failure and it is still required to trigger a
|
|||
|
|
callback. Such as example might result from lack of memory leading to the
|
|||
|
|
impossibility of allocating enough memory requirements for the event to start
|
|||
|
|
processing another subrequest, or from a clear intention to skip other
|
|||
|
|
procedures and trigger the callback regardless of other progress. In these
|
|||
|
|
cases, the function tevent_req_post() is very handy and offers this option.
|
|||
|
|
|
|||
|
|
@code
|
|||
|
|
struct tevent_req* tevent_req_post (struct tevent_req *req,
|
|||
|
|
struct tevent_context *ev);
|
|||
|
|
@endcode
|
|||
|
|
|
|||
|
|
A request finished in this way does not behave as a time event nor as a file
|
|||
|
|
descriptor event but as a immediately scheduled event, and therefore it will be
|
|||
|
|
treated according the description laid down in @subpage tevent_events .
|
|||
|
|
</li>
|
|||
|
|
</ul>
|
|||
|
|
|
|||
|
|
|
|||
|
|
@section nested Subrequests - Nested Requests
|
|||
|
|
|
|||
|
|
To create more complex and interconnected asynchronous operations, it is
|
|||
|
|
possible to submerge a request into another and thus create a so-called
|
|||
|
|
subrequest. Subrequests are not represented by any other special structure but
|
|||
|
|
they are created from tevent_req_create(). This diagram shows the nesting and
|
|||
|
|
life time of each request. The table below describes the same in words, and
|
|||
|
|
shows the triggering of functions during the application run.
|
|||
|
|
|
|||
|
|
<i>Wrapper</i> represents the trigger of the whole cascade of (sub)requests. It
|
|||
|
|
may be e.g. a time or file descriptor event, or another request that was
|
|||
|
|
created at a specific time by the function tevent_wakeup_send() which is a
|
|||
|
|
slightly exceptional method of creating
|
|||
|
|
|
|||
|
|
@code
|
|||
|
|
struct tevent_req *tevent_wakeup_send(TALLOC_CTX *mem_ctx,
|
|||
|
|
struct tevent_context *ev,
|
|||
|
|
struct timeval wakeup_time);
|
|||
|
|
@endcode
|
|||
|
|
|
|||
|
|
By calling this function, it is possible to create a tevent request which is
|
|||
|
|
actually the return value of this function. In summary, it sets the time value
|
|||
|
|
of the tevent request’s creation. While using this function it is necessary to
|
|||
|
|
use another function in the subrequest’s callback to check for any problems
|
|||
|
|
tevent_wakeup_recv() )
|
|||
|
|
|
|||
|
|
@image html tevent_subrequest.png
|
|||
|
|
|
|||
|
|
A comprehensive example of nested subrequests can be found in the file
|
|||
|
|
echo_server.c. It implements a complete, self-contained echo server with no
|
|||
|
|
dependencies but libevent and libtalloc.
|
|||
|
|
|
|||
|
|
*/
|