--- categories: - docs - operate - stack - oss description: What's an event library, and how was the original Redis event library implemented? linkTitle: Event library title: Event library weight: 1 --- **Note: this document was written by the creator of Redis, Salvatore Sanfilippo, early in the development of Redis (c. 2010), and does not necessarily reflect the latest Redis implementation.** ## Why is an Event Library needed at all? Let us figure it out through a series of Q&As. Q: What do you expect a network server to be doing all the time?
A: Watch for inbound connections on the port its listening and accept them. Q: Calling [accept](http://man.cx/accept%282%29) yields a descriptor. What do I do with it?
A: Save the descriptor and do a non-blocking read/write operation on it. Q: Why does the read/write have to be non-blocking?
A: If the file operation ( even a socket in Unix is a file ) is blocking how could the server for example accept other connection requests when its blocked in a file I/O operation. Q: I guess I have to do many such non-blocking operations on the socket to see when it's ready. Am I right?
A: Yes. That is what an event library does for you. Now you get it. Q: How do Event Libraries do what they do?
A: They use the operating system's polling facility along with timers. Q: So are there any open source event libraries that do what you just described?
A: Yes. `libevent` and `libev` are two such event libraries that I can recall off the top of my head. Q: Does Redis use such open source event libraries for handling socket I/O?
A: No. For various [reasons](http://groups.google.com/group/redis-db/browse_thread/thread/b52814e9ef15b8d0/) Redis uses its own event library. ## The Redis event library Redis implements its own event library. The event library is implemented in `ae.c`. The best way to understand how the Redis event library works is to understand how Redis uses it. Event Loop Initialization --- `initServer` function defined in `redis.c` initializes the numerous fields of the `redisServer` structure variable. One such field is the Redis event loop `el`: aeEventLoop *el `initServer` initializes `server.el` field by calling `aeCreateEventLoop` defined in `ae.c`. The definition of `aeEventLoop` is below: typedef struct aeEventLoop { int maxfd; long long timeEventNextId; aeFileEvent events[AE_SETSIZE]; /* Registered events */ aeFiredEvent fired[AE_SETSIZE]; /* Fired events */ aeTimeEvent *timeEventHead; int stop; void *apidata; /* This is used for polling API specific data */ aeBeforeSleepProc *beforesleep; } aeEventLoop; `aeCreateEventLoop` --- `aeCreateEventLoop` first `malloc`s `aeEventLoop` structure then calls `ae_epoll.c:aeApiCreate`. `aeApiCreate` `malloc`s `aeApiState` that has two fields - `epfd` that holds the `epoll` file descriptor returned by a call from [`epoll_create`](http://man.cx/epoll_create%282%29) and `events` that is of type `struct epoll_event` define by the Linux `epoll` library. The use of the `events` field will be described later. Next is `ae.c:aeCreateTimeEvent`. But before that `initServer` call `anet.c:anetTcpServer` that creates and returns a _listening descriptor_. The descriptor listens on *port 6379* by default. The returned _listening descriptor_ is stored in `server.fd` field. `aeCreateTimeEvent` --- `aeCreateTimeEvent` accepts the following as parameters: * `eventLoop`: This is `server.el` in `redis.c` * milliseconds: The number of milliseconds from the current time after which the timer expires. * `proc`: Function pointer. Stores the address of the function that has to be called after the timer expires. * `clientData`: Mostly `NULL`. * `finalizerProc`: Pointer to the function that has to be called before the timed event is removed from the list of timed events. `initServer` calls `aeCreateTimeEvent` to add a timed event to `timeEventHead` field of `server.el`. `timeEventHead` is a pointer to a list of such timed events. The call to `aeCreateTimeEvent` from `redis.c:initServer` function is given below: aeCreateTimeEvent(server.el /*eventLoop*/, 1 /*milliseconds*/, serverCron /*proc*/, NULL /*clientData*/, NULL /*finalizerProc*/); `redis.c:serverCron` performs many operations that helps keep Redis running properly. `aeCreateFileEvent` --- The essence of `aeCreateFileEvent` function is to execute [`epoll_ctl`](http://man.cx/epoll_ctl) system call which adds a watch for `EPOLLIN` event on the _listening descriptor_ create by `anetTcpServer` and associate it with the `epoll` descriptor created by a call to `aeCreateEventLoop`. Following is an explanation of what precisely `aeCreateFileEvent` does when called from `redis.c:initServer`. `initServer` passes the following arguments to `aeCreateFileEvent`: * `server.el`: The event loop created by `aeCreateEventLoop`. The `epoll` descriptor is got from `server.el`. * `server.fd`: The _listening descriptor_ that also serves as an index to access the relevant file event structure from the `eventLoop->events` table and store extra information like the callback function. * `AE_READABLE`: Signifies that `server.fd` has to be watched for `EPOLLIN` event. * `acceptHandler`: The function that has to be executed when the event being watched for is ready. This function pointer is stored in `eventLoop->events[server.fd]->rfileProc`. This completes the initialization of Redis event loop. Event Loop Processing --- `ae.c:aeMain` called from `redis.c:main` does the job of processing the event loop that is initialized in the previous phase. `ae.c:aeMain` calls `ae.c:aeProcessEvents` in a while loop that processes pending time and file events. `aeProcessEvents` --- `ae.c:aeProcessEvents` looks for the time event that will be pending in the smallest amount of time by calling `ae.c:aeSearchNearestTimer` on the event loop. In our case there is only one timer event in the event loop that was created by `ae.c:aeCreateTimeEvent`. Remember, that the timer event created by `aeCreateTimeEvent` has probably elapsed by now because it had an expiry time of one millisecond. Since the timer has already expired, the seconds and microseconds fields of the `tvp` `timeval` structure variable is initialized to zero. The `tvp` structure variable along with the event loop variable is passed to `ae_epoll.c:aeApiPoll`. `aeApiPoll` functions does an [`epoll_wait`](http://man.cx/epoll_wait) on the `epoll` descriptor and populates the `eventLoop->fired` table with the details: * `fd`: The descriptor that is now ready to do a read/write operation depending on the mask value. * `mask`: The read/write event that can now be performed on the corresponding descriptor. `aeApiPoll` returns the number of such file events ready for operation. Now to put things in context, if any client has requested for a connection then `aeApiPoll` would have noticed it and populated the `eventLoop->fired` table with an entry of the descriptor being the _listening descriptor_ and mask being `AE_READABLE`. Now, `aeProcessEvents` calls the `redis.c:acceptHandler` registered as the callback. `acceptHandler` executes [accept](http://man.cx/accept) on the _listening descriptor_ returning a _connected descriptor_ with the client. `redis.c:createClient` adds a file event on the _connected descriptor_ through a call to `ae.c:aeCreateFileEvent` like below: if (aeCreateFileEvent(server.el, c->fd, AE_READABLE, readQueryFromClient, c) == AE_ERR) { freeClient(c); return NULL; } `c` is the `redisClient` structure variable and `c->fd` is the connected descriptor. Next the `ae.c:aeProcessEvent` calls `ae.c:processTimeEvents` `processTimeEvents` --- `ae.processTimeEvents` iterates over list of time events starting at `eventLoop->timeEventHead`. For every timed event that has elapsed `processTimeEvents` calls the registered callback. In this case it calls the only timed event callback registered, that is, `redis.c:serverCron`. The callback returns the time in milliseconds after which the callback must be called again. This change is recorded via a call to `ae.c:aeAddMilliSeconds` and will be handled on the next iteration of `ae.c:aeMain` while loop. That's all.