From 1bce2510ca9a2eec20f2a32499f0839b768f7c4a Mon Sep 17 00:00:00 2001 From: David Koňař Date: Wed, 12 Jun 2013 13:54:36 +0200 Subject: tevent: Add tevent tutorial files. Reviewed-by: Andreas Schneider Reviewed-by: Volker Lendecke --- lib/tevent/doc/img/tevent_context_stucture.png | Bin 0 -> 21888 bytes lib/tevent/doc/img/tevent_subrequest.png | Bin 0 -> 22453 bytes lib/tevent/doc/tevent_context.dox | 75 ++++++ lib/tevent/doc/tevent_data.dox | 133 ++++++++++ lib/tevent/doc/tevent_events.dox | 341 +++++++++++++++++++++++++ lib/tevent/doc/tevent_queue.dox | 274 ++++++++++++++++++++ lib/tevent/doc/tevent_request.dox | 191 ++++++++++++++ lib/tevent/doc/tevent_tutorial.dox | 20 ++ 8 files changed, 1034 insertions(+) create mode 100644 lib/tevent/doc/img/tevent_context_stucture.png create mode 100644 lib/tevent/doc/img/tevent_subrequest.png create mode 100644 lib/tevent/doc/tevent_context.dox create mode 100644 lib/tevent/doc/tevent_data.dox create mode 100644 lib/tevent/doc/tevent_events.dox create mode 100644 lib/tevent/doc/tevent_queue.dox create mode 100644 lib/tevent/doc/tevent_request.dox create mode 100644 lib/tevent/doc/tevent_tutorial.dox (limited to 'lib') diff --git a/lib/tevent/doc/img/tevent_context_stucture.png b/lib/tevent/doc/img/tevent_context_stucture.png new file mode 100644 index 0000000000..fba8161572 Binary files /dev/null and b/lib/tevent/doc/img/tevent_context_stucture.png differ diff --git a/lib/tevent/doc/img/tevent_subrequest.png b/lib/tevent/doc/img/tevent_subrequest.png new file mode 100644 index 0000000000..ea79223d4d Binary files /dev/null and b/lib/tevent/doc/img/tevent_subrequest.png differ diff --git a/lib/tevent/doc/tevent_context.dox b/lib/tevent/doc/tevent_context.dox new file mode 100644 index 0000000000..1036d3c106 --- /dev/null +++ b/lib/tevent/doc/tevent_context.dox @@ -0,0 +1,75 @@ +/** +@page tevent_context Chapter 1: Tevent context + +@section context Tevent context + +Tevent context is an essential logical unit of tevent library. For working with +events at least one such context has to be created - allocated, initialized. +Then, events which are meant to be caught and handled have to be registered +within this specific context. Reason for subordinating events to a tevent +context structure rises from the fact that several context can be created and +each of them is processed at different time. So, there can be 1 context +containing just file descriptor events, another one taking care of signal and +time events and the third one which keeps information about the rest. + +Tevent loops are the part of the library which represents the mechanism where +noticing events and triggering handlers actually happens. They accept just one +argument - tevent context structure. Therefore if theoretically an infinity +loop (tevent_loop_wait) was called, only those arguments which belong to the +passed tevent context structure can be caught and invoked within this call. +Although some more signal events were registered (but within some other +context) they will not be noticed. + +@subsection Example + +First lines which handle mem_ctx belong to talloc library +knowledge but because of the fact that tevent uses the talloc library for its +mechanisms it is necessary to understand a bit talloc as well. For more +information about working with talloc, please visit talloc website where tutorial and +documentation are located. + +Tevent context structure *event_ctx represents the unit which will +further contain information about registered events. It is created via calling +tevent_context_init(). + +@code +TALLOC_CTX *mem_ctx = talloc_new(NULL); +if (mem_ctx == NULL) { + // error handling +} + +struct tevent_context *ev_ctx = tevent_context_init(mem_ctx); +if(ev_ctx == NULL) { + // error handling +} +@endcode + +Tevent context has a structure containing lots of information. It include lists +of all events which are divided according their type and are in order showing +the sequence as they came. + +@image html tevent_context_stucture.png + +In addition to the lists shown in the diagram, the tevent context also contains +many other data (e.g. information about the available system mechanism for +triggering callbacks). + +@section tevent_loops Tevent loops + +Tevent loops are the dispatcher for events. They catch them and trigger the +handlers. In the case of longer processes, the program spends most of its time +at this point waiting for events, invoking handlers and waiting for another +event again. There are 2 types of loop available for use in tevent library: + +
    +
  • int tevent_loop_wait()
  • +
  • int tevent_loop_once()
  • +
+ +Both of functions accept just one parametr (tevent context) and the only +difference lies in the fact that the first loop can theoretically last for ever +but the second one will wait just for a single one event to catch and then the +loop breaks and the program continue. + +*/ diff --git a/lib/tevent/doc/tevent_data.dox b/lib/tevent/doc/tevent_data.dox new file mode 100644 index 0000000000..4ee4ac2266 --- /dev/null +++ b/lib/tevent/doc/tevent_data.dox @@ -0,0 +1,133 @@ +/** +@page tevent_data Chapter 3: Accessing data +@section data Accessing data with tevent + +A tevent request is (usually) created together with a structure for storing the +data necessary for an asynchronous computation. For these private data, tevent +library uses void (generic) pointers, therefore any data type can be very +simply pointed at. However, this attitude requires clear and guaranteed +knowledge of the data type that will be handled, in advance. Private data can +be of 2 types: connected with a request itself or given as an individual +argument to a callback. It is necessary to differentiate these types, because +there is a slightly different method of data access for each. There are two +possibilities how to access data that is given as an argument directly to a +callback. The difference lies in the pointer that is returned. In one case it +is the data type specified in the function’s argument, in another void* is +returned. + +@code +void tevent_req_callback_data (struct tevent_req *req, #type) +void tevent_req_callback_data_void (struct tevent_req *req) +@endcode + + +To obtain data that are strictly bound to a request, this function is the only +direct procedure. + +@code +void *tevent_req_data (struct tevent_req *req, #type) +@endcode + +Example with both calls which differs between private data within tevent +request and data handed over as an argument. + +@code +#include +#include +#include + +struct foo_state { + int x; +}; + +struct testA { + int y; +}; + + +static void foo_done(struct tevent_req *req) { +// a->x contains 9 +struct foo_state *a = tevent_req_data(req, struct foo_state); + +// b->y contains 10 +struct testA *b = tevent_req_callback_data(req, struct testA); + +// c->y contains 10 +struct testA *c = (struct testA *)tevent_req_callback_data_void(req); + +printf("a->x: %d\n", a->x); +printf("b->y: %d\n", b->y); +printf("c->y: %d\n", c->y); +} + + +struct tevent_req * foo_send(TALLOC_CTX *mem_ctx, struct tevent_context *event_ctx) { + +printf("_send\n"); +struct tevent_req *req; +struct foo_state *state; + +req = tevent_req_create(event_ctx, &state, struct foo_state); +state->x = 10; + +return req; +} + +static void run(struct tevent_context *ev, struct tevent_timer *te, + struct timeval current_time, void *private_data) { + struct tevent_req *req; + struct testA *tmp = talloc(ev, struct testA); + tmp->y = 9; + req = foo_send(ev, ev); + + tevent_req_set_callback(req, foo_done, tmp); + tevent_req_done(req); + +} + +int main (int argc, char **argv) { + + struct tevent_context *event_ctx; + struct testA *data; + TALLOC_CTX *mem_ctx; + struct tevent_timer *time_event; + + mem_ctx = talloc_new(NULL); //parent + if (mem_ctx == NULL) + return EXIT_FAILURE; + + event_ctx = tevent_context_init(mem_ctx); + if (event_ctx == NULL) + return EXIT_FAILURE; + + data = talloc(mem_ctx, struct testA); + data->y = 10; + + time_event = tevent_add_timer(event_ctx, + mem_ctx, + tevent_timeval_current(), + run, + data); + if (time_event == NULL) { + fprintf(stderr, " FAILED\n"); + return EXIT_FAILURE; + } + + tevent_loop_once(event_ctx); + + talloc_free(mem_ctx); + + printf("Quit\n"); + return EXIT_SUCCESS; +} +@endcode + +Output of this example is: + +@code +a->x: 9 +b->y: 10 +c->y: 10 +@endcode + +*/ diff --git a/lib/tevent/doc/tevent_events.dox b/lib/tevent/doc/tevent_events.dox new file mode 100644 index 0000000000..8e350d2b93 --- /dev/null +++ b/lib/tevent/doc/tevent_events.dox @@ -0,0 +1,341 @@ +/** +@page tevent_events Chapter 2: Tevent events +@section pools Tevent events + +Ok, after reading previous chapter we can start doing something useful. So, the +way of creating events is similar for all types - signals, file descriptors, +time or immediate events. At the beginning it is good to know about some +typedefs which are set in tevent library and which specify the arguments for +each callback. These callbacks are: + +- tevent_timer_handler_t() + +- tevent_immediate_handler_t() + +- tevent_signal_handler_t() + +- tevent_fd_handler_t() + +According their names it is obvious that for creating callback for e.g. time +event, tevent_timer_handler_t will be used. + +The best way how to introduce registering an event and setting up a callback +would be example, so examples describing all the types of events follow. + +@subsection Time Time event + +This example shows how to set up an event which will be repeated for a minute +with interval of 2 seconds (will be triggered 30 times). After exceeding this +limit, the event loop will finish and all the memory resources will be freed. +This is just example describing repeated activity, nothing usefull is done +within foo function + +@code +#include +#include +#include +#include + +struct state { + struct timeval endtime; + int counter; + TALLOC_CTX *ctx; +}; + +static void callback(struct tevent_context *ev, struct tevent_timer *tim, + struct timeval current_time, void *private_data) +{ + struct state *data = talloc_get_type(private_data, struct state); + struct tevent_timer *time_event; + struct timeval schedule; + + printf("Data value: %d\n", data->counter); + data->counter += 1; // increase counter + + // if time has not reached its limit, set another event + if (tevent_timeval_compare(¤t_time, &(data->endtime)) < 0) { + // do something + // set repeat with delay 2 seconds + schedule = tevent_timeval_current_ofs(2, 0); + time_event = tevent_add_timer(ev, data->ctx, schedule, callback, data); + if (time_event == NULL) { // error ... + fprintf(stderr, "MEMORY PROBLEM\n"); + return; + } + } else { + // time limit exceeded + } +} + +int main(void) { + struct tevent_context *event_ctx; + TALLOC_CTX *mem_ctx; + struct tevent_timer *time_event; + struct timeval schedule; + + mem_ctx = talloc_new(NULL); // parent + event_ctx = tevent_context_init(mem_ctx); + + struct state *data = talloc(mem_ctx, struct state); + + schedule = tevent_timeval_current_ofs(2, 0); // +2 second time value + data->endtime = tevent_timeval_add(&schedule, 60, 0); // one minute time limit + data->ctx = mem_ctx; + data->counter = 0; + + // add time event + time_event = tevent_add_timer(event_ctx, mem_ctx, schedule, callback, data); + if (time_event == NULL) { + fprintf(stderr, "FAILED\n"); + return EXIT_FAILURE; + } + + tevent_loop_wait(event_ctx); + talloc_free(mem_ctx); + return EXIT_SUCCESS; +} +@endcode + +Variable counter is only used for counting the number of triggered +functions. List of all available functions which tevent offers for working with +time are listed +here together +with their description. More detailed view at these functions is unnecessary +because their purpose and usage is quite simple and clear. + +@subsection Immediate Immediate event + +These events are, as their name indicates, activated and performed immediately. +It means that this kind of events have priority over others (except signal +events). So if there is a bulk of events registered and after that a +tevent loop is launched, then all the immediate events will be triggered before +the other events. Except other immediate events (and signal events) because +they are also processed sequentially - according the order they were scheduled. +Signals have the highest priority and therefore they are processed +preferentially. Therefore the expression immediate may not correspond exactly +to the dictionary definition of "something without delay" but rather "as soon +as possible" after all preceding immediate events. + +For creating an immediate event there is a small different which lies in the +fact that the creation of such event is done in 2 steps. One represents the +creation (memory allocation), the second one represents registering as the +event within some tevent context. + +@code +struct tevent_immediate *run(TALLOC_CTX* mem_ctx, + struct tevent_context event_ctx, + void * data) +{ + struct tevent_immediate *im; + + im = tevent_create_immediate(mem_ctx); + if (im == NULL) { + return NULL; + } + tevent_schedule_immediate(im, event_ctx, foo, data); + + return im; +} +@endcode + +Example which may be compiled and run representing the creation of immediate event. + +@code + +#include +#include +#include + +struct info_struct { + int counter; +}; + +static void foo(struct tevent_context *ev, struct tevent_immediate *im, + void *private_data) +{ + struct info_struct *data = talloc_get_type(private_data, struct info_struct); + printf("Data value: %d\n", data->counter); +} + +int main (void) { + struct tevent_context *event_ctx; + TALLOC_CTX *mem_ctx; + struct tevent_immediate *im; + + printf("INIT\n"); + + mem_ctx = talloc_new(NULL); + event_ctx = tevent_context_init(mem_ctx); + + struct info_struct *data = talloc(mem_ctx, struct info_struct); + + // setting up private data + data->counter = 1; + + // first immediate event + im = tevent_create_immediate(mem_ctx); + if (im == NULL) { + fprintf(stderr, "FAILED\n"); + return EXIT_FAILURE; + } + tevent_schedule_immediate(im, event_ctx, foo, data); + + tevent_loop_wait(event_ctx); + talloc_free(mem_ctx); + + return 0; +} +@endcode + +@subsection Signal Signal event + +This is an alternative to standard C library functions signal() or sigaction(). +The main difference that distinguishes these ways of treating signals is their +setting up of handlers for different time intervals of the running program. + +While standard C library methods for dealing with signals offer sufficient +tools for most cases, they are inadequate for handling signals within the +tevent loop. It could be necessary to finish certain tevent requests within the +tevent loop without interruption. If a signal was sent to a program at a moment +when the tevent loop is in progress, a standard signal handler would not return +processing to the application at the very same place and it would quit the +tevent loop for ever. In such cases, tevent signal handlers offer the +possibility of dealing with these signals by masking them from the rest of +application and not quitting the loop, so the other events can still be +processed. + +Tevent offers also a control function, which enables us to verify whether it is +possible to handle signals via tevent, is defined within tevent library and it +returns a boolean value revealing the result of the verification. + +@code +bool tevent_signal_support (struct tevent_context *ev) +@endcode + +Checking for signal support is not necessary, but if it is not guaranteed, this +is a good and easy control to prevent unexpected behaviour or failure of the +program occurring. Such a test of course does not have to be run every single +time you wish to create a signal handler, but simply at the beginning - during +the initialization procedures of the program. Afterthat, simply adapt to each +situation that arises. + +@code + +#include +#include +#include + +static void handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + + // Do something usefull + + printf("handling signal...\n"); + exit(EXIT_SUCCESS); +} + +int main (void) +{ + struct tevent_context *event_ctx; + TALLOC_CTX *mem_ctx; + struct tevent_signal *sig; + + mem_ctx = talloc_new(NULL); //parent + if (mem_ctx == NULL) { + fprintf(stderr, "FAILED\n"); + return EXIT_FAILURE; + } + + event_ctx = tevent_context_init(mem_ctx); + if (event_ctx == NULL) { + fprintf(stderr, "FAILED\n"); + return EXIT_FAILURE; + } + + if (tevent_signal_support(event_ctx)) { + // create signal event + sig = tevent_add_signal(event_ctx, mem_ctx, SIGINT, 0, handler, NULL); + if (sig == NULL) { + fprintf(stderr, "FAILED\n"); + return EXIT_FAILURE; + } + tevent_loop_wait(event_ctx); + } + + talloc_free(mem_ctx); + return EXIT_SUCCESS; +} +@endcode + + +@subsection File File descriptor event + +Support of events on file descriptors is mainly useful for socket communication +but it certainly works flawlessly with standard streams (stdin, stdout, stderr) + as well. Working asynchronously with file descriptors enables switching + within processing I/O operations. This ability may rise with a greater + number of I/O operations and such overlapping leads to enhancement of the + throughput. + +There are several other functions included in tevent API related to handling +file descriptors (there are too many functions defined within tevent therefore +just some of them are fully described within this thesis. The +declaration of the rest can be easily found on the library’s website or +directly from the source code): + +
    +
  • tevent_fd_set_close_fn() - can add another function to be called at the + moment when a structure tevent fd is freed.
  • +
  • tevent_fd_set_auto_close() - calling this function can simplify the + maintenance of file descriptors, because it instructs tevent to close the + appropriate file descriptor when the tevent fd structure is about to be + freed.
  • +
  • tevent_fd_get_flags() - returns flags which are set on the file descriptor + connected with this tevent fd structure.
  • +
  • tevent_fd_set_flags() - sets specified flags on the event’s file + descriptor.
  • +
+ +@code + +static void close_fd(struct tevent_context *ev, struct tevent_fd *fd_event, + int fd, void *private_data) +{ + // processing when fd_event is freed +} + +struct static void handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + // handling event; reading from a file descriptor + tevent_fd_set_close_fn (fd_event, close_fd); +} + +int run(TALLOC_CTX *mem_ctx, struct tevent_context *event_ctx, + int fd, uint16_t flags, char *buffer) +{ + struct tevent_fd* fd_event = NULL; + + if (flags & TEVENT_FD_READ) { + fd_event = tevent_add_fd(event_ctx, + mem_ctx, + fd, + flags, + handler, + buffer); + } + if (fd_event == NULL) { + // error handling + } + return tevent_loop_once(); +} +@endcode + +*/ diff --git a/lib/tevent/doc/tevent_queue.dox b/lib/tevent/doc/tevent_queue.dox new file mode 100644 index 0000000000..fef98c81e0 --- /dev/null +++ b/lib/tevent/doc/tevent_queue.dox @@ -0,0 +1,274 @@ +/** +@page tevent_queue Chapter 5: Tevent queue +@section queue Tevent queue + +There is a possibility that the dispatcher and its handlers may not be able to +handle all the incoming events as quickly as they arrive. One way to deal with +this situation is to buffer the received events by introducing an event queue +into the events stream, between the events generator and the dispatcher. Events +are added to the queue as they arrive, and the dispatcher pops them off the +beginning of the queue as fast as possible. In tevent library it is +similar, but the queue is not automatically set for any event. The queue has to +be created on purpose, and events which should follow the order of the FIFO +queue have to be explicitly pinpointed. Creating such a queue is crucial in +situations when sequential processing is absolutely essential for the succesful +completion of a task, e.g. for a large quantity of data that are about to be +written from a buffer into a socket. The tevent library has its own queue +structure that is ready to use after it has been initialized and started up +once. + +@subsection cr_queue Creation of Queues + +The first and most important step is the creation of the tevent queue +(represented by struct tevent queue), which will then be in running mode. + +@code +struct tevent_queue* tevent_queue_create (TALLOC_CTX *mem_ctx, const char *name) +@endcode + +When the program returns from this function, the allocated memory, set +destructor and labeled queue as running has been done and the structure is +ready to be filled with entries. Stopping and starting queues on the run. If +you need to stop a queue from processing its entries, and then turn it on +again, a couple of functions which serve this purpose are: + +- bool tevent_queue_stop() +- bool tevent_queue_start() + +These functions actually only provide for the simple setting of a variable, +which indicates that the queue has been stopped/started. Returned value +indicates result. + +@subsection add_queue Adding Requests to a Queue + +Tevent in fact offers 3 possible ways of inserting a request into a queue. +There are no vast differences between them, but still there might be situations +where one of them is more suitable and desired than another. + +@code +bool tevent_queue_add(struct tevent_queue *queue, + struct tevent_context *ev, + struct tevent_req *req, + tevent_queue_trigger_fn_t trigger, + void *private_data) +@endcode + +This call is the simplest of all three. It offers only boolean verification of +whether the operation of adding the request into a queue was successful or not. +No additional deletion of an item from the queue is possible, i.e. it is only +possible to deallocate the whole tevent request, which would cause triggering +of destructor handling and also dropping the request from the queue. + +Extended Options + +Both of the following functions have a feature in common - they return tevent +queue entry structure representing the item in a queue. There is no further +possible handling with this structure except the use of the structure’s pointer +for its deallocation (which leads also its removal from the queue). The +difference lies in the possibility that with the following functions it is +possible to remove the tevent request from a queue without its deallocation. +The previous function can only deallocate the tevent request as it was from +memory, and thereby logically cause its removal from the queue as well. There +is no other utilization of this structure via API at this stage of tevent +library. The possibility of easier debugging while developing with tevent could +be considered to be an advantage of this returned pointer. + +@code +struct tevent_queue_entry *tevent_queue_add_entry(struct tevent_queue *queue, + struct tevent_context *ev, + struct tevent_req *req, + tevent_queue_trigger_fn_t trigger, + void *private_data) +@endcode + +The feature that allows for the optimized addition of entries to a queue is +that a check for an empty queue with no items is first of all carried out. If +it is found that the queue is empty, then the request for inserting the entry +into a queue will be omitted and directly triggered. + +@code +struct tevent_queue_entry *tevent_queue_add_optimize_empty(struct tevent_queue *queue, + struct tevent_context *ev, + struct tevent_req *req, + tevent_queue_trigger_fn_t trigger, + void *private_data) +@endcode + +When calling any of the functions serving for inserting an item into a queue, +it is possible to leave out the fourth argument (trigger) and instead of a +function pass a NULL pointer. This usage sets so-called blocking entries. +These entries, since they do not have any trigger operation to be activated, +just sit in their position until they are labeled as a done by another +function. Their purpose is to block other items in the queue from being +triggered. + +@subsection example_q Example of tevent queue + +@code +#include +#include +#include + +struct foo_state { + int local_var; + int x; +}; + +struct juststruct { + TALLOC_CTX * ctx; + struct tevent_context *ev; + int y; +}; + +int created = 0; + +static void timer_handler(struct tevent_context *ev, struct tevent_timer *te, + struct timeval current_time, void *private_data) +{ + // time event which after all sets request as done. Following item from + // the queue may be invoked. + struct tevent_req *req = private_data; + struct foo_state *stateX = tevent_req_data(req, struct foo_state); + + // processing some stuff + + printf("time_handler\n"); + + tevent_req_done(req); + talloc_free(req); + + printf("Request #%d set as done.\n", stateX->x); +} + +static void trigger(struct tevent_req *req, void *private_data) +{ + struct juststruct *priv = tevent_req_callback_data (req, struct juststruct); + struct foo_state *in = tevent_req_data(req, struct foo_state); + struct timeval schedule; + struct tevent_timer *tim; + schedule = tevent_timeval_current_ofs(1, 0); + printf("Processing request #%d\n", in->x); + + if (in->x % 3 == 0) { // just example; third request does not contain + // any further operation and will be finished right + // away. + tim = NULL; + } else { + tim = tevent_add_timer(priv->ev, req, schedule, timer_handler, req); + } + + if (tim == NULL) { + tevent_req_done(req); + talloc_free(req); + printf("Request #%d set as done.\n", in->x); + } +} + +struct tevent_req *foo_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *name, int num) +{ + struct tevent_req *req; + struct foo_state *state; + struct foo_state *in; + struct tevent_timer *tim; + + printf("foo_send\n"); + req = tevent_req_create(mem_ctx, &state, struct foo_state); + if (req == NULL) { // check for appropriate allocation + tevent_req_error(req, 1); + return NULL; + } + + // exemplary filling of variables + state->local_var = 1; + state->x = num; + + return req; +} + +static void foo_done(struct tevent_req *req) { + + enum tevent_req_state state; + uint64_t err; + + if (tevent_req_is_error(req, &state, &err)) { + printf("ERROR WAS SET %d\n", state); + return; + } else { + // processing some stuff + printf("Callback is done...\n"); + } +} + +int main (int argc, char **argv) +{ + TALLOC_CTX *mem_ctx; + struct tevent_req* req[6]; + struct tevent_req* tmp; + struct tevent_context *ev; + struct tevent_queue *fronta = NULL; + struct juststruct *data; + int ret; + int i = 0; + + const char * const names[] = { + "first", "second", "third", "fourth", "fifth" + }; + + printf("INIT\n"); + + mem_ctx = talloc_new(NULL); //parent + talloc_parent(mem_ctx); + ev = tevent_context_init(mem_ctx); + if (ev == NULL) { + fprintf(stderr, "MEMORY ERROR\n"); + return EXIT_FAILURE; + } + + // setting up queue + fronta = tevent_queue_create(mem_ctx, "test_queue"); + tevent_queue_stop(fronta); + tevent_queue_start(fronta); + if (tevent_queue_running(fronta)) { + printf ("Queue is runnning (length: %d)\n", tevent_queue_length(fronta)); + } else { + printf ("Queue is not runnning\n"); + } + + data = talloc(ev, struct juststruct); + data->ctx = mem_ctx; + data->ev = ev; + + + // create 4 requests + for (i = 1; i < 5; i++) { + req[i] = foo_send(mem_ctx, ev, names[i], i); + tmp = req[i]; + if (req[i] == NULL) { + fprintf(stderr, "Request error! %d \n", ret); + break; + } + tevent_req_set_callback(req[i], foo_done, data); + created++; + } + + // add item to a queue + tevent_queue_add(fronta, ev, req[1], trigger, data); + tevent_queue_add(fronta, ev, req[2], trigger, data); + tevent_queue_add(fronta, ev, req[3], trigger, data); + tevent_queue_add(fronta, ev, req[4], trigger, data); + + printf("Queue length: %d\n", tevent_queue_length(fronta)); + while(tevent_queue_length(fronta) > 0) { + tevent_loop_once(ev); + printf("Queue: %d items left\n", tevent_queue_length(fronta)); + } + + talloc_free(mem_ctx); + printf("FINISH\n"); + + return EXIT_SUCCESS; +} +@endcode + +*/ diff --git a/lib/tevent/doc/tevent_request.dox b/lib/tevent/doc/tevent_request.dox new file mode 100644 index 0000000000..14613846a8 --- /dev/null +++ b/lib/tevent/doc/tevent_request.dox @@ -0,0 +1,191 @@ +/** +@page tevent_request Chapter 4: Tevent request +@section request Tevent requst + +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. + +
    +
  • \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.
  • + +
  • +Setting up a timeout for request - 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 +
  • + + +
  • Premature Triggering - 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 . +
  • +
+ + +@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. + +Wrapper 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 + +Comprehensive example containing features from this chapter is especially by +reason of nested subrequests very long and therefore it is located as an +example on the attached CD. Description and explanation is placed within the +source code itself (subrequest.c) and enclosed file README. The example is +available here. + +*/ diff --git a/lib/tevent/doc/tevent_tutorial.dox b/lib/tevent/doc/tevent_tutorial.dox new file mode 100644 index 0000000000..9f01fa1062 --- /dev/null +++ b/lib/tevent/doc/tevent_tutorial.dox @@ -0,0 +1,20 @@ +/** +@page tevent_tutorial The Tutorial + +@section tevent_tutorial_introduction Introduction + +Tutorial describing working with tevent library. + +@section tevent_tutorial_toc Table of contents + +@subpage tevent_context + +@subpage tevent_events + +@subpage tevent_data + +@subpage tevent_request + +@subpage tevent_queue + +*/ -- cgit