libevent reference Mannual III--working with events

FYI: http://www.wangafu.net/~nickm/libevent-book/TOC.html

Working with events

Libevent’s basic unit of operation is the event. Every event represents a set of conditions, including:

  • A file descriptor being ready to read from or write to.

  • A file descriptor becoming ready to read from or write to (Edge-triggered IO only).

  • A timeout expiring.

  • A signal occurring.

  • A user-triggered event.

Constructing event objects

To create a new event, use the event_new() interface.

Interface

 1 #define EV_TIMEOUT      0x01
 2 #define EV_READ         0x02
 3 #define EV_WRITE        0x04
 4 #define EV_SIGNAL       0x08
 5 #define EV_PERSIST      0x10
 6 #define EV_ET           0x20
 7 
 8 typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
 9 
10 struct event *event_new(struct event_base *base, evutil_socket_t fd, short what, event_callback_fn cb, void *arg);
11 
12 void event_free(struct event *event);

The event_new() function tries to allocate and construct a new event for use with base. The what argument is a set of the flags defined above. If fd >=0, it is the file that we’ll observe for read or write events. When the event is active, cb function will be invoked and passing it as arguments: fd, a bitfield of all the events that triggered.

Return: NULL when error or invalid arguments

All new events are initialized and non-pending. To make an event pending, call event_add() (documented below).

To deallocate an event, call event_free(). It is safe to call event_free() on an event that is pending or active: doing so makes the event non-pending and inactive before deallocating it.

Example
 1 #include <event2/event.h>
 2 
 3 void cb_func(evutil_socket_t fd, short what, void *arg)
 4 {
 5         const char *data = arg;
 6         printf("Got an event on socket %d:%s%s%s%s [%s]",
 7             (int) fd,
 8             (what&EV_TIMEOUT) ? " timeout" : "",
 9             (what&EV_READ)    ? " read" : "",
10             (what&EV_WRITE)   ? " write" : "",
11             (what&EV_SIGNAL)  ? " signal" : "",
12             data);
13 }
14 
15 void main_loop(evutil_socket_t fd1, evutil_socket_t fd2)
16 {
17         struct event *ev1, *ev2;
18         struct timeval five_seconds = {5,0};
19         struct event_base *base = event_base_new();
20 
21         /* The caller has already set up fd1, fd2 somehow, and make them
22            nonblocking. */
23 
24         ev1 = event_new(base, fd1, EV_TIMEOUT|EV_READ|EV_PERSIST, cb_func,
25            (char*)"Reading event");
26         ev2 = event_new(base, fd2, EV_WRITE|EV_PERSIST, cb_func,
27            (char*)"Writing event");
28 
29         event_add(ev1, &five_seconds);
30         event_add(ev2, NULL);
31         event_base_dispatch(base);
32 }
The event flags
EV_TIMEOUT

This flag indicates an event that becomes active after a timeout elapses.

The EV_TIMEOUT flag is ignored when constructing an event: you
can either set a timeout when you add the event, or not.  It is
set in the 'what' argument to the callback function when a timeout
has occurred.
EV_SIGNAL

Used to implement signal detection. See "Constructing signal events" below.

EV_PERSIST

Indicates that the event is persistent. See "About Event Persistence" below.

EV_ET

Indicates that the event should be edge-triggered, if the underlying event_base backend supports edge-triggered events. This affects the semantics of EV_READ and EV_WRITE.

About Event Persistence

By default, whenever a pending event becomes active, it becomes non-pending right before its callback is executed. Thus, if you want to make the event pending again, you can call event_add() on it again from inside the callback function.

If the EV_PERSIST flag is set on an event, however, the event is persistent. This means that event remains pending even when its callback is activated. If you want to make it non-pending from within its callback, you can call event_del() on it.

The timeout on a persistent event resets whenever the event’s callback runs. Thus, if you have an event with flags EV_READ|EV_PERSIST and a timeout of five seconds, the event will become active:

  • Whenever the socket is ready for reading.

  • Whenever five seconds have passed since the event last became active.

Creating an event as its own callback argument

Frequently, you might want to create an event that receives itself as a callback argument. You can’t just pass a pointer to the event as an argument to event_new(), though, because it does not exist yet. To solve this problem, you can use event_self_cbarg().

Interface
void *event_self_cbarg();

The event_self_cbarg() function returns a "magic" pointer which, when passed as an event callback argument, tells event_new() to create an event receiving itself as its callback argument.

