mirror of
https://github.com/samba-team/samba.git
synced 2025-01-11 05:18:09 +03:00
6a081e9919
Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Andrew Bartlett <abartlet@samba.org> Autobuild-User(master): Andrew Bartlett <abartlet@samba.org> Autobuild-Date(master): Mon Aug 12 09:17:10 CEST 2013 on sn-devel-104
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 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.
|
||
|
||
<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.
|
||
|
||
*/
|