https://github.com/nmathewson/libevent-book

Libevent is a library for writing fast portable nonblocking IO.

1 Design Goals

  • Portability
  • Speed
  • Scalability
  • Convenience

2 Components

  • evutil
  • event & event_base
  • bufferevent
  • evbuffer
  • evhttp
  • evdns
  • evrpc

3 Libraries

  • libevent_core
  • libevent_extra
  • libevent [outdated]
  • libevent_pthreads
  • libevent_openssl

4 Headers

  • API headers
  • Compatibility headers
  • Structure headers

5 Setup

Log

1
2
3
4
5
6
7
8
9
10
void event_set_log_callback(event_log_cb cb);
// typedef void (*event_log_cb)(int severity, const char *msg);
// #define EVENT_LOG_DEBUG 0
// #define EVENT_LOG_MSG 1
// #define EVENT_LOG_WARN 2
// #define EVENT_LOG_ERR 3

void event_enable_debug_logging(ev_uint32_t which);
// #define EVENT_DBG_NONE 0
// #define EVENT_DBG_ALL 0xffffffffu

Handle Error

1
2
void event_set_fatal_callback(event_fatal_cb cb);
// typedef void (*event_fatal_cb)(int err);

Manage Memory

1
2
3
4
void event_set_mem_functions(
void *(*malloc_fn)(size_t sz),
void *(*realloc_fn)(void *ptr, size_t sz),
void (*free_fn)(void *ptr));

Locks and threading

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
// #ifdef WIN32
int evthread_use_windows_threads(void);
// #define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED
// #endif

// #ifdef _EVENT_HAVE_PTHREAD
int evthread_use_pthreads(void);
// #define EVTHREAD_USE_PTHREADS_IMPLEMENTED
// #endif

int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *);
// struct evthread_lock_callbacks {
// int lock_api_version;
// unsigned supported_locktypes;
// void *(*alloc)(unsigned locktype);
// void (*free)(void *lock, unsigned locktype);
// int (*lock)(unsigned mode, void *lock);
// int (*unlock)(unsigned mode, void *lock);
// };

// #define EVTHREAD_WRITE 0x04
// #define EVTHREAD_READ 0x08
// #define EVTHREAD_TRY 0x10
// #define EVTHREAD_LOCKTYPE_RECURSIVE 1
// #define EVTHREAD_LOCKTYPE_READWRITE 2
// #define EVTHREAD_LOCK_API_VERSION 1

void evthread_set_id_callback(unsigned long (*id_fn)(void));
int evthread_set_condition_callbacks(const struct evthread_condition_callbacks *);
// struct evthread_condition_callbacks {
// int condition_api_version;
// void *(*alloc_condition)(unsigned condtype);
// void (*free_condition)(void *cond);
// int (*signal_condition)(void *cond, int broadcast);
// int (*wait_condition)(void *cond, void *lock, const struct timeval *timeout);
// };

Debug

1
2
3
void evthread_enable_lock_debugging(void);
void event_enable_debug_mode(void);
void event_debug_unassign(struct event *ev);

Detect Version

1
2
3
4
#define LIBEVENT_VERSION_NUMBER 0x02000300 
#define LIBEVENT_VERSION "2.0.3-alpha"
const char *event_get_version(void);
ev_uint32_t event_get_version_number(void);

Free global structures

1
void libevent_global_shutdown(void);

6 event_base

Recognized backend

  • select
  • poll
  • epoll
  • kqueue
  • devpoll
  • evport
  • win32

Turn off

  • Set environment variables. e.g. EVENT_NOKQUEUE.
  • Call event_config_avoid_method() below.

New & free & reinit

1
2
3
4
struct event_base *event_base_new(void);
void event_base_free(struct event_base *base); // 不会 deallocate any of the events that are currently associated with the event_base, or close any of their sockets, or free any of their pointers.
int event_reinit(struct event_base *base); // call after fork() in child proc
struct event_base *event_init(void); // [obsolete]

