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:
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.
#include <stdio.h> #include <unistd.h> #include <tevent.h> #include <sys/time.h> 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_abort(private_data, struct state); struct tevent_timer *time_event; struct timeval schedule; printf("Data value: %d, 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); 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); return EXIT_FAILURE; } tevent_loop_wait(event_ctx); talloc_free(mem_ctx); return EXIT_SUCCESS; }
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.
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.
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; }
Example which may be compiled and run representing the creation of immediate event.
#include <stdio.h> #include <unistd.h> #include <tevent.h> 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_abort(private_data, struct info_struct); printf("Data value: %d, data->counter); } int main (void) { struct tevent_context *event_ctx; TALLOC_CTX *mem_ctx; struct tevent_immediate *im; printf("INIT); 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); return EXIT_FAILURE; } tevent_schedule_immediate(im, event_ctx, foo, data); tevent_loop_wait(event_ctx); talloc_free(mem_ctx); return 0; }
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.
bool tevent_signal_support (struct tevent_context *ev)
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.
#include <stdio.h> #include <tevent.h> #include <signal.h> 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...); 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); return EXIT_FAILURE; } event_ctx = tevent_context_init(mem_ctx); if (event_ctx == NULL) { fprintf(stderr, "FAILED); 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); return EXIT_FAILURE; } tevent_loop_wait(event_ctx); } talloc_free(mem_ctx); return EXIT_SUCCESS; }
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):
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(); }