How to access user-context data set on epoll when calling epoll_wait

Below I add sockets to epoll and set an application-context index within epoll_event.data.u32.

When receiving packets, recv() requires the socket file descriptor. In all the examples events[i].data.fd is used.

However, events[i].data.fd and events[i].data.u32 are in a union, so how do I also access my user-context index events[i].data.u32? It looks like it is overwritten with the socket file descriptor?

// Initially
int epollFd = epoll_create1(0);

// Adding each socket, along with a user-context index for callbacks
struct epoll_event event;
event.events = EPOLLIN;
event.data.u32 = callbackIndex; // Here is the user-defined index
int sock = createSocket(port, address);
assert(epoll_ctl(epollFd, EPOLL_CTL_ADD, sock, &event));

// Later when receiving packets

struct epoll_event events[MAX_EVENTS];

while (true)
{
    int event_count = epoll_wait(epollFd, events, MAX_EVENTS, 30000);
            
    for (i = 0; i < event_count; i++)
    {
        int n = recv(events[i].data.fd, &buffer[0], sizeof(buffer), flags);
        // How do I access the user-context index I set when adding the socket to epoll?
    }
}

Answer

You tell epoll_ctl() which socket descriptor you want to listen for events for, and provide an epoll_event struct to associate with that listen operation.

Whenever epoll_wait() detects a registered event on a socket, it gives you back only the epoll_event struct that you had provided for that event, exactly as you had provided it. It does not tell you which socket triggered the event.

So, if you want to discover the socket, you have to either:

  • store the socket descriptor itself in the epoll_event, but then you can’t use any other user-defined data.

  • store the socket descriptor somewhere else (ie, in an array, an object pool, etc) and then put identifying information needed to get back to the socket descriptor as user-defined data in the epoll_event (ie, array index, object pointer, etc).

Whatever you put in the epoll_event when calling epoll_ctl() is what you will get back from epoll_wait(). No more, no less.