New with event_config

To avoid specific available backend by name, or by feature.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct event_config *event_config_new(void);
struct event_base event_base_new_with_config(const struct event_config *cfg);
void event_config_free(struct event_config *cfg);
int event_config_avoid_method(struct event_config *cfg,const char *method);
int event_config_require_features(struct event_config *cfg, enum event_method_feature feature);
// enum event_method_feature {
// EV_FEATURE_ET = 0x01, (支持边缘触发。)
// EV_FEATURE_O1 = 0x02, (增/删一个 event, 或激活一个 event 都能在O(1)完成。) (Windows 上没有。)
// EV_FEATURE_FDS = 0x04, (支持通用的文件描述符,不止 socket。) (Linux 上不能和 O(1) 同时满足。)
// };
int event_config_set_flag(struct event_config *cfg, enum event_base_config_flag flag);
// enum event_base_config_flag {
// EVENT_BASE_FLAG_NOLOCK = 0x01, (不在 event_base 上使用锁,换取性能。)
// EVENT_BASE_FLAG_IGNORE_ENV = 0x02, (不检查 EVENT_* 环境变量,不推荐)
// EVENT_BASE_FLAG_STARTUP_IOCP = 0x04, (On Windows only, this flag makes Libevent enable any necessary IOCP dispatch logic on startup, rather than on-demand.)
// EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08, (每个 timeout callback 后检查时间,更耗性能。)
// EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10, (同环境变量EVENT_EPOLL_USE_CHANGELIST,允许使用更快速的 "changelist"-based backend,但这种方式可能会触发内核 bug)
// EVENT_BASE_FLAG_PRECISE_TIMER = 0x20 (使用 slower-but-more-precise 的计时,前提是系统支持)
// };
int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus) // 目前只对使用了 IOCP 的 Windows 有用,指定 multithreading 中最大可利用的 CPU 核数。
int event_config_set_max_dispatch_interval(struct event_config *cfg,
const struct timeval *max_interval, int max_callbacks, int min_priority);

backend

1
2
3
4
const char **event_get_supported_methods(void);
const char *event_base_get_method(const struct event_base *base);
enum event_method_feature event_base_get_features(const struct event_base *base); // 与 EV_FEATURE_ET、EV_FEATURE_O1、EV_FEATURE_FDS 进行 & 运算。
event_get_method() // [obsolete]

priorities

1
2
3
int event_base_priority_init(struct event_base *base, int n_priorities);    // 优先级数量:1 ~ EVENT_MAX_PRIORITIES,但优先级本身是从 0 开始的。这个函数必须在任何事件前调用!事件默认值为 n_priorities / 2
int event_base_get_npriorities(struct event_base *base); // 返回设定值。
event_priority_init() // [obsolete]

7 Event loop

Run

default: run until no more events registered in it.

1
2
3
4
5
int event_base_loop(struct event_base *base, int flags);
int event_base_dispatch(struct event_base *base);
// #define EVLOOP_ONCE 0x01
// #define EVLOOP_NONBLOCK 0x02
// #define EVLOOP_NO_EXIT_ON_EMPTY 0x04

stop

1
2
3
4
5
6
7
int event_base_loopexit(struct event_base *base, const struct timeval *tv);     // exit after all active events cb done.
int event_base_loopbreak(struct event_base *base); // exit after current one cb done.

int event_base_got_exit(struct event_base *base);
int event_base_got_break(struct event_base *base);

int event_base_loopcontinue(struct event_base *);

internal time cache

1
2
int event_base_gettimeofday_cached(struct event_base *base, struct timeval *tv_out);
int event_base_update_cache_time(struct event_base *base);

dump status

1
void event_base_dump_events(struct event_base *base, FILE *f);

iterate events

1
2
int event_base_foreach_event(struct event_base *base, event_base_foreach_event_cb fn, void *arg);
// typedef int (*event_base_foreach_event_cb)(const struct event_base *, const struct event *, void *)