Example
 1 #include <event2/event.h>
 2 
 3 static int n_calls = 0;
 4 
 5 void cb_func(evutil_socket_t fd, short what, void *arg)
 6 {
 7     struct event *me = arg;
 8 
 9     printf("cb_func called %d times so far.\n", ++n_calls);
10 
11     if (n_calls > 100)
12        event_del(me);
13 }
14 
15 void run(struct event_base *base)
16 {
17     struct timeval one_sec = { 1, 0 };
18     struct event *ev;
19     /* We're going to set up a repeating timer to get called called 100
20        times. */
21     ev = event_new(base, -1, EV_PERSIST, cb_func, event_self_cbarg());
22     event_add(ev, &one_sec);
23     event_base_dispatch(base);
24 }

This function can also be used with event_new(), evtimer_new(), evsignal_new(), event_assign(), evtimer_assign(), and evsignal_assign().

Timeout-only events

As a convenience, there are a set of macros beginning with evtimer_ that you can use in place of the event_* calls to allocate and manipulate pure-timeout events. Using these macros provides no benefit beyond improving the clarity of your code.

Interface
#define evtimer_new(base, callback, arg) \
    event_new((base), -1, 0, (callback), (arg))
#define evtimer_add(ev, tv) \
    event_add((ev),(tv))
#define evtimer_del(ev) \
    event_del(ev)
#define evtimer_pending(ev, tv_out) \
    event_pending((ev), EV_TIMEOUT, (tv_out))
Constructing signal events
Interface
#define evsignal_new(base, signum, cb, arg) \
    event_new(base, signum, EV_SIGNAL|EV_PERSIST, cb, arg)

The arguments are as for event_new, except that we provide a signal number instead of a file descriptor.

Example

struct event *hup_event;
struct event_base *base = event_base_new();

/* call sighup_function on a HUP signal */
hup_event = evsignal_new(base, SIGHUP, sighup_function, NULL);

Note that signal callbacks are run in the event loop after the signal occurs, so it is safe for them to call functions that you are not supposed to call from a regular POSIX signal handler.

 
Warning Don’t set a timeout on a signal event. It might not be supported. [FIXME: is this true?]

There are also a set of convenience macros you can use when working with signal events.

Interface

#define evsignal_add(ev, tv) \
    event_add((ev),(tv))
#define evsignal_del(ev) \
    event_del(ev)
#define evsignal_pending(ev, what, tv_out) \
    event_pending((ev), (what), (tv_out))
Caveats when working with signals

With current versions of Libevent, with most backends, only one event_base per process at a time can be listening for signals. If you add signal events to two event_bases at once ---even if the signals are different!--- only one event_base will receive signals.

The kqueue backend does not have this limitation.

Setting up events without heap-allocation

For performance and other reasons, some people like to allocate events as a part of a larger structure. For each use of the event, this saves them:

  • The memory allocator overhead for allocating a small object on the heap.

  • The time overhead for dereferencing the pointer to the struct event.

  • The time overhead from a possible additional cache miss if the event is not already in the cache.

Using this method risks breaking binary compatibility with other versions of Libevent, which may have different sizes for the event structure.

These are very small costs, and do not matter for most applications. You should just stick to using event_new() unless you know that you’re incurring a significant performance penalty for heap-allocating your events. Using event_assign() can cause hard-to-diagnose errors with future versions of Libevent if they use a larger event structure than the one you’re building with.

Interface
1 int event_assign(struct event *event, struct event_base *base,
2     evutil_socket_t fd, short what,
3 void (*callback)(evutil_socket_t, short, void *), void *arg);

All the arguments of event_assign() are as for event_new(), except for the event argument, which must point to an uninitialized event. It returns 0 on success, and -1 on an internal error or bad arguments.

Example
 1 #include <event2/event.h>
 2 /* Watch out!  Including event_struct.h means that your code will not
 3  * be binary-compatible with future versions of Libevent. */
 4 #include <event2/event_struct.h>
 5 #include <stdlib.h>
 6 
 7 struct event_pair {
 8          evutil_socket_t fd;
 9          struct event read_event;
10          struct event write_event;
11 };
12 void readcb(evutil_socket_t, short, void *);
13 void writecb(evutil_socket_t, short, void *);
14 struct event_pair *event_pair_new(struct event_base *base, evutil_socket_t fd)
15 {
16         struct event_pair *p = malloc(sizeof(struct event_pair));
17         if (!p) return NULL;
18         p->fd = fd;
19         event_assign(&p->read_event, base, fd, EV_READ|EV_PERSIST, readcb, p);
20         event_assign(&p->write_event, base, fd, EV_WRITE|EV_PERSIST, writecb, p);
21         return p;
22 }

