#sidebar
RedisInternalsRedis 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.
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 first mallocs aeEventLoop structure then calls ae_epoll.c:aeApiCreate
.
aeApiCreate
mallocs aeApiState
that has two fields - epfd
that holds the epoll file descriptor returned by a call from [http://man.cx/epoll_create%282%29 epoll_create] 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 is listens to
port 6379 by default. The returned
listening descriptor is stored in
server.fd field.
aeCreateTimeEvent accepts the following as parameters:
- eventLoop: This is
server.el in redis.c - milliseconds: The number of milliseconds from the curent 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.
The essence of
aeCreateFileEvent function is to execute
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.
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.
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 timer event created by
aeCreateTimeEvent has by now probably elapsed because it had a 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 a
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. The
- 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 [
http://man.cx/accept(2) 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:processTimeEventsae.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.