LiEvent Chinese help document -- Chapter 8 [create event]
Libevent
Fast portable non blocking network programming
Revision history | |||
edition | date | author | remarks |
V1.0 | 2016-11-15 | Zhou Yong | Libevent programming Chinese help document |
The document was created by Nick Mathewson from 2009 to 2012 based on the attribute noncommercial share like license agreement 3.0. The future version will use a less restrictive license
In addition, the source code example of this document is also based on the "3 clause" or "modification" clause of BSD For details, please refer to all terms of BSD documents Latest download address of this document:
english: http://libevent.org/
chinese: http://blog.csdn.net/zhouyongku/article/details/53431750
Please download and run "GI" tclonegit://github.com/nmathewson/libevent- book. Git "get the latest version of the source code described in this document
8. Handling events
The basic operating unit of libevent is event Each event represents a set of conditions, including:
-
The file descriptor is ready to read or write
-
The file descriptor becomes ready for reading or writing (for edge triggered IO only)
-
Timeout event
-
A signal occurs
-
User triggered event
All events have a similar life cycle Call the {libevent function to set the event and associate it with} event_ After base, the event enters the "initialized" state You can add the event to the event_base, which puts it in the pending state In the pending state, if the condition triggering the event occurs (for example, the state of the file descriptor changes, or the timeout time arrives), the event enters the "active" state, and the (user provided) event callback function will be executed If configured as persistent, the event will remain pending Otherwise, after the callback is executed, the event is no longer pending Deleting can make pending events non pending (initialized); Add operations can make non pending events pending again
8.1 constructing event objects
Use event_ The new () interface creates an event
Interface
#define EV_TIMEOUT 0x01 #define EV_READ 0x02 #define EV_WRITE 0x04 #define EV_SIGNAL 0x08 #define EV_PERSIST 0x10 #define EV_ET 0x20 typedef void ( * event_callback_fn)(evutil_socket_t, short, void* ); struct event* event_new(struct event_base * base, evutil_socket_t fd, short what, event_callback_fn cb,void* arg); void event_free(struct event* event);
event_new() attempts to allocate and construct a new event for base The what parameter is a collection of the above flags If fd is nonnegative, it is the file whose read and write events will be observed When the event is activated, libevent will call the cb function and pass these parameters: the file descriptor fd, the bit field representing all triggered events, and the arg parameter when constructing the event Event occurs when an internal error occurs or an invalid parameter is passed in_ New() will return NULL All newly created events are in an initialized and non pending state, and event is called_ Add () can make it pending To release the event, call event_free(). Call event on an event that is pending or active_ Free () is safe: before releasing an event, the function will make the event inactive and non pending
Interface
#include <event2/event.h> void cb_func(evutil_socket_t fd, short what, void* arg) { const char* data = arg; printf("Got an event on socket %d:%s%s%s%s [%s]", (int) fd, (what&EV_TIMEOUT) ? " timeout" : "", (what&EV_READ) ? " read" : "", (what&EV_WRITE) ? " write" : "", (what&EV_SIGNAL) ? " signal" : "", data); } void main_loop(evutil_socket_t fd1, evutil_socket_t fd2) { struct event* ev1, * ev2; struct timeval five_seconds = {5,0}; struct event_base * base = event_base_new(); /* The caller has already set up fd1, fd2 somehow, and make them nonblocking.*/ ev1 = event_new(base, fd1, EV_TIMEOUT|EV_READ|EV_PERSIST, cb_func, (char * )"Reading event"); ev2 = event_new(base, fd2, EV_WRITE|EV_PERSIST, cb_func, (char * )"Writing event"); event_add(ev1, &five_seconds); event_add(ev2, NULL); event_base_dispatch(base); }
The function is defined in < event2 / event h> It first appeared in the 2.0.1 - alpha version of libevent event_ callback_ The FN type first appeared as a typedef in version 2.0.4-alpha
8.1.1 event signs
-
EV_TIMEOUT: this flag indicates that the event becomes active after a timeout elapses When constructing events, ev_ The timeout flag is ignored: you can set the timeout when adding events or not When the timeout occurs, the what parameter of the callback function will carry this flag
-
EV_READ: indicates that the specified file descriptor is ready for reading, and the event will become active
-
EV_WRITE: indicates that the specified file descriptor is ready for writing, and the event will become active
-
EV_SIGNAL: used to realize signal detection. Please see the section "constructing signal events" below
-
EV_PERSIST: indicates that the event is "persistent". Please see the "about event persistence" section below
-
EV_ET: indicates if the underlying event_ If the base backend supports edge triggered events, the events should be triggered by edges This flag affects EV_READ and ev_ The semantics of write
Starting with version 2.0.1-alpha, any number of events can be pending because of the same conditions For example, two events can be activated because a given fd is ready to be read In this case, the order in which multiple event callbacks are executed is uncertain
These flags are defined in < event2 / event h> Yes Except ev_ Except for the introduction of ET in version 2.0.1-alpha, all flags have existed since version 1.0
8.1.2 about event persistence
By default, whenever a pending event becomes active (because fd is ready to read or write, or because of timeout), the event will become non pending before its callback is executed If you want the event to be pending again, you can call event again in the callback function_ add( ).
If EV is set_ The persist flag indicates that the event is persistent This means that the event remains pending even if its callback is activated If you want to make the event non pending in the callback, you can call event on it_ del( ). Each time an event callback is executed, the timeout value of the persistent event is reset
Therefore, if there is ev_ READ|EV_ With the persist flag and a timeout value of 5 seconds, the event becomes active when:
-
The socket is ready to be read
-
Five seconds have elapsed since the last time it became active
8.1.3 create an event with itself as the callback function parameter
In many cases, you want to create an event and use itself as a callback function parameter You cannot pass only a pointer to event as an argument to the function event_new(), because it doesn't actually exist yet. To solve this problem, you can use event_self_cbkarg() function
Interface
void* event_self_cbarg();
event_ self_ The cbarg () function returns a magic pointer passed into the event callback function as a parameter, which tells event_new() creates an event and takes itself as a callback function parameter
Example
#include <event2/event.h> static int n_calls = 0; void cb_func(evutil_socket_t fd, short what, void* arg) { struct event* me = arg; printf("cb_func called %d times so far.\n", ++n_calls); if (n_calls > 100) event_del(me); } void run(struct event_base* base) { struct timeval one_sec = { 1, 0 }; struct event* ev; /* We're going to set up a repeating timer to get called called 100 times.*/ ev = event_new(base, -1, EV_PERSIST, cb_func, event_self_cbarg()); event_add(ev, &one_sec); event_base_dispatch(base); }
This function can also be used for event_new(), evtimer_new(), evsignal_new(), event_assign(), evtimer_assign() and evsignal nal_ Assign(), but not as a parameter of the callback function of non events
event_ self_ The cbarg() function appears in LibEvent-2.1.2-alpha
8.1.4 timeout event
Compared with the previous way, there are a series of macros to evtimer_ The beginning macro can be used to allocate and control event s with pure timeout These macros make the code clearer and serve no other purpose
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))
Except evtimer_new() appears in libevent2 0.1-alpha, other macros from libevent0 6 begins to appear
8.1.5 construction signal events
LibEvent also supports monitoring POSIX format signals Construct a signal handle using the following method:
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: the signal callback function runs in the event loop after the signal occurs, so it is safe for them to call standard POSIX signal handles that you should not call
____________________________________________________________________________________________________
be careful
Do not set a timeout for an event This may not be supported (to be corrected: is this true?)
____________________________________________________________________________________________________
There are also a series of convenient macros used with the signal event
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))
evsignal_* Macros in libevent2 0.1-alpha, such as signal_add(),signal_del() and other functions
Warning when using signals
Most of the signals in the current version run in the background, and each process can only have one event_base can monitor signals at the same time. If two events are added to event at the same time_ Base, even if the signals are different, there will only be one event_ The base will receive the signal
kqueue running in the background does not have this restriction
8.1.6 setting events without heap allocation
For performance or other reasons, people are used to assigning events as part of a large structure For each event use, save the event as follows:
-
The overhead of allocating a small object on the heap by the memory allocator
-
The time cost of fetching from the pointer to event
-
The time overhead caused by cache loss due to event not in the cache
This method may destroy the binary compatibility between LibEvent versions because the size of event structure is not uniform between versions
This method has little overhead and will not affect the operation of other programs You should stick with event_new() to allocate events unless you suffer serious performance problems when allocating events on the heap If the event structure used in later versions is larger than the event structure you currently use, use event_assign() can lead to errors that are difficult to troubleshoot
Interface
int event_assign( struct event* event, struct event_base * base, evutil_socket_t fd, short what, void (*callback)(evutil_socket_t, short, void*), void * arg);
The event parameter must point to an uninitialized event_ The parameters of assign() are the same as {event_ The parameters of new() are the same The function returns 0 on success. If an internal error occurs or an incorrect parameter is used, the function returns - 1
Example
#include <event2/event.h> /* Watch out! Including event_struct.h means that your code will not be binary-compatible with future versions of Libevent.*/ #include <event2/event_struct.h> #include <stdlib.h> struct event_pair { evutil_socket_t fd; struct event read_event; struct event write_event; }; void readcb(evutil_socket_t, short, void* ); void writecb(evutil_socket_t, short, void* ); struct event_pair* event_pair_new(struct event_base * base, evutil_socket_t fd) { struct event_pair* p = malloc(sizeof(struct event_pair)); if (!p) return NULL; p->fd = fd; event_assign(&p->read_event, base, fd, EV_READ|EV_PERSIST, readcb, p); event_assign(&p->write_event, base, fd, EV_WRITE|EV_PERSIST, writecb, p); return p; }
You can also use event_assign() initializes events allocated on the stack or statically allocated
____________________________________________________________________________________________________
warning
Don't worry about what's already in the event_ The pending event in base calls event_assign(), which may lead to errors that are difficult to diagnose If it has been initialized and becomes pending, call event_ You need to call event before assign()_ del(). Libevent provides a convenient macro to create event_assign() is used only for timeout events or signal events
Interface
size_t event_get_struct_event_size(void);
This function returns the number of bytes of the event structure to be set Before that, if you realize that allocating memory on the heap for your program is actually a very important problem, you may only use this function because it will make your program more difficult to read and write
Note that event_get_struct_event_size() may give a larger value than the current sizeof(struct event) in the future version. If so, it means that the additional byte at the end of the event byte is a reserved padding byte for future LibEvent versions
There is also an example similar to the above, except that it does not rely on sizeof to calculate struct The byte size of the event structure in H, but event_get_struct_event_size() to calculate the correct value at runtime
Interface
#include <event2/event.h> #include <stdlib.h> /* When we allocate an event_pair in memory, we'll actually allocate more space at the end of the structure. We define some macros to make accessing those events less error-prone.*/ struct event_pair { evutil_socket_t fd; }; /* Macro: yield the struct event 'offset' bytes from the start of 'p'*/ #define EVENT_AT_OFFSET(p, offset) \ ((struct event * ) ( ((char * )(p)) + (offset) )) /* Macro: yield the read event of an event_pair*/ #define READEV_PTR(pair) \ EVENT_AT_OFFSET((pair), sizeof(struct event_pair)) /* Macro: yield the write event of an event_pair*/ #define WRITEEV_PTR(pair) \ EVENT_AT_OFFSET((pair), \ sizeof(struct event_pair)+event_get_struct_event_size()) /* Macro: yield the actual size to allocate for an event_pair*/ #define EVENT_PAIR_SIZE() \ (sizeof(struct event_pair)+2 * event_get_struct_event_size()) void readcb(evutil_socket_t, short, void* ); void writecb(evutil_socket_t, short, void* ); struct event_pair* event_pair_new(struct event_base * base, evutil_socket_t fd) { struct event_pair* p = malloc(EVENT_PAIR_SIZE()); if (!p) return NULL; p->fd = fd; event_assign(READEV_PTR(p), base, fd, EV_READ|EV_PERSIST, readcb, p); event_assign(WRITEEV_PTR(p), base, fd, EV_WRITE|EV_PERSIST, writecb, p); return p; }
From libevent2 0.1-alpha version, event_ The assign() function is defined in < event2 / event h> Yes Since 2.0.3, this function has returned an int, which has no return value before event_ get_ struct_ The size() function first appeared in libevent2 In the 0.4-alpha version The event structure is defined in < event2 / event h> In the file
8.2 pending and non pending events
Once you construct an event, it won't do anything until you add it to pending event_add do this:
Interface
int event_add(struct event* ev, const struct timeval * tv);
Call event on a non pending event_add() will make it in the configured event_ Become pending in base The function returns 0 on success and - 1 on failure If tv is NULL, the added event will not time out Otherwise, tv specifies the timeout value in seconds and microseconds If event is called on an event that is already pending_ Add(), the event will remain pending and will be rescheduled within the specified timeout
____________________________________________________________________________________________________
be careful
Do not set TV to the time you want the timeout event to execute If "TV - > tv_sec = time (null) + 10;" is set on January 1, 2010, The timeout event will wait 40 years instead of 10 seconds
____________________________________________________________________________________________________
Interface
int event_del(struct event* ev);
Call event on an event that has already been initialized_ Del() will make it non pending and inactive If the event is not pending or active, the call has no effect The function returns 0 on success and - 1 on failure
____________________________________________________________________________________________________
be careful
If the event is deleted after the event is activated and before its callback is executed, the callback will not be executed
____________________________________________________________________________________________________
Interface
int event_remove_timer(struct event* ev);
Finally, you can remove the timeout of an event without deleting IO or signal components If event does not time out pending, event_ remove_ The timer () function call has no effect If event has only timeout but no signal component, event_remove_timer() and event_del() has the same effect The function returns 0 successfully and - 1 if failed
These functions are defined in < event2 / event h> In, event_add() and event_del() function in libevent0 1 already has it, Eve_ remove_ Timer () was only added in version 2.1.2-alpha
8.3 event priority
When multiple events are triggered at the same time, LibEvent does not define any order to follow for the execution of the fallback function Priority can be used to define some events that are more important than others
In the previous discussion, we know that event_base has one or more priorities associated with it Add event to event in_ Before base, after initializing it, you can set its priority
Interface
int event_priority_set(struct event* event, int priority);
The priority of event is a value between 0 and the total number of priorities, with a minimum of 1 The function returns 0 successfully and - 1 failed
However, multiple priorities of multiple events start the activity, and the low priority events will not be executed, but the high priority events will be executed back, and then check the priority of the event again. The low priority events will not be run until the high priority events are not active
Interface
#include <event2/event.h> void read_cb(evutil_socket_t, short, void* ); void write_cb(evutil_socket_t, short, void* ); void main_loop(evutil_socket_t fd) { struct event* important, * unimportant; struct event_base* base; base = event_base_new(); event_base_priority_init(base, 2); /* Now base has priority 0, and priority 1*/ important = event_new(base, fd, EV_WRITE|EV_PERSIST, write_cb, NULL); unimportant = event_new(base, fd, EV_READ|EV_PERSIST, read_cb, NULL); event_priority_set(important, 0); event_priority_set(unimportant, 1); /* Now, whenever the fd is ready for writing, the write callback will happen before the read callback. The read callback won't happen at all until the write callback is no longer active.*/ }
When you do not set the priority of event, the default value is the total number of priority lists divided by 2
The function is defined in < event2 / event h> It first appeared in libevent1 0
8.4 check event status
Sometimes you want to judge whether an event is added and what it refers to
Interface
int event_pending(const struct event* ev, short what, struct timeval * tv_out); #define event_get_signal(ev) /* ...*/ evutil_socket_t event_get_fd(const struct event* ev); struct event_base* event_get_base(const struct event * ev); short event_get_events(const struct event* ev); event_callback_fn event_get_callback(const struct event* ev); void* event_get_callback_arg(const struct event * ev); int event_get_priority(const struct event* ev); void event_get_assignment( const struct event* event, struct event_base** base_out, evutil_socket_t* fd_out, short* events_out, event_callback_fn* callback_out, void** arg_out);
event_ The pending function determines whether the given event is pending or active If EV_READ,EV_WRITE,EV_SIGNAL,EV_ When timeout is set to the what parameter, the function will return all flags that event is pending or active If TV is provided_ Out and EV is set_ The timeout flag is given to the what parameter. The current event is pending or active on the timeout, tv_out is set to the time after saving event timeout
event_get_fd() function and event_ get_ The signal () function returns the file descriptor or model number of event configuration event_get_base() returns the configured event_ base. event_ get_ The events() function returns the flag of the event (EV_READ, EV_WRITE, etc.) event_get_callback() and event_ get_ callback_ The Arg () function returns the event return function and its parameter pointer event_ get_ The priority () function returns the currently assigned priority of the event
event_ get_ The assignment () function copies all the fields allocated by event to the provided pointer If the pointer is null, it is ignored
Example
#include <event2/event.h> #include <stdio.h> /* Change the callback and callback_arg of 'ev', which must not be pending.*/ int replace_callback(struct event* ev, event_callback_fn new_callback, void*new_callback_arg) { struct event_base* base; evutil_socket_t fd; short events; int pending; pending = event_pending(ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT,NULL); if (pending) { /* We want to catch this here so that we do not re-assign a pending event. That would be very very bad.*/ fprintf(stderr,"Error! replace_callback called on a pending event!\n"); return -1; } event_get_assignment(ev, &base, &fd, &events, NULL /* ignore old callback*/ , NULL /* ignore old callback argument*/); event_assign(ev, base, fd, events, new_callback, new_callback_arg); return 0; }
These functions are declared in < event2 / event h> Yes event_ The pending () function has existed since version 0.1 Event is introduced in version 2.0.1-alpha_ get_ FD () and event_ get_ Signal (). 2.0.2-alpha introduces event_get_base() . Other functions are introduced in version 2.0.4-alpha
8.5 find current running events
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 the behavior of this function is only in the event provided_ Base is defined when the loop is called internally Calling it on another thread is not supported and may cause undefined behavior
This function is defined in < event2 / event h> It first appeared in libevent2 In the 1.1-alpha version
8.6 configuring one-time events
If you don't want to add event repeatedly or delete it immediately after adding, it doesn't need to be persistent. You can use event_base_once() function
Interface
int event_base_once( struct event_base* , evutil_socket_t, short, void ( * )(evutil_socket_t, short, void* ), void * , const struct timeval * );
This function interfaces with event_new() is the same, except EV is not supported_ Signal or SIG_PERSIST flag Queue events are inserted and run at the default priority When the fallback function is finally completed, LibEvent releases its own internal event structure, returns 0 for success and - 1 for failure
With event_base_once inserted events cannot be deleted or manually activated: if you want to enable or cancel an event, use the commonly used event_new() or event_assign() interface
Note that in libevent2 Before 0, if event is never triggered, the memory used to save it will never be released From libevent2 1.2-alpha version starts when event_ Release events when base releases. Even if they are not activated, be aware that if there is memory associated with their return function parameters, your memory will not be released unless your program does something to track it or release it
8.7 manual activation events
There may be very few cases where you want event activation even if the event condition is not triggered
Interface
void event_active(struct event* ev, int what, short ncalls);
This function enables the event to be activated with the flag what (a combination of ev_read, EV_WRITE and EV_TIMEOUT). The event does not need to be pending in advance, nor does it need to be pending to activate the event
Bad example: use event_active() infinite loop
struct event* ev; static void cb(int sock, short which, void* arg) { /* Whoops: Calling event_active on the same event unconditionally from within its callback means that no other events might not getrun!*/ event_active(ev, EV_WRITE, 0); } int main(int argc, char** argv) { struct event_base * base = event_base_new(); ev = event_new(base, -1, EV_PERSIST | EV_READ, cb, NULL); event_add(ev, NULL); event_active(ev, EV_WRITE, 0); event_base_loop(base, 0); return 0; }
This creates an event loop that executes only once, and then calls the function "cb" forever
Example: timer alternative to the above problem
struct event* ev; struct timeval tv; static void cb(int sock, short which, void* arg) { if (!evtimer_pending(ev, NULL)) { event_del(ev); evtimer_add(ev, &tv); } } int main(int argc, char** argv) { struct event_base * base = event_base_new(); tv.tv_sec = 0; tv.tv_usec = 0; ev = evtimer_new(base, cb, NULL); evtimer_add(ev, &tv); event_base_loop(base, 0); return 0; }
Example: event of the above problem_ config_ set_ max_ dispatch_ Interval alternative
struct event* ev; static void cb(int sock, short which, void* arg) { event_active(ev, EV_WRITE, 0); } int main(int argc, char** argv) { struct event_config* cfg = event_config_new(); /* Run at most 16 callbacks before checking for other events.*/ event_config_set_max_dispatch_interval(cfg, NULL, 16, 0); struct event_base * base = event_base_new_with_config(cfg); ev = event_new(base, -1, EV_PERSIST | EV_READ, cb, NULL); event_add(ev, NULL); event_active(ev, EV_WRITE, 0); event_base_loop(base, 0); return 0; }
This function is defined in < event2 / event h> In, it has existed since version 0.3
8.8 optimize general timeout
The current version of libevent uses binary heap algorithm to track the timeout value of pending events, which makes adding and deleting event timeout values have O(logN) performance This is optimized for a randomly distributed set of timeout values, but not for a large number of event sets with the same timeout value
For example, suppose there are 10000 events, each of which needs to trigger a timeout event 5 seconds after adding In this case, O(1) performance can only be achieved by using double chain queue implementation
In fact, you do not want to use queues for all timeout values, because queues are only faster for constant timeout values If the timeout values are more or less randomly distributed, the performance of adding timeout values to the queue will be O(n), which is obviously much worse than using binary heap
Libevent solves this problem by putting some timeout values into the queue and others into the binary heap To use this mechanism, you need to request a "common timeout" value from libevent, and then use it to add events If there are a large number of events with a single common timeout value, using this optimization should improve timeout processing performance
Interface
const struct timeval* event_base_init_common_timeout( struct event_base* base, const struct timeval * duration);
This function requires event_base and the common timeout value to initialize as parameters The function returns a pointer to a special timeval structure. You can use this pointer to indicate that the event should be added to the O(1) queue instead of the O(logN) heap This particular timeval can be freely copied or assigned in the code, but it is only for the specific event used to construct it_ Base is valid You can't rely on its actual content: libevent uses this content to tell itself which queue to use
Example
#include <event2/event.h> #include <string.h> /* We're going to create a very large number of events on a given base, nearly all of which have a ten-second timeout. If initialize_timeout is called, we'll tell Libevent to add the ten-second ones to an O(1) queue.*/ struct timeval ten_seconds = { 10, 0 }; void initialize_timeout(struct event_base* base) { struct timeval tv_in = { 10, 0 }; const struct timeval * tv_out; tv_out = event_base_init_common_timeout(base, &tv_in); memcpy(&ten_seconds, tv_out, sizeof(struct timeval)); } int my_event_add(struct event* ev, const struct timeval * tv) { /* Note that ev must have the same event_base that we passed to initialize_timeout */ if (tv && tv->tv_sec == 10 && tv->tv_usec == 0) return event_add(ev, &ten_seconds); else return event_add(ev, tv); }
As with all optimization functions, you should avoid using the common timeout function unless you are sure it is suitable for use This function was introduced in version 2.0.4-alpha
8.9 identifying events from cleared memory
libevent provides functions to identify initialized events from memory that has been cleared by setting to 0 (for example, allocated by calloc(), or cleared by memset() or bzero())
Interface
int event_initialized(const struct event* ev); #define evsignal_initialized(ev) event_initialized(ev) #define evtimer_initialized(ev) event_initialized(ev)
Warning this function cannot reliably identify initialized events from uninitialized memory blocks Use this function unless you know that the queried memory is either cleared or initialized as an event
Unless you write a very special application, you usually don't need to use this function event_ The event returned by new () is always initialized
Interface
#include <event2/event.h> #include <stdlib.h> struct reader { evutil_socket_t fd; }; #define READER_ACTUAL_SIZE() \ (sizeof(struct reader) + \ event_get_struct_event_size()) #define READER_EVENT_PTR(r) \ ((struct event* ) (((char * )(r))+sizeof(struct reader))) struct reader* allocate_reader(evutil_socket_t fd) { struct reader* r = calloc(1, READER_ACTUAL_SIZE()); if (r) r->fd = fd; return r; } void readcb(evutil_socket_t, short, void* ); int add_reader(struct reader* r, struct event_base * b) { struct event* ev = READER_EVENT_PTR(r); if (!event_initialized(ev)) event_assign(ev, b, r->fd, EV_READ, readcb, r); return event_add(ev, NULL); }
event_ The initialized () function has existed since version 0.3
8.10 obsolete event operation functions
libevent before 2.0 does not have event_assign() or event_new() . Instead, associate the event to the current event_ Event of base_ set ( ) . If there are multiple events_ Base, remember to call event_base_set() to determine that the event is indeed associated with the currently used event_base
Interface
void event_set(struct event* event, evutil_socket_t fd, short what, void( * callback)(evutil_socket_t, short, void* ), void * arg); int event_base_set(struct event_base* base, struct event * event);
In addition to using the current event_base, event_set() and event_assign () is similar
event_base_set() is used to modify the event to which the event is associated_ base. event_ Set () has a few variants for easier processing of timers and signals: evtimer_set() roughly corresponds to evtimer_assign() .
Libevent before 2.0 uses "signal" As an event for the signal_ Prefix of function variants such as set() instead of "evsignal_" (that is, there are signal_set(), signal_add(),signal_del(),signal_pending() and signal_initialized()) . The ancient version (before version 0.6) of libevent uses "timeout_" Not "evtimer_" Therefore, you may see a timeout when doing code Archaeology (Note: this translation seems incorrect, is there a more professional term? For example, "code review")_ add(),timeout_del(),timeout_initialized(),timeout_set() and timeout_pending() and so on
The macro event is used for libevent in older versions (before version 2.0)_ FD () and EVENT_SIGNAL() replaces the current event_get_fd() and event_get_signal() function These two macros directly check the contents of the event structure, which will hinder binary compatibility between different versions In version 2.0 and later, these two macros are just events_ get_ FD () and event_ get_ Alias for signal()
Since versions before 2.0 do not support locks, event is running_ It is not safe for any thread other than the thread of base to call the function to modify the event state These functions include event_add(),event_del(),event_active() and event_base_once().
There is an event_once() and event_base_once() is similar, but is used for the current event_base.
EV before version 2.0_ The persist flag does not timeout correctly The flag does not reset the timeout value when the event is activated, but there is no action
Versions before 2.0 do not support adding multiple events with the same fd and READ/WRITE flags at the same time In other words, on each fd, there can only be one event waiting to be read and one event waiting to be written at a certain time