You can also use event_assign() to initialize stack-allocated or statically allocated events.

WARNING

Never call event_assign() on an event that is already pending in an event base. Doing so can lead to extremely hard-to-diagnose errors. If the event is already initialized and pending, call event_del() on it before you call event_assign() on it again.

There are convenience macros you can use to event_assign() a timeout-only or a signal event:

Interface
1 #define evtimer_assign(event, base, callback, arg) \
2     event_assign(event, base, -1, 0, callback, arg)
3 #define evsignal_assign(event, base, signum, callback, arg) \
4     event_assign(event, base, signum, EV_SIGNAL|EV_PERSIST, callback, arg)

If you need to use event_assign() and retain binary compatibility with future versions of Libevent, you can ask the Libevent library to tell you at runtime how large a struct event should be:

Interface
size_t event_get_struct_event_size(void);

This function returns the number of bytes you need to set aside for a struct event. As before, you should only be using this function if you know that heap-allocation is actually a significant problem in your program, since it can make your code much harder to read and write.

Note that event_get_struct_event_size() may in the future give you a value smaller than sizeof(struct event). If this happens, it means that any extra bytes at the end of struct event are only padding bytes reserved for use by a future version of Libevent.

Here’s the same example as above, but instead of relying on the size of struct event from event_struct.h, we use event_get_struct_size() to use the correct size at runtime.

Example
 1 #include <event2/event.h>
 2 #include <stdlib.h>
 3 
 4 /* When we allocate an event_pair in memory, we'll actually allocate
 5  * more space at the end of the structure.  We define some macros
 6  * to make accessing those events less error-prone. */
 7 struct event_pair {
 8          evutil_socket_t fd;
 9 };
10 
11 /* Macro: yield the struct event 'offset' bytes from the start of 'p' */
12 #define EVENT_AT_OFFSET(p, offset) \
13             ((struct event*) ( ((char*)(p)) + (offset) ))
14 /* Macro: yield the read event of an event_pair */
15 #define READEV_PTR(pair) \
16             EVENT_AT_OFFSET((pair), sizeof(struct event_pair))
17 /* Macro: yield the write event of an event_pair */
18 #define WRITEEV_PTR(pair) \
19             EVENT_AT_OFFSET((pair), \
20                 sizeof(struct event_pair)+event_get_struct_event_size())
21 
22 /* Macro: yield the actual size to allocate for an event_pair */
23 #define EVENT_PAIR_SIZE() \
24             (sizeof(struct event_pair)+2*event_get_struct_event_size())
25 
26 void readcb(evutil_socket_t, short, void *);
27 void writecb(evutil_socket_t, short, void *);
28 struct event_pair *event_pair_new(struct event_base *base, evutil_socket_t fd)
29 {
30         struct event_pair *p = malloc(EVENT_PAIR_SIZE());
31         if (!p) return NULL;
32         p->fd = fd;
33         event_assign(READEV_PTR(p), base, fd, EV_READ|EV_PERSIST, readcb, p);
34         event_assign(WRITEEV_PTR(p), base, fd, EV_WRITE|EV_PERSIST, writecb, p);
35         return p;
36 }

Making events pending and non-pending

Once you have constructed an event, it won’t actually do anything until you have made it pending by adding it. You do this with event_add:

Interface
int event_add(struct event *ev, const struct timeval *tv);

Calling event_add on a non-pending event makes it pending in its configured base. The function returns 0 on success, and -1 on failure. If tv is NULL, the event is added with no timeout. Otherwise, tv is the size of the timeout in seconds and microseconds.

If you call event_add() on an event that is already pending, it will leave it pending, and reschedule it with the provided timeout. If the event is already pending, and you re-add it with the timeout NULL, event_add() will have no effect.

 
int event_del(struct event *ev);

Calling event_del on an initialized event makes it non-pending and non-active. If the event was not pending or active, there is no effect. The return value is 0 on success, -1 on failure.

Note If you delete an event after it becomes active but before its callback has a chance to execute, the callback will not be executed.
Interface
int event_remove_timer(struct event *ev);

