`|Redis Documentation| `_
**RedisEventLibrary: Contents**
`Redis Event Library <#Redis%20Event%20Library>`_
`Event Loop Initialization <#Event%20Loop%20Initialization>`_
`aeCreateEventLoop <#aeCreateEventLoop>`_
`aeCreateTimeEvent <#aeCreateTimeEvent>`_
`aeCreateFileEvent <#aeCreateFileEvent>`_
`Event Loop Processing <#Event%20Loop%20Processing>`_
`aeProcessEvents <#aeProcessEvents>`_
`processTimeEvents <#processTimeEvents>`_
RedisEventLibrary
=================
#sidebar `RedisInternals `_
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 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
~~~~~~~~~~~~~~~~~
``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.
aeCreateFileEvent
~~~~~~~~~~~~~~~~~
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.
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 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: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.
.. |Redis Documentation| image:: redis.png