inotify
asynchronous reading
file monitoring
Linux
error handling

Asynchronous reading on the inotify descriptor failed

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

An inotify file descriptor is just another Linux file descriptor, so asynchronous reading failures usually come from ordinary I/O problems: reading at the wrong time, using a buffer that is too small, mishandling nonblocking mode, or racing multiple readers against the same descriptor. The fix is usually in how the event loop consumes inotify events, not in inotify itself.

How Inotify Reading Works

You create an inotify instance, add watches, and then read a stream of struct inotify_event records from the descriptor. Those records are variable-length because an event may include a filename after the fixed header.

That means your read logic must handle:

  • readiness notification correctly
  • nonblocking behavior such as EAGAIN
  • enough buffer space for one or more complete events
  • parsing multiple events from one read call

Nonblocking Example In C

A common asynchronous pattern is to mark the descriptor nonblocking and wait with poll or epoll.

c
1#include <sys/inotify.h>
2#include <poll.h>
3#include <unistd.h>
4#include <stdio.h>
5#include <errno.h>
6
7int main() {
8    int fd = inotify_init1(IN_NONBLOCK);
9    int wd = inotify_add_watch(fd, "/tmp", IN_CREATE | IN_MODIFY);
10
11    struct pollfd pfd = { .fd = fd, .events = POLLIN };
12    char buffer[4096];
13
14    while (1) {
15        int ready = poll(&pfd, 1, -1);
16        if (ready <= 0) {
17            continue;
18        }
19
20        ssize_t len = read(fd, buffer, sizeof(buffer));
21        if (len < 0) {
22            if (errno == EAGAIN) {
23                continue;
24            }
25            perror("read");
26            break;
27        }
28
29        for (char *ptr = buffer; ptr < buffer + len; ) {
30            struct inotify_event *event = (struct inotify_event *)ptr;
31            printf("mask=%u name=%s\n", event->mask, event->len ? event->name : "");
32            ptr += sizeof(struct inotify_event) + event->len;
33        }
34    }
35
36    inotify_rm_watch(fd, wd);
37    close(fd);
38    return 0;
39}

The important parts are handling EAGAIN correctly and walking through every event packed into the buffer.

Common Failure Modes

The most common asynchronous read failures are predictable:

  • calling read on a nonblocking descriptor before it is ready
  • assuming one read returns exactly one event
  • using a buffer too small to hold realistic batches of events
  • closing the descriptor while another thread still polls or reads it
  • reading the same inotify descriptor from multiple threads without coordination

In many cases the error message looks mysterious, but the root cause is just ordinary file-descriptor misuse.

Why Partial Parsing Breaks Things

If you treat the event stream like fixed-size records, parsing will drift because inotify_event.name has variable length. One bad increment and every later event becomes garbage. That can look like asynchronous read corruption even when the underlying read succeeded.

The parser must advance by:

  • 'sizeof(struct inotify_event) + event->len'

for every event in the returned buffer.

Python Example With Selectors

The same pattern appears in higher-level code.

python
1import os
2import selectors
3
4fd = os.inotify_init()
5wd = os.inotify_add_watch(fd, "/tmp", os.IN_CREATE)
6os.set_blocking(fd, False)
7
8sel = selectors.DefaultSelector()
9sel.register(fd, selectors.EVENT_READ)
10
11while True:
12    for key, _ in sel.select():
13        try:
14            data = os.read(fd, 4096)
15            print(len(data))
16        except BlockingIOError:
17            pass

Even in Python, readiness plus nonblocking error handling is the core idea.

Common Pitfalls

The most common mistake is expecting asynchronous mode to remove the need for readiness handling. Nonblocking I/O still needs poll, select, epoll, or a higher-level event loop.

Another mistake is reading with tiny buffers and assuming the kernel will split events in a friendly way. Use a reasonably sized buffer and parse all events returned.

A third issue is allowing more than one consumer to read the same inotify descriptor. That makes event consumption nondeterministic and very hard to debug.

Summary

  • Inotify asynchronous read failures usually come from ordinary descriptor misuse.
  • Use readiness APIs such as poll or epoll with nonblocking descriptors.
  • Handle EAGAIN as a normal nonblocking condition.
  • Parse every event in the returned buffer using the event length field.
  • Keep one coordinated reader per inotify descriptor whenever possible.

Course illustration
Course illustration

All Rights Reserved.