Finally, you can remove a pending event’s timeout completely without deleting its IO or signal components. If the event had no timeout pending, event_remove_timer() has no effect. If the event had only a timeout but no IO or signal component, event_remove_timer() has the same effect as event_del(). The return value is 0 on success, -1 on failure.

Events with priorities

When multiple events trigger at the same time, Libevent does not define any order with respect to when their callbacks will be executed. You can define some events as more important than others by using priorities.

As discussed in an earlier section, each event_base has one or more priority values associated with it. Before adding an event to the event_base, but after initializing it, you can set its priority.

Interface
int event_priority_set(struct event *event, int priority);

The priority of the event is a number between 0 and the number of priorities in an event_base, minus 1. The function returns 0 on success, and -1 on failure.

When multiple events of multiple priorities become active, the low-priority events are not run. Instead, Libevent runs the high priority events, then checks for events again. Only when no high-priority events are active are the low-priority events run.

Example
 1 #include <event2/event.h>
 2 
 3 void read_cb(evutil_socket_t, short, void *);
 4 void write_cb(evutil_socket_t, short, void *);
 5 
 6 void main_loop(evutil_socket_t fd)
 7 {
 8   struct event *important, *unimportant;
 9   struct event_base *base;
10 
11   base = event_base_new();
12   event_base_priority_init(base, 2);
13   /* Now base has priority 0, and priority 1 */
14   important = event_new(base, fd, EV_WRITE|EV_PERSIST, write_cb, NULL);
15   unimportant = event_new(base, fd, EV_READ|EV_PERSIST, read_cb, NULL);
16   event_priority_set(important, 0);
17   event_priority_set(unimportant, 1);
18 
19   /* Now, whenever the fd is ready for writing, the write callback will
20      happen before the read callback.  The read callback won't happen at
21      all until the write callback is no longer active. */
22 }

When you do not set the priority for an event, the default is the number of queues in the event base, divided by 2.

Inspecting event status

Sometimes you want to tell whether an event has been added, and check what it refers to.

Interface
 1 int event_pending(const struct event *ev, short what, struct timeval *tv_out);
 2 
 3 #define event_get_signal(ev) /* ... */
 4 evutil_socket_t event_get_fd(const struct event *ev);
 5 struct event_base *event_get_base(const struct event *ev);
 6 short event_get_events(const struct event *ev);
 7 event_callback_fn event_get_callback(const struct event *ev);
 8 void *event_get_callback_arg(const struct event *ev);
 9 int event_get_priority(const struct event *ev);
10 
11 void event_get_assignment(const struct event *event,
12         struct event_base **base_out,
13         evutil_socket_t *fd_out,
14         short *events_out,
15         event_callback_fn *callback_out,
16         void **arg_out);

The event_pending function determines whether the given event is pending or active and returns all of the flags that the event is currently pending or active on. If tv_out is provided, and EV_TIMEOUT is set in what, and the event is currently pending or active on a timeout, then tv_out is set to hold the time when the event’s timeout will expire.

The event_get_fd() and event_get_signal() functions return the configured file descriptor or signal number for an event. The event_get_base() function returns its configured event_base. The event_get_events() function returns the event flags (EV_READ, EV_WRITE, etc) of the event. The event_get_callback() and event_get_callback_arg() functions return the callback function and argument pointer. The event_get_priority() function returns the event’s currently assigned priority.

The event_get_assignment() function copies all of the assigned fields of the event into the provided pointers. If any of the pointers is NULL, it is ignored.

Example
 1 #include <event2/event.h>
 2 #include <stdio.h>
 3 
 4 /* Change the callback and callback_arg of 'ev', which must not be
 5  * pending. */
 6 int replace_callback(struct event *ev, event_callback_fn new_callback,
 7     void *new_callback_arg)
 8 {
 9     struct event_base *base;
10     evutil_socket_t fd;
11     short events;
12 
13     int pending;
14 
15     pending = event_pending(ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT,
16                             NULL);
17     if (pending) {
18         /* We want to catch this here so that we do not re-assign a
19          * pending event.  That would be very very bad. */
20         fprintf(stderr,
21                 "Error! replace_callback called on a pending event!\n");
22         return -1;
23     }
24 
25     event_get_assignment(ev, &base, &fd, &events,
26                          NULL /* ignore old callback */ ,
27                          NULL /* ignore old callback argument */);
28 
29     event_assign(ev, base, fd, events, new_callback, new_callback_arg);
30     return 0;
31 }

Finding the currently running event

For debugging or other purposes, you can get a pointer to the currently running event.