obsolete methods

  • event_dispatch()
  • event_loop()
  • event_loopexit()
  • event_loopbreak()

8 Events

conditions:

  • fd being ready to read from or write to.
  • fd becoming ready to read from or write to (Edge-triggered IO only).
  • a timeout expiring.
  • a signal occuring.
  • a user-triggered event.

status:

  • initialized -> pending (by add)
  • pending -> active (condition trigger an event occur, cb run)
  • active -> pending (persistent) -> delete -> non-pending -> add -> pending
  • active -> non-pending (non-persistent)

construct

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

You can’t create an event that receives itself as a cb argument. Instead:

1
void *event_self_cbarg();

Timeout-only events

no benefit beyond clarifying your code.

1
2
3
4
5
6
7
8
#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))

Signal events

1
2
3
4
5
6
7
8
#define evsignal_new(base, signum, cb, arg) \     
event_new(base, signum, EV_SIGNAL|EV_PERSIST, cb, arg)
#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))
  • Don’t set a timeout on a signal event.
  • It’s safe to call functions that you aren’t supposed to call from a regulat POSIX signal handler.
  • 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_base at once - even if the signals are different - only one event_base will receive signals. (kqueue doesn’t have this limitation.)

Heap-allocation events

1
2
3
4
5
6
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);

#define evtimer_assign(event, base, callback, arg) \
event_assign(event, base, -1, 0, callback, arg)
#define evsignal_assign(event, base, signum, callback, arg) \
event_assign(event, base, signum, EV_SIGNAL|EV_PERSIST, callback, arg)
  • Your code won’t be binary-compatible with future versions of libevent as the size of a event may differ. To check, make use of this function:
1
size_t event_get_struct_event_size(void);
  • May return a value less than sizeof(event) because of padding bytes at the end.
  • Never call event_assign on an event that is already pending. call event_del() first.

Pending and Non-pending

1
2
3
4
5
int event_add(struct event *ev, const struct timeval *tv);
int event_del(struct event *ev);
int event_remove_timer(struct event *ev);

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

Inspecting Event Status

1
2
3
4
5
6
7
8
9
10
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);

struct event *event_base_get_running_event(struct event_base *base);

One-off events

1
int event_base_once(struct event_base *, evutil_socket_t, short, void (*)(evutil_socket_t, short, void *), void *, const struct timeval *);
  • 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 persistent, use event_base_once.
  • It doesn’t support EV_SIGNAL or EV_PERSIST.
  • Can’t be deleted or manually activated.

Manually activating an event

1
void event_active(struct event *ev, int what, short ncalls);
  • Don’t calling event_active recursively on the same event.

Optimizing common timeouts

  • Libevent use a binary algorithm to keep track of pending events’ timeouts, which gives performance of O(lg n) for adding and deleting each event timeout.
  • If you have a large number of events with the same timeout, a doubly-linked queue will be better.
1
const struct timeval *event_base_init_common_timeout( struct event_base *base, const struct timeval *duration);

Distinguish an initialized event and a cleared event

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

9 Helper

Basic Types

  • evutil_socket_t

  • ev_uint64_t EV_UINT64_MAX 0

  • ev_int64_t EV_INT64_MAX EV_INT64_MIN
  • ev_uint32_t EV_UINT32_MAX 0
  • ev_int32_t EV_INT32_MAX EV_INT32_MIN
  • ev_uint16_t EV_UINT16_MAX 0
  • ev_int16_t EV_INT16_MAX EV_INT16_MIN
  • ev_uint8_t EV_UINT8_MAX 0
  • ev_int8_t EV_INT8_MAX EV_INT8_MIN

  • ev_ssize_t EV_SSIZE_MIN EV_SSIZE_MAX

  • ev_off_t
  • ev_socket_t
  • ev_intptr_t
  • ev_uintptr_t

Timer

