`|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