Interface
struct event *event_base_get_running_event(struct event_base *base);

Note that this function’s behavior is only defined when it’s called from within the provided event_base’s loop. Calling it from another thread is not supported, and can cause undefined behavior.

Configuring one-off events

If you don’t need to add an event more than once, or delete it once it has been added, and it doesn’t have to be persistent, you can use event_base_once().

Interface
1 int event_base_once(struct event_base *, evutil_socket_t, short,
2 void (*)(evutil_socket_t, short, void *), void *, const struct timeval *);

This function’s interface is the same as event_new(), except that it does not support EV_SIGNAL or EV_PERSIST. The scheduled event is inserted and run with the default priority. When the callback is finally done, Libevent frees the internal event structure itself. The return value is 0 on success, -1 on failure.

Events inserted with event_base_once cannot be deleted or manually activated: if you want to be able to cancel an event, create it with the regular event_new() or event_assign() interfaces.

Manually activating an event

Rarely, you may want to make an event active even though its conditions have not triggered.

Interface
1 void event_active(struct event *ev, int what, short ncalls);

This function makes an event ev become active with the flags what (a combination of EV_READ, EV_WRITE, and EV_TIMEOUT). The event does not need to have previously been pending, and activating it does not make it pending.

Warning: calling event_active() recursively on the same event may result in resource exhaustion. The following snippet of code is an example of how event_active can be used incorrectly.

Bad Example: making an infinite loop with event_active()
 1 struct event *ev;
 2 
 3 static void cb(int sock, short which, void *arg) {
 4         /* Whoops: Calling event_active on the same event unconditionally
 5            from within its callback means that no other events might not get
 6            run! */
 7 
 8         event_active(ev, EV_WRITE, 0);
 9 }
10 
11 int main(int argc, char **argv) {
12         struct event_base *base = event_base_new();
13 
14         ev = event_new(base, -1, EV_PERSIST | EV_READ, cb, NULL);
15 
16         event_add(ev, NULL);
17 
18         event_active(ev, EV_WRITE, 0);
19 
20         event_base_loop(base, 0);
21 
22         return 0;
23 }

This creates a situation where the event loop is only executed once and calls the function "cb" forever.

Example: Alternative solution to the above problem using timers
 1 struct event *ev;
 2 struct timeval tv;
 3 
 4 static void cb(int sock, short which, void *arg) {
 5    if (!evtimer_pending(ev, NULL)) {
 6        event_del(ev);
 7        evtimer_add(ev, &tv);
 8    }
 9 }
10 
11 int main(int argc, char **argv) {
12    struct event_base *base = event_base_new();
13 
14    tv.tv_sec = 0;
15    tv.tv_usec = 0;
16 
17    ev = evtimer_new(base, cb, NULL);
18 
19    evtimer_add(ev, &tv);
20 
21    event_base_loop(base, 0);
22 
23    return 0;
24 }

Example: Alternative solution to the above problem using event_config_set_max_dispatch_interval()

 1 struct event *ev;
 2 
 3 static void cb(int sock, short which, void *arg) {
 4         event_active(ev, EV_WRITE, 0);
 5 }
 6 
 7 int main(int argc, char **argv) {
 8         struct event_config *cfg = event_config_new();
 9         /* Run at most 16 callbacks before checking for other events. */
10         event_config_set_max_dispatch_interval(cfg, NULL, 16, 0);
11         struct event_base *base = event_base_new_with_config(cfg);
12         ev = event_new(base, -1, EV_PERSIST | EV_READ, cb, NULL);
13 
14         event_add(ev, NULL);
15 
16         event_active(ev, EV_WRITE, 0);
17 
18         event_base_loop(base, 0);
19 
20         return 0;
21 }

Optimizing common timeouts

Current versions of Libevent use a binary heap algorithm to keep track of pending events' timeouts. A binary heap gives performance of order O(lg n) for adding and deleting each event timeout. This is optimal if you’re adding events with a randomly distributed set of timeout values, but not if you have a large number of events with the same timeout.

For example, suppose you have ten thousand events, each of which should trigger its timeout five seconds after it was added. In a situation like this, you could get O(1) performance for each timeout by using a doubly-linked queue implementation.

Naturally, you wouldn’t want to use a queue for all of your timeout values, since a queue is only faster for constant timeout values. If some of the timeouts are more-or-less randomly distributed, then adding one of those timeouts to a queue would take O(n) time, which would be significantly worse than a binary heap.