1
2
3
4
5
6
#define evutil_timeradd(tvp, uvp, vvp) /* ... */ 
#define evutil_timersub(tvp, uvp, vvp) /* ... */
#define evutil_timerclear(tvp) /* ... */
#define evutil_timerisset(tvp) /* ... */
#define evutil_timercmp(tvp, uvp, cmp)
int evutil_gettimeofday(struct timeval *tv, struct timezone *tz);

Socket

1
2
3
4
5
6
7
8
9
10
int evutil_closesocket(evutil_socket_t s); 
#define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s)
#define EVUTIL_SOCKET_ERROR()
#define EVUTIL_SET_SOCKET_ERROR(errcode)
#define evutil_socket_geterror(sock)
#define evutil_socket_error_to_string(errcode)
int evutil_make_socket_nonblocking(evutil_socket_t sock);
int evutil_make_listen_socket_reuseable(evutil_socket_t sock);
int evutil_make_socket_closeonexec(evutil_socket_t sock);
int evutil_socketpair(int family, int type, int protocol, evutil_socket_t sv[2]);

String

1
2
3
4
5
ev_int64_t evutil_strtoll(const char *s, char **endptr, int base);
int evutil_snprintf(char *buf, size_t buflen, const char *format, ...);
int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap);
int evutil_ascii_strcasecmp(const char *str1, const char *str2);
int evutil_ascii_strncasecmp(const char *str1, const char *str2, size_t n);

IPv6

1
2
3
4
const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len); 
int evutil_inet_pton(int af, const char *src, void *dst);
int evutil_parse_sockaddr_port(const char *str, struct sockaddr *out, int *outlen);
int evutil_sockaddr_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2, int include_port);

Structure

1
2
3
4
#define evutil_offsetof(type, field) /* ... */
void evutil_secure_rng_get_bytes(void *buf, size_t n);
int evutil_secure_rng_init(void);
void evutil_secure_rng_add_bytes(const char *dat, size_t datlen);

10 Bufferevents

Type

  • socket-based bufferevents
  • asynchronous-IO bufferevents (Windows IOCP only, experimental)
  • filtering bufferevents (for example, to compress or translate data)
  • paired bufferevents (two bufferevents transmit data to one another)

BufferEvents and evbuffers

  • <event2/bufferevent.h>
  • <event2/buffer.h>
  • Every bufferevent has an input buffer and an output buffer. These are of type “struct evbuffer”.
  • Every bufferevent has two data-related callbacks: a read callback and a write callback.

Callbacks

  • The read callback is called whenever any data is read from the underlying transport.
  • The write callback is called whenever enough data from the output buffer is emptied to the underlying transport.

Watermarks

  • Read low-water mark (defaults to 0)
  • Read high-water mark (defaults to unlimited)
  • Write low-water mark (defaults to 0)
  • Write high-water mark (have special meaning in filtering bufferevents)

Callbacks

When a connection is closed or an error occurs. Flags:

  • BEV_EVENT_READING
  • BEV_EVENT_WRITING
  • BEV_EVENT_ERROR
  • BEV_EVENT_TIMEOUT
  • BEV_EVENT_EOF
  • BEV_EVENT_CONNECTED

Deferred callbacks

By default, bufferevent and evbuffer ‘s callbacks are executed immediately. But it may make trouble.

For example, one callback moves data into evbuffer A when it grows empty, and another one put data out of A when it grows full. You may risk a stack overflow if the dependency grows nasty enough.

To solve this, you can use deferred callbacks. It is queued as part of the event_loop(), and invoked after the regular events’ callbacks.

Option Flags

  • BEV_OPT_CLOSE_ON_FREE: When the bufferevent is freed, close the underlying transport, such as socket.
  • BEV_OPT_THREADSAFE
  • BEV_OPT_DEFER_CALLBACKS
  • BEV_OPT_UNLOCK_CALLBACKS: When threadsafe is set and invoking the user-provided callback, release the lock.

11 Socket-based Bufferevents

Create

1
struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, enum bufferevent_options options);
  • fd 需要是 non-blocking 的,可以使用evutil_make_socket_nonblocking()
  • 不想现在指定 fd 可以将其设为 -1。

