tevent_events(3)
π NAME π
okay bestie, welcome to tevent_events(3) β this one lives in the Library Functions Manual, and its whole vibe is Chapter 2: Tevent events β¨
π¦ Tevent events π¦
okay so now that we've survived the previous chapter, we can finally do something actually useful π and here's the tea: making events is basically the same process no matter what type you're doing β signals, file descriptors, time events, immediate events, all of it. same recipe, different toppings π
first things first though, you gotta know about some typedefs that are set in the tevent library and that decide what arguments each callback gets. these callbacks are:
tevent_timer_handler_t()tevent_immediate_handler_t()tevent_signal_handler_t()tevent_fd_handler_t()
and like, their names are literally telling you everything β so if you wanna make a callback for, say, a time event, you're gonna be using tevent_timer_handler_t. no mystery, no cap π«Ά
the best way to show how registering an event and setting up a callback actually works is with examples, so here come examples covering every type of event. buckle up π
β° Time event
this example shows how to set up an event that repeats for a whole minute with a 2 second interval β so it fires 30 times. once it blows past that limit, the event loop wraps up and all the memory resources get freed. and yeah, it's just an example showing off repeated activity, nothing useful happens inside the foo function, it's giving demo energy only β¨
#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\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;
}
so the variable counter is literally just there to count how many times the function got triggered, that's its whole job π
and every function tevent offers for working with time is listed together with its description. honestly we don't need to deep-dive those functions individually because their purpose and usage is super simple and clear β it's giving self-explanatory, periodt.
β‘ Immediate event
these events are, exactly like the name says, activated and performed immediately. which means this kind of event has priority over the others β except signal events, of course π so if you've got a whole bulk of events registered and then you launch a tevent loop, all the immediate events get triggered before the other events. well β except for other immediate events (and signal events), because those are processed sequentially too, in the order they were scheduled. signals have the highest priority and that's why they get processed preferentially π
so basically the word "immediate" might not match the dictionary definition of "something without delay" so much as "as soon as possible" after all the preceding immediate events. semantics, bestie π
making an immediate event has one small difference: the creation happens in 2 steps. one step is the creation (memory allocation), the other step is registering it as an event within some tevent context. two-step slay π«Ά
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;
}
and here's an example you can actually compile and run that shows off creating an 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\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;
}
π¨ Signal event
this is an alternative to the standard C library functions signal() or sigaction(). the main difference that sets these ways of treating signals apart is how they set up handlers for different time intervals of the running program.
so like, standard C library methods for dealing with signals offer plenty of tools for most cases, but they're inadequate for handling signals inside the tevent loop π¬ it might be necessary to finish certain tevent requests within the tevent loop without interruption. if a signal got 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 β it would quit the tevent loop forever π and in those cases, tevent signal handlers give you the possibility of dealing with those signals by masking them from the rest of the application and not quitting the loop, so the other events can still be processed. iconic behaviour honestly.
tevent also offers a control function that lets us verify whether it's possible to handle signals via tevent β it's defined within the 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's not guaranteed, this is a good and easy control to prevent unexpected behaviour or failure of the program happening. and like, you don't have to run this test every single time you wanna create a signal handler β just once at the beginning, during the initialization procedures of the program. after that, just adapt to whatever situation comes up π
#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 useful
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;
}
π File descriptor event
support for events on file descriptors is mainly useful for socket communication, but it absolutely works flawlessly with standard streams (stdin, stdout, stderr) too, no cap π working asynchronously with file descriptors lets you switch around within processing I/O operations. and this ability may rise with a greater number of I/O operations, and that kind of overlapping leads to an enhancement of the throughput. it's giving efficiency queen π
there are several other functions included in the tevent API related to handling file descriptors (there are so many functions defined within tevent that only some of them are fully described here β 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.
static void close_fd(struct tevent_context *ev, struct tevent_fd *fd_event,
int fd, void *private_data)
{
// processing when fd_event is freed
}
okay so like, here's the actual code vibe of it all β a handler doing its thing, and a run that sets up an fd event and then just... asks the loop for one (1) single pass π«Ά watch:
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();
}
not the // error handling comment sitting there empty like a little todo ghost π β bestie, pls actually handle it, i'm begging. and then return tevent_loop_once(); at the very end, one loop pass, and we're out. iconic behavior, no notes π
and that's the whole page, bestie!! π you made it to the end, i'm so proud of you fr fr π this one's from tevent, Version 0.9.8, living under the name tevent_events(3) β go forth and loop responsibly, you absolute legend πβ¨
π¦ where this page comes from
- fedora 43: libtevent-devel 0.17.1-3.fc43 Β· LGPL-3.0-or-later
- fedora 43: libtevent-devel 0.17.1-3.fc43 Β· LGPL-3.0-or-later
- fedora 44: libtevent-devel 0.17.1-4.fc44 Β· LGPL-3.0-or-later
- fedora 44: libtevent-devel 0.17.1-4.fc44 Β· LGPL-3.0-or-later
- opensuse-leap 16.0: tevent-man 0.16.2-160000.2.2 Β· LGPL-3.0-or-later
- opensuse-leap 16.0: tevent-man 0.16.2-160000.2.2 Β· LGPL-3.0-or-later
- opensuse-leap 16.0: tevent-man 0.16.2-160000.2.2 Β· LGPL-3.0-or-later
- opensuse-leap 16.0: tevent-man 0.16.2-160000.2.2 Β· LGPL-3.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms