forked from libevent/libevent-book
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Ref3_eventloop.txt
233 lines (181 loc) · 7.7 KB
/
Ref3_eventloop.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
include::license.txt[]
:language: C
Working with an event loop
--------------------------
Running the loop
~~~~~~~~~~~~~~~~
Once you have an event_base with some events registered (see the next section
about how to create and register events), you will want Libevent to wait for
events and alert you about them.
.Interface
[code,C]
--------
#define EVLOOP_ONCE 0x01
#define EVLOOP_NONBLOCK 0x02
int event_base_loop(struct event_base *base, int flags);
--------
By default, the event_base_loop() function 'runs' an event_base until there
are no more events registered in it. To run the loop, it repeatedly checks
whether any of the registered events has triggered (for example, if a read
event's file descriptor is ready to read, or if a timeout event's timeout is
ready to expire). Once this happens, it marks all triggered events as
"active", and starts to run them.
You can change the behavior of event_base_loop() by setting one or more flags
in its 'flags' argument. If EVLOOP_ONCE is set, then the loop will stop
immediately after running any active events. If EVLOOP_NONBLOCK is set, then
the loop will not wait for events to trigger: it will only check whether
any events are ready to trigger immediately.
When it is done, event_base_loop() returns 0 if it exited normally, and -1 if
it exited because of some unhandled error in the backend.
To aid in understanding, here's an approximate summary of the event_base_loop
algorithm:
.Pseudocode
[code,C]
--------
while (any events are registered with the loop) {
if (EVLOOP_NONBLOCK was set, or any events are already active)
If any registered events have triggered, mark them active.
else
Wait until at least one event has triggered, and mark it active.
for (p = 0; p < n_priorities; ++p {
if (any event with priority of p is active) {
Run all active events with priority of p.
break; /* Do not run any events of a less important priority */
}
}
if (EVLOOP_ONCE was set or EVLOOP_NONBLOCK was set)
break;
}
--------
As a convenience, you can also call:
.Interface
[code,C]
--------
int event_base_dispatch(struct event_base *base);
--------
The event_base_dispatch() call is the same as event_base_loop(), with no
flags set. Thus, it keeps running until there are no more registered events
or until event_base_loopbreak() or event_base_loopexit() is called.
These functions are defined in <event2/event.h>. They have existed since
before Libevent 1.0.
Stopping the loop
~~~~~~~~~~~~~~~~~
If you want an active event loop to stop running before all events are
removed from it, you have two slightly different functions you can call.
.Interface
[code,C]
--------
int event_base_loopexit(struct event_base *base,
const struct timeval *tv);
int event_base_loopbreak(struct event_base *base);
--------
The event_base_loopexit() function tells an event_base to stop looping after
a given time has elapsed. If the 'tv' argument is NULL, the event_base stops
looping without a delay. If the event_base is currently processing any active
events, it will continue processing them, and not exit until it is done.
The event_base_loopbreak() function tells the event_base to exit its loop
immediately. It differs from event_base_loopexit(base, NULL) in that if
the event_base is currently processing any active events, it will exit
immediately after finishing the one it's currently processing.
Both of these methods return 0 on success and -1 on failure.
.Example: Shut down immediately
[code,C]
--------
#include <event2/event.h>
/* Here's a callback function that calls loopbreak */
void cb(int sock, short what, void *arg)
{
struct event_base *base = arg;
event_base_loopbreak(base);
}
void main_loop(struct event_base *base, evutil_socket_t watchdog_fd)
{
struct event *watchdog_event;
/* Construct a new event to trigger whenever there are any bytes to
read from a watchdog socket. When that happens, we'll call the
cb function, which will make the loop exit immediately without
running any other active events at all.
*/
watchdog_event = event_new(base, watchdog_fd, EV_READ, cb, base);
event_add(watchdog_event, NULL);
event_base_dispatch(base);
}
--------
.Example: Run an event loop for 10 seconds, then exit.
[code,C]
--------
#include <event2/event.h>
void run_base_with_ticks(struct event_base *base)
{
struct timeval ten_sec;
ten_sec.tv_sec = 10;
ten_sec.tv_usec = 0;
/* Now we run the event_base for a series of 10-second intervals, printing
"Tick" after each. For a much better way to implement a 10-second
timer, see the section below about persistent timer events. */
while (1) {
/* This schedules an exit ten seconds from now. */
event_base_loopexit(base, &ten_sec);
event_base_dispatch(base);
puts("Tick");
}
}
--------
Sometimes you may want to tell whether your call to event_base_dispatch() or
event_base_loop() exited normally, or because of a call to
event_base_loopexit() or event_base_break(). You can use these functions to
tell whether loopexit or break was called:
.Interface
[code,C]
--------
int event_base_got_exit(struct event_base *base);
int event_base_got_break(struct event_base *base);
--------
These two functions will return true if the loop was stopped with
event_base_loopexit() or event_base_break() respectively, and false
otherwise. Their values will be reset the next time you start the event
loop.
These functions are declared in <event2/event.h>. The event_break_loopexit()
function was first implemented in Libevent 1.0c; event_break_loopbreak() was
first implemented in Libevent 1.4.3.
Checking the internal time cache
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes you want to get an approximate view of the current time inside
an event callback, and you want to get it without calling gettimeofday()
yourself (presumably because your OS implements gettimeofday() as a
syscall, and you're trying to avoid syscall overhead).
From within a callback, you can ask Libevent for its view of the current
time when it began executing this round of callbacks:
.Interface
[code,C]
-----
int event_base_gettimeofday_cached(struct event_base *base,
struct timeval *tv_out);
-----
The event_base_gettimeofday_cached() function sets the value of its
'tv_out' argument to the cached time if the event_base is currently
executing callbacks. Otherwise, it calls evutil_gettimeofday() for the
actual current time. It returns 0 on success, and negative on failure.
Note that since the timeval is cached when Libevent starts running
callbacks, it will be at least a little inaccurate. If your callbacks
take a long time to run, it may be *very* inaccurate.
This function was new in Libevent 2.0.4-alpha.
Obsolete event loop functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As discussed above, older versions of Libevent APIs had a global
notion of a "current" event_base.
Some of the event loop functions in this section had variants that
operated on the current base. These functions behaved as the current
functions, except that they took no base argument.
[options="header",width="85%"]
|======================================================================
| Current function | Obsolete current-base version
| event_base_dispatch() | event_dispatch()
| event_base_loop() | event_loop()
| event_base_loopexit() | event_loopexit()
| event_base_loopbreak() | event_loopbreak()
|======================================================================
NOTE: Because event_base did not support locking before Libevent 2.0,
these functions weren't completely threadsafe: it was not permissible
to call the _loopbreak() or _loopexit() functions from a thread other
than the one executing the event loop.