Launch

1
2
3
int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address, int addrlen);
int bufferevent_socket_connect_hostname(struct bufferevent *bev, struct evdns_base *dns_base, int family, const char *hostname, int port);
int bufferevent_socket_get_dns_error(struct bufferevent *bev);
  • If no socket set, it will allocates a new nonblocking one.

Generic operations

1
2
3
4
5
6
7
8
9
10
void bufferevent_free(struct bufferevent *bev);

void bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb eventcb, void *cbarg);
void bufferevent_getcb(struct bufferevent *bufev, bufferevent_data_cb *readcb_ptr, bufferevent_data_cb *writecb_ptr, bufferevent_event_cb *eventcb_ptr, void **cbarg_ptr);
// typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
// typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);

void bufferevent_enable(struct bufferevent *bufev, short events); void bufferevent_disable(struct bufferevent *bufev, short events); // EV_READ, EV_WRITE, or EV_READ|EV_WRITE
short bufferevent_get_enabled(struct bufferevent *bufev); // EV_READ, EV_WRITE, or EV_READ|EV_WRITE
void bufferevent_setwatermark(struct bufferevent *bufev, short events, size_t lowmark, size_t highmark);
  • bufferevent_free will free the bufferevent as soon as possible, probably won’t flush data on write buffer.
  • BEV_OPT_CLOSE_ON_FREE will close its transport underlying it - such as socket - when free.
  • By default, a newly created bufferevent has writing enabled, but not reading.

Manipulate Data

1
2
3
4
5
6
7
8
9
10
11
12
struct evbuffer *bufferevent_get_input(struct bufferevent *bufev); 
struct evbuffer *bufferevent_get_output(struct bufferevent *bufev);

int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
int bufferevent_read_buffer(struct bufferevent *bufev, struct evbuffer *buf);

void bufferevent_set_timeouts(struct bufferevent *bufev, const struct timeval *timeout_read, const struct timeval *timeout_write);
int bufferevent_flush(struct bufferevent *bufev, short iotype, enum bufferevent_flush_mode state);
// iotype: EV_READ, EV_WRITE, or EV_READ|EV_WRITE
// state: BEV_NORMAL, BEV_FLUSH, or BEV_FINISHED
  • It won’t be timeout if r/w is disabled or no data to r/w.
  • If timeout, invoke cb with BEV_EVENT_TIMEOUT|BEV_EVENT_READING or BEV_EVENT_TIMEOUT|BEV_EVENT_WRITING.

Type-specific functions

1
2
3
4
5
6
7
8
9
// socket-based only
int bufferevent_priority_set(struct bufferevent *bufev, int pri);
int bufferevent_get_priority(struct bufferevent *bufev);

int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd);
evutil_socket_t bufferevent_getfd(struct bufferevent *bufev);

struct event_base *bufferevent_get_base(struct bufferevent *bev);
struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev);

Locking and Unlocking

1
2
void bufferevent_lock(struct bufferevent *bufev); 
void bufferevent_unlock(struct bufferevent *bufev);
  • Required threading support activated and BEV_OPT_THREADSAFE on creation.

12 Bufferevents: Advanced topics

Paired bufferevents

All bytes written on one are received on the other, without via the network stack.

1
2
int bufferevent_pair_new(struct event_base *base, int options, struct bufferevent *pair[2]);
struct bufferevent *bufferevent_pair_get_partner(struct bufferevent *bev)
  • BEV_OPT_CLOSE_ON_FREE has no effect.
  • BEV_OPT_DEFER_CALLBACKS is always on.

Filtering bufferevents

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct bufferevent *bufferevent_filter_new(struct bufferevent *underlying, bufferevent_filter_cb input_filter, bufferevent_filter_cb output_filter, int options, void (*free_context)(void *), void *ctx);

