Event driven state machines: file system events

Hi, I wrote yet another example of edsm - monitoring events in file system (based on inotify). Below are some examples of how it works.

Prerequisites:

/opt/zig/zig build
mkdir /tmp/test/
./zig-out/bin/dirmon /tmp/test/

Program output when scanning the directory with ls -l:

file system events = 0x40000020
DirMon-1 @ WORK got 'F5' from OS
'/tmp/test/': opened
file system events = 0x40000001
DirMon-1 @ WORK got 'F0' from OS
'/tmp/test/': accessed
file system events = 0x40000010
DirMon-1 @ WORK got 'F4' from OS
'/tmp/test/': closed (read)

Program output when touching a file with touch /tmp/test/a-file:

file system events = 0x00000100
DirMon-1 @ WORK got 'F8' from OS
'a-file': created
file system events = 0x00000020
DirMon-1 @ WORK got 'F5' from OS
'a-file': opened
file system events = 0x00000004
DirMon-1 @ WORK got 'F2' from OS
'a-file': metadata changed
file system events = 0x00000008
DirMon-1 @ WORK got 'F3' from OS
'a-file': closed (write)

Program output when deleting the directory being monitored:

DirMon-1 @ WORK got 'F10' from OS
'/tmp/test/': deleted
file system events = 0x00008000
DirMon-1 @ WORK got 'F15' from OS
'/tmp/test/': ignored
DirMon-1 @ WORK got 'S1' from OS
got signal #15 from PID 110187
Bye!
2 Likes

I made (eventually successful) attempt to redesign this example in such a way that it does not use heap allocation (as an exercize). And I must confess that was not very easy adventure for me. It seems I am more comfortable with heap allocation rather than stack one with my current understanding of Zig :slight_smile:

Also I’ve made a couple of things along the way:

  • generic (fixed sized) ring buffer (I tried to implement it before but at that time something went wrong)
  • the code related to event sources (signals and so on) was completely refactored, now it is more OOP-fashioned:
pub const EventSource = struct {

    id: i32 = -1, // fd in most cases, but not always
    owner: *StageMachine,
    eq: *ecap.EventQueue,
    // "virtual method"
    readInfoImpl: *const fn(es: *EventSource, event_mask: u32) anyerror!RowCol,
    pub fn readInfo(es: *EventSource, event_mask: u32) !RowCol {
        return try es.readInfoImpl(es, event_mask);
    }
    // "final methods"
    pub fn enable(es: *EventSource) !void {
        try es.eq.enableCanRead(es);
    }
    pub fn disable(es: *EventSource) !void {
        try es.eq.disableEventSource(es);
    }
};

pub const Timer = struct {
    es: EventSource, // inheritance
....
    // "specific method"
    pub fn start(tm: *Timer, msec: u32) !void {
        return try setValue(tm.es.id, msec);
    }

No, I am not a big fan of OOP languages, but sometimes inheritance/polymorphism are convenient things and what is good that in C/Zig they are explicit.

The sources are here. I am using @fieldParentPtr() hard (event sources and machines themselves code), because I just like to do this way, it is an analog of well-known magic container_of() C macro.