Implement init/deinit of inotify system

Use RAII to handle inotify resource lifetime, i.e. initialize with actor
creation and deinitialize with actor deletion.
This commit is contained in:
T. R. Bernstein
2026-03-11 17:25:27 +01:00
parent 1a7e5ca5de
commit 098339f9d1
6 changed files with 65 additions and 2 deletions

View File

@@ -22,9 +22,11 @@ let package = Package(
.package(url: "https://github.com/tuist/Noora", from: "0.55.1")
],
targets: [
.systemLibrary(name: "CInotify"),
.target(
name: "Inotify",
dependencies: [
"CInotify",
.product(name: "Logging", package: "swift-log"),
]
),

View File

@@ -0,0 +1,24 @@
#ifndef CINOTIFY_H
#define CINOTIFY_H
#include <stdlib.h>
#include <sys/inotify.h>
#include <errno.h>
static inline int cinotify_deinit(int fd) {
return close(fd);
}
static inline int cinotify_get_errno(void) {
return errno;
}
static inline char* get_error_message() {
int error_number = errno;
errno = 0;
char* error_message = strerror(error_number);
if (errno > 0) return NULL;
return error_message;
}
#endif

View File

@@ -0,0 +1,4 @@
module CInotify [system] {
header "cinotify.h"
export *
}

View File

@@ -1,2 +1,16 @@
actor Inotify {
import CInotify
public actor Inotify {
private let fd: Int32
public init() throws {
self.fd = inotify_init1(Int32(IN_NONBLOCK | IN_CLOEXEC))
guard self.fd >= 0 else {
throw InotifyError.initFailed(errno: cinotify_get_errno())
}
}
deinit {
cinotify_deinit(self.fd)
}
}

View File

@@ -0,0 +1,19 @@
import CInotify
public enum InotifyError: Error, Sendable, CustomStringConvertible {
case initFailed(errno: Int32)
public var description: String {
switch self {
case .initFailed(let code):
"inotify_init1 failed: \(readableErrno(code))"
}
}
private func readableErrno(_ code: Int32) -> String {
if let cStr = get_error_message() {
return String(cString: cStr) + " (errno \(code))"
}
return "errno \(code)"
}
}

View File

@@ -4,6 +4,6 @@ import Testing
@Suite("Initialisation")
struct InitTests {
@Test func createsCleanly() async throws {
let _ = Inotify()
let _ = try Inotify()
}
}