// enum bufferevent_filter_result {
// BEV_OK = 0,
// BEV_NEED_MORE = 1,
// BEV_ERROR = 2
// };
// typedef enum bufferevent_filter_result (*bufferevent_filter_cb)(
// struct evbuffer *source,
// struct evbuffer *destination,
// ev_ssize_t dst_limit,
// enum bufferevent_flush_mode mode,
// void *ctx
// );

Limit max single r/w size

1
2
3
4
int bufferevent_set_max_single_read(struct bufferevent *bev, size_t size); 
int bufferevent_set_max_single_write(struct bufferevent *bev, size_t size);
ev_ssize_t bufferevent_get_max_single_read(struct bufferevent *bev);
ev_ssize_t bufferevent_get_max_single_write(struct bufferevent *bev);

Rate-limiting

algorithm: token bucket.

  • read bucket & write bucket.
  • refill rate: determines the max average rate.
  • max burst size: determines the max number of bytes that will be r/w in a single burst.
  • timing unit: smoothness of the traffic.
1
2
3
4
5
6
7
8
9
10
11
#define EV_RATE_LIMIT_MAX EV_SSIZE_MAX 
struct ev_token_bucket_cfg;
struct ev_token_bucket_cfg *ev_token_bucket_cfg_new(
size_t read_rate,
size_t read_burst,
size_t write_rate,
size_t write_burst, c
onst struct timeval *tick_len
);
void ev_token_bucket_cfg_free(struct ev_token_bucket_cfg *cfg);
int bufferevent_set_rate_limit(struct bufferevent *bev, struct ev_token_bucket_cfg *cfg);

Rate-limiting on group

1
2
3
4
5
6
struct bufferevent_rate_limit_group; 
struct bufferevent_rate_limit_group *bufferevent_rate_limit_group_new(struct event_base *base, const struct ev_token_bucket_cfg *cfg);
int bufferevent_rate_limit_group_set_cfg(struct bufferevent_rate_limit_group *group, const struct ev_token_bucket_cfg *cfg);
void bufferevent_rate_limit_group_free(struct bufferevent_rate_limit_group *);
int bufferevent_add_to_rate_limit_group(struct bufferevent *bev, struct bufferevent_rate_limit_group *g);
int bufferevent_remove_from_rate_limit_group(struct bufferevent *bev);

Inspecting rate-limiting

1
2
3
4
5
6
7
8
9
10
ev_ssize_t bufferevent_get_read_limit(struct bufferevent *bev); 
ev_ssize_t bufferevent_get_write_limit(struct bufferevent *bev);
ev_ssize_t bufferevent_rate_limit_group_get_read_limit( struct bufferevent_rate_limit_group *);
ev_ssize_t bufferevent_rate_limit_group_get_write_limit( struct bufferevent_rate_limit_group *);

ev_ssize_t bufferevent_get_max_to_read(struct bufferevent *bev);
ev_ssize_t bufferevent_get_max_to_write(struct bufferevent *bev);

void bufferevent_rate_limit_group_get_totals( struct bufferevent_rate_limit_group *grp, ev_uint64_t *total_read_out, ev_uint64_t *total_written_out);
void bufferevent_rate_limit_group_reset_totals( struct bufferevent_rate_limit_group *grp);

Manually adjusting rate limits

1
2
3
4
int bufferevent_decrement_read_limit(struct bufferevent *bev, ev_ssize_t decr); 
int bufferevent_decrement_write_limit(struct bufferevent *bev, ev_ssize_t decr);
int bufferevent_rate_limit_group_decrement_read( struct bufferevent_rate_limit_group *grp, ev_ssize_t decr);
int bufferevent_rate_limit_group_decrement_write( struct bufferevent_rate_limit_group *grp, ev_ssize_t decr);

Setting the smallest share possible in a rate-limited group

1
int bufferevent_rate_limit_group_set_min_share( struct bufferevent_rate_limit_group *group, size_t min_share);