Libevent lets you solve this by placing some of your timeouts in queues, and others in the binary heap. To do this, you ask Libevent for a special "common timeout" timeval, which you then use to add events having that timeval. If you have a very large number of events with a single common timeout, using this optimization should improve timeout performance.

Interface
1 const struct timeval *event_base_init_common_timeout(
2     struct event_base *base, const struct timeval *duration);

This function takes as its arguments an event_base, and the duration of the common timeout to initialize. It returns a pointer to a special struct timeval that you can use to indicate that an event should be added to an O(1) queue rather than the O(lg n) heap. This special timeval can be copied or assigned freely in your code. It will only work with the specific base you used to construct it. Do not rely on its actual contents: Libevent uses them to tell itself which queue to use.

Example
 1 #include <event2/event.h>
 2 #include <string.h>
 3 
 4 /* We're going to create a very large number of events on a given base,
 5  * nearly all of which have a ten-second timeout.  If initialize_timeout
 6  * is called, we'll tell Libevent to add the ten-second ones to an O(1)
 7  * queue. */
 8 struct timeval ten_seconds = { 10, 0 };
 9 
10 void initialize_timeout(struct event_base *base)
11 {
12     struct timeval tv_in = { 10, 0 };
13     const struct timeval *tv_out;
14     tv_out = event_base_init_common_timeout(base, &tv_in);
15     memcpy(&ten_seconds, tv_out, sizeof(struct timeval));
16 }
17 
18 int my_event_add(struct event *ev, const struct timeval *tv)
19 {
20     /* Note that ev must have the same event_base that we passed to
21        initialize_timeout */
22     if (tv && tv->tv_sec == 10 && tv->tv_usec == 0)
23         return event_add(ev, &ten_seconds);
24     else
25         return event_add(ev, tv);
26 }

Telling a good event apart from cleared memory

Libevent provides functions that you can use to distinguish an initialized event from memory that has been cleared by setting it to 0 (for example, by allocating it with calloc() or clearing it with memset() or bzero()).

Interface
1 int event_initialized(const struct event *ev);
2 
3 #define evsignal_initialized(ev) event_initialized(ev)
4 #define evtimer_initialized(ev) event_initialized(ev)
Warning

These functions can’t reliably distinguish between an initialized event and a hunk of uninitialized memory. You should not use them unless you know that the memory in question is either cleared or initialized as an event.

Generally, you shouldn’t need to use these functions unless you’ve got a pretty specific application in mind. Events returned by event_new() are always initialized.

Example
 1 #include <event2/event.h>
 2 #include <stdlib.h>
 3 
 4 struct reader {
 5     evutil_socket_t fd;
 6 };
 7 
 8 #define READER_ACTUAL_SIZE() \
 9     (sizeof(struct reader) + \
10      event_get_struct_event_size())
11 
12 #define READER_EVENT_PTR(r) \
13     ((struct event *) (((char*)(r))+sizeof(struct reader)))
14 
15 struct reader *allocate_reader(evutil_socket_t fd)
16 {
17     struct reader *r = calloc(1, READER_ACTUAL_SIZE());
18     if (r)
19         r->fd = fd;
20     return r;
21 }
22 
23 void readcb(evutil_socket_t, short, void *);
24 int add_reader(struct reader *r, struct event_base *b)
25 {
26     struct event *ev = READER_EVENT_PTR(r);
27     if (!event_initialized(ev))
28         event_assign(ev, b, r->fd, EV_READ, readcb, r);
29     return event_add(ev, NULL);
30 }

Obsolete event manipulation functions

Pre-2.0 versions of Libevent did not have event_assign() or event_new(). Instead, you had event_set(), which associated the event with the "current" base. If you had more than one base, you needed to remember to call event_base_set() afterwards to make sure that the event was associated with the base you actually wanted to use.

Interface
1 void event_set(struct event *event, evutil_socket_t fd, short what,
2         void(*callback)(evutil_socket_t, short, void *), void *arg);
3 int event_base_set(struct event_base *base, struct event *event);

The event_set() function was like event_assign(), except for its use of the current base. The event_base_set() function changes the base associated with an event.

There were variants of event_set() for dealing more conveniently with timers and signals: evtimer_set() corresponded roughly to evtimer_assign(), and evsignal_set() corresponded roughly to evsignal_assign().

 

 
 
 
 
 
 
 
 

 

转载于:https://www.cnblogs.com/tadeas/p/10118026.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值