{
  "id": "internals-rediseventlib",
  "title": "Event library",
  "url": "https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/internals-rediseventlib/",
  "summary": "What's an event library, and how was the original Redis event library implemented?",
  "tags": [
    "docs",
    "operate",
    "stack",
    "oss"
  ],
  "last_updated": "2026-04-01T08:10:08-05:00",
  "page_type": "content",
  "content_hash": "9712a60b53542f743339b5b643817b4bab274b39d959c68d406d5745a56cc77b",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "**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.**"
    },
    {
      "id": "why-is-an-event-library-needed-at-all",
      "title": "Why is an Event Library needed at all?",
      "role": "content",
      "text": "Let us figure it out through a series of Q&As.\n\nQ: What do you expect a network server to be doing all the time? <br/>\nA: Watch for inbound connections on the port its listening and accept them.\n\nQ: Calling [accept](http://man.cx/accept%282%29) yields a descriptor. What do I do with it?<br/>\nA: Save the descriptor and do a non-blocking read/write operation on it.\n\nQ: Why does the read/write have to be non-blocking?<br/>\nA: 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.\n\nQ: I guess I have to do many such non-blocking operations on the socket to see when it's ready. Am I right?<br/>\nA: Yes. That is what an event library does for you. Now you get it.\n\nQ: How do Event Libraries do what they do?<br/>\nA: They use the operating system's polling facility along with timers.\n\nQ: So are there any open source event libraries that do what you just described? <br/>\nA: Yes. `libevent` and `libev` are two such event libraries that I can recall off the top of my head.\n\nQ: Does Redis use such open source event libraries for handling socket I/O?<br/>\nA: No. For various [reasons](http://groups.google.com/group/redis-db/browse_thread/thread/b52814e9ef15b8d0/) Redis uses its own event library."
    },
    {
      "id": "the-redis-event-library",
      "title": "The Redis event library",
      "role": "content",
      "text": "Redis implements its own event library. The event library is implemented in `ae.c`.\n\nThe best way to understand how the Redis event library works is to understand how Redis uses it.\n\nEvent Loop Initialization\n---\n\n`initServer` function defined in `redis.c` initializes the numerous fields of the `redisServer` structure variable. One such field is the Redis event loop `el`:\n\n    aeEventLoop *el\n\n`initServer` initializes `server.el` field by calling `aeCreateEventLoop` defined in `ae.c`. The definition of `aeEventLoop` is below:\n\n    typedef struct aeEventLoop\n    {\n        int maxfd;\n        long long timeEventNextId;\n        aeFileEvent events[AE_SETSIZE]; /* Registered events */\n        aeFiredEvent fired[AE_SETSIZE]; /* Fired events */\n        aeTimeEvent *timeEventHead;\n        int stop;\n        void *apidata; /* This is used for polling API specific data */\n        aeBeforeSleepProc *beforesleep;\n    } aeEventLoop;\n\n`aeCreateEventLoop`\n---\n\n`aeCreateEventLoop` first `malloc`s `aeEventLoop` structure then calls `ae_epoll.c:aeApiCreate`.\n\n`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.\n\nNext 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.\n\n`aeCreateTimeEvent`\n---\n\n`aeCreateTimeEvent` accepts the following as parameters:\n\n  * `eventLoop`: This is `server.el` in `redis.c`\n  * milliseconds: The number of milliseconds from the current time after which the timer expires.\n  * `proc`: Function pointer. Stores the address of the function that has to be called after the timer expires.\n  * `clientData`: Mostly `NULL`.\n  * `finalizerProc`: Pointer to the function that has to be called before the timed event is removed from the list of timed events.\n\n`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:\n\n    aeCreateTimeEvent(server.el /*eventLoop*/, 1 /*milliseconds*/, serverCron /*proc*/, NULL /*clientData*/, NULL /*finalizerProc*/);\n\n`redis.c:serverCron` performs many operations that helps keep Redis running properly.\n\n`aeCreateFileEvent`\n---\n\nThe 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`.\n\nFollowing is an explanation of what precisely `aeCreateFileEvent` does when called from `redis.c:initServer`.\n\n`initServer` passes the following arguments to `aeCreateFileEvent`:\n\n  * `server.el`: The event loop created by `aeCreateEventLoop`. The `epoll` descriptor is got from `server.el`.\n  * `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.\n  * `AE_READABLE`: Signifies that `server.fd` has to be watched for `EPOLLIN` event.\n  * `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`.\n\nThis completes the initialization of Redis event loop.\n\nEvent Loop Processing\n---\n\n`ae.c:aeMain` called from `redis.c:main` does the job of processing the event loop that is initialized in the previous phase.\n\n`ae.c:aeMain` calls `ae.c:aeProcessEvents` in a while loop that processes pending time and file events.\n\n`aeProcessEvents`\n---\n\n`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`.\n\nRemember, 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.\n\nThe `tvp` structure variable along with the event loop variable is passed to `ae_epoll.c:aeApiPoll`.\n\n`aeApiPoll` functions does an [`epoll_wait`](http://man.cx/epoll_wait) on the `epoll` descriptor and populates the `eventLoop->fired` table with the details:\n\n  * `fd`: The descriptor that is now ready to do a read/write operation depending on the mask value.\n  * `mask`: The read/write event that can now be performed on the corresponding descriptor.\n\n`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`.\n\nNow, `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:\n\n    if (aeCreateFileEvent(server.el, c->fd, AE_READABLE,\n        readQueryFromClient, c) == AE_ERR) {\n        freeClient(c);\n        return NULL;\n    }\n\n`c` is the `redisClient` structure variable and `c->fd` is the connected descriptor.\n\nNext the `ae.c:aeProcessEvent` calls `ae.c:processTimeEvents`\n\n`processTimeEvents`\n---\n\n`ae.processTimeEvents` iterates over list of time events starting at `eventLoop->timeEventHead`.\n\nFor 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.\n\nThat's all."
    }
  ],
  "examples": []
}