Bufferevents and SSL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum bufferevent_ssl_state {         
BUFFEREVENT_SSL_OPEN = 0,
BUFFEREVENT_SSL_CONNECTING = 1,
BUFFEREVENT_SSL_ACCEPTING = 2
};
struct bufferevent * bufferevent_openssl_filter_new(struct event_base *base, struct bufferevent *underlying, SSL *ssl, enum bufferevent_ssl_state state, int options);
struct bufferevent * bufferevent_openssl_socket_new(struct event_base *base, evutil_socket_t fd, SSL *ssl, enum bufferevent_ssl_state state, int options);

SSL *bufferevent_openssl_get_ssl(struct bufferevent *bev);
unsigned long bufferevent_get_openssl_error(struct bufferevent *bev);
int bufferevent_ssl_renegotiate(struct bufferevent *bev);

int bufferevent_openssl_get_allow_dirty_shutdown(struct bufferevent *bev);
void bufferevent_openssl_set_allow_dirty_shutdown(struct bufferevent *bev, int allow_dirty_shutdown);

13 Evbuffers

Create & free

1
2
struct evbuffer *evbuffer_new(void); 
void evbuffer_free(struct evbuffer *buf);

Thread-Safety

1
2
3
int evbuffer_enable_locking(struct evbuffer *buf, void *lock);  // NULL is legal, it will allocate a new lock.
void evbuffer_lock(struct evbuffer *buf);
void evbuffer_unlock(struct evbuffer *buf);

Inspecting an evbuffer

1
2
size_t evbuffer_get_length(const struct evbuffer *buf);
size_t evbuffer_get_contiguous_space(const struct evbuffer *buf);

Adding Data

1
2
3
4
int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen);
int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...);
int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap);
int evbuffer_expand(struct evbuffer *buf, size_t datlen);

Move Data

1
2
int evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src); 
int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst, size_t datlen);

Adding Data to the front

1
2
int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size); 
int evbuffer_prepend_buffer(struct evbuffer *dst, struct evbuffer* src);

Rearranging the internal layout

1
unsigned char *evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size);

Remove Data

1
2
int evbuffer_drain(struct evbuffer *buf, size_t len); 
int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen);

Copying Data

1
2
ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data, size_t datlen); 
ev_ssize_t evbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos, void *data_out, size_t datlen);

Line-oriented Input

1
2
3
4
5
6
7
8
char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, enum evbuffer_eol_style eol_style);
// enum evbuffer_eol_style {
// EVBUFFER_EOL_ANY,
// EVBUFFER_EOL_CRLF,
// EVBUFFER_EOL_CRLF_STRICT,
// EVBUFFER_EOL_LF,
// EVBUFFER_EOL_NUL
// };

Searching

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct evbuffer_ptr evbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start); 
struct evbuffer_ptr evbuffer_search_range(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start, const struct evbuffer_ptr *end);
struct evbuffer_ptr evbuffer_search_eol(struct evbuffer *buffer, struct evbuffer_ptr *start, size_t *eol_len_out, enum evbuffer_eol_style eol_style);
// struct evbuffer_ptr {
// ev_ssize_t pos;
// struct {
// /* internal fields */
// } _internal;
// };

int evbuffer_ptr_set(struct evbuffer *buffer, struct evbuffer_ptr *pos, size_t position, enum evbuffer_ptr_how how);
// enum evbuffer_ptr_how {
// EVBUFFER_PTR_SET,
// EVBUFFER_PTR_ADD
// };

Inspecting data without copying it

1
2
3
4
5
int evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len, struct evbuffer_ptr *start_at, struct evbuffer_iovec *vec_out, int n_vec);
// struct evbuffer_iovec {
// void *iov_base;
// size_t iov_len;
// };

Adding data to an evbuffer directly

1
2
int evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size, struct evbuffer_iovec *vec, int n_vecs); 
int evbuffer_commit_space(struct evbuffer *buf, struct evbuffer_iovec *vec, int n_vecs);

Network IO with evbuffers

1
2
3
int evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd); 
int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd, ev_ssize_t howmuch);
int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch);

Evbuffers and callbacks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct evbuffer_cb_entry; 
struct evbuffer_cb_entry *evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg);
// typedef void (*evbuffer_cb_func)(struct evbuffer *buffer, const struct evbuffer_cb_info *info, void *arg);
// struct evbuffer_cb_info {
// size_t orig_size;
// size_t n_added;
// size_t n_deleted;
// };

int evbuffer_remove_cb_entry(struct evbuffer *buffer, struct evbuffer_cb_entry *ent);
int evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg);
#define EVBUFFER_CB_ENABLED 1
int evbuffer_cb_set_flags(struct evbuffer *buffer, struct evbuffer_cb_entry *cb, ev_uint32_t flags);
int evbuffer_cb_clear_flags(struct evbuffer *buffer, struct evbuffer_cb_entry *cb, ev_uint32_t flags);

int evbuffer_defer_callbacks(struct evbuffer *buffer, struct event_base *base);

Avoiding data copies with evbuffer-based IO

1
2
int evbuffer_add_reference(struct evbuffer *outbuf, const void *data, size_t datlen, evbuffer_ref_cleanup_cb cleanupfn, void *extra);
// typedef void (*evbuffer_ref_cleanup_cb)(const void *data, size_t datalen, void *extra);

Adding a file to an evbuffer

1
int evbuffer_add_file(struct evbuffer *output, int fd, ev_off_t offset, size_t length);

Fine-grained control with file segments

1
2
3
4
5
6
7
struct evbuffer_file_segment; 
struct evbuffer_file_segment *evbuffer_file_segment_new( int fd, ev_off_t offset, ev_off_t length, unsigned flags);
void evbuffer_file_segment_free(struct evbuffer_file_segment *seg);
int evbuffer_add_file_segment(struct evbuffer *buf, struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length);

typedef void (*evbuffer_file_segment_cleanup_cb)( struct evbuffer_file_segment const *seg, int flags, void *arg);
void evbuffer_file_segment_add_cleanup_cb(struct evbuffer_file_segment *seg, evbuffer_file_segment_cleanup_cb cb, void *arg);

Adding an evbuffer to another by reference

1
int evbuffer_add_buffer_reference(struct evbuffer *outbuf, struct evbuffer *inbuf);

Making an evbuffer add- or remove-only

1
2
int evbuffer_freeze(struct evbuffer *buf, int at_front); 
int evbuffer_unfreeze(struct evbuffer *buf, int at_front);

14 Connection listeners: evconnlistener

1
2
3
4
5
6
7
8
9
10
11
12
13
struct evconnlistener *evconnlistener_new(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, evutil_socket_t fd); 
struct evconnlistener *evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, const struct sockaddr *sa, int socklen);
void evconnlistener_free(struct evconnlistener *lev);
// typedef void (*evconnlistener_cb)(struct evconnlistener *listener, evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr);

int evconnlistener_disable(struct evconnlistener *lev);
int evconnlistener_enable(struct evconnlistener *lev);
void evconnlistener_set_cb(struct evconnlistener *lev, evconnlistener_cb cb, void *arg);
evutil_socket_t evconnlistener_get_fd(struct evconnlistener *lev);
struct event_base *evconnlistener_get_base(struct evconnlistener *lev);

void evconnlistener_set_error_cb(struct evconnlistener *lev, evconnlistener_errorcb errorcb);
// typedef void (*evconnlistener_errorcb)(struct evconnlistener *lis, void *ptr);

flag:

  • LEV_OPT_LEAVE_SOCKETS_BLOCKING
  • LEV_OPT_CLOSE_ON_FREE
  • LEV_OPT_CLOSE_ON_EXEC
  • LEV_OPT_REUSEABLE
  • LEV_OPT_THREADSAFE
  • LEV_OPT_DISABLED
  • LEV_OPT_DEFERRED_ACCEPT

15 Use DNS