diff options
| author | Laytan Laats <laytanlaats@hotmail.com> | 2026-01-11 20:12:28 +0100 |
|---|---|---|
| committer | Laytan Laats <laytanlaats@hotmail.com> | 2026-01-11 20:21:25 +0100 |
| commit | 872fe3ff73b4673ac3e393da6a69b98ae5c4dcf2 (patch) | |
| tree | 8d718a907b639fc6dc5d484fcf33f8b4068e4e0f | |
| parent | bc7040209364f06817ceeff4d5effd599f70f5f5 (diff) | |
kqueue: fix bsds, add user filter, add struct tests
| -rw-r--r-- | core/sys/kqueue/kqueue.odin | 93 | ||||
| -rw-r--r-- | tests/core/normal.odin | 1 | ||||
| -rw-r--r-- | tests/core/sys/kqueue/structs.odin | 56 | ||||
| -rw-r--r-- | tests/core/sys/kqueue/structs/structs.c | 63 | ||||
| -rw-r--r-- | tests/core/sys/kqueue/structs/structs.odin | 58 |
5 files changed, 244 insertions, 27 deletions
diff --git a/core/sys/kqueue/kqueue.odin b/core/sys/kqueue/kqueue.odin index 25ee9bdce..b51f3426f 100644 --- a/core/sys/kqueue/kqueue.odin +++ b/core/sys/kqueue/kqueue.odin @@ -32,6 +32,7 @@ kevent :: proc(kq: KQ, change_list: []KEvent, event_list: []KEvent, timeout: ^po timeout, ) if n_events == -1 { + n_events = 0 err = posix.errno() } return @@ -60,6 +61,7 @@ Filter :: enum _Filter_Backing { Proc = _FILTER_PROC, // Check for changes to the subject process. Signal = _FILTER_SIGNAL, // Check for signals delivered to the process. Timer = _FILTER_TIMER, // Timers. + User = _FILTER_USER, // User events. } RW_Flag :: enum u32 { @@ -82,18 +84,30 @@ Proc_Flag :: enum u32 { Exit = log2(0x80000000), // Process exited. Fork = log2(0x40000000), // Process forked. Exec = log2(0x20000000), // Process exec'd. - Signal = log2(0x08000000), // Shared with `Filter.Signal`. } Proc_Flags :: bit_set[Proc_Flag; u32] Timer_Flag :: enum u32 { Seconds = log2(0x00000001), // Data is seconds. - USeconds = log2(0x00000002), // Data is microseconds. - NSeconds = log2(_NOTE_NSECONDS), // Data is nanoseconds. + USeconds = log2(_NOTE_USECONDS), // Data is microseconds. Absolute = log2(_NOTE_ABSOLUTE), // Absolute timeout. } Timer_Flags :: bit_set[Timer_Flag; u32] +User_Flag :: enum u32 { + Trigger = log2(0x01000000), + FFAnd = log2(0x40000000), + FFOr = log2(0x80000000), +} +User_Flags :: bit_set[User_Flag; u32] + +USER_FLAGS_COPY :: User_Flags{.FFOr, .FFAnd} +USER_FLAGS_CONTROL_MASK :: transmute(User_Flags)u32(0xc0000000) +USER_FLAGS_MASK :: transmute(User_Flags)u32(0x00FFFFFF) + +// Data is nanoseconds. +TIMER_FLAGS_NSECONDS :: _TIMER_FLAGS_NSECONDS + when ODIN_OS == .Darwin { _Filter_Backing :: distinct i16 @@ -106,10 +120,14 @@ when ODIN_OS == .Darwin { _FILTER_PROC :: -5 _FILTER_SIGNAL :: -6 _FILTER_TIMER :: -7 + _FILTER_USER :: -10 + _NOTE_USECONDS :: 0x00000002 _NOTE_NSECONDS :: 0x00000004 _NOTE_ABSOLUTE :: 0x00000008 + _TIMER_FLAGS_NSECONDS :: Timer_Flags{Timer_Flag(log2(_NOTE_NSECONDS))} + KEvent :: struct #align(4) { // Value used to identify this event. The exact interpretation is determined by the attached filter. ident: uintptr, @@ -119,11 +137,12 @@ when ODIN_OS == .Darwin { flags: Flags, // Filter specific flags. fflags: struct #raw_union { - rw: RW_Flags, - vnode: VNode_Flags, - fproc: Proc_Flags, + rw: RW_Flags `raw_union_tag:"filter=.Read, filter=.Write"`, + vnode: VNode_Flags `raw_union_tag:"filter=.VNode"`, + fproc: Proc_Flags `raw_union_tag:"filter=.Proc"`, // vm: VM_Flags, - timer: Timer_Flags, + timer: Timer_Flags `raw_union_tag:"filter=.Timer"`, + user: User_Flags `raw_union_tag:"filter=.User"`, }, // Filter specific data. data: c.long /* intptr_t */, @@ -143,9 +162,13 @@ when ODIN_OS == .Darwin { _FILTER_PROC :: -5 _FILTER_SIGNAL :: -6 _FILTER_TIMER :: -7 + _FILTER_USER :: -11 - _NOTE_NSECONDS :: 0x00000004 - _NOTE_ABSOLUTE :: 0x00000008 + _NOTE_USECONDS :: 0x00000004 + _NOTE_NSECONDS :: 0x00000008 + _NOTE_ABSOLUTE :: 0x00000010 + + _TIMER_FLAGS_NSECONDS :: Timer_Flags{Timer_Flag(log2(_NOTE_NSECONDS))} KEvent :: struct { // Value used to identify this event. The exact interpretation is determined by the attached filter. @@ -156,11 +179,12 @@ when ODIN_OS == .Darwin { flags: Flags, // Filter specific flags. fflags: struct #raw_union { - rw: RW_Flags, - vnode: VNode_Flags, - fproc: Proc_Flags, + rw: RW_Flags `raw_union_tag:"filter=.Read, filter=.Write"`, + vnode: VNode_Flags `raw_union_tag:"filter=.VNode"`, + fproc: Proc_Flags `raw_union_tag:"filter=.Proc"`, // vm: VM_Flags, - timer: Timer_Flags, + timer: Timer_Flags `raw_union_tag:"filter=.Timer"`, + user: User_Flags `raw_union_tag:"filter=.User"`, }, // Filter specific data. data: i64, @@ -181,11 +205,14 @@ when ODIN_OS == .Darwin { _FILTER_PROC :: 4 _FILTER_SIGNAL :: 5 _FILTER_TIMER :: 6 + _FILTER_USER :: 8 - _NOTE_NSECONDS :: 0x00000003 + _NOTE_USECONDS :: 0x00000002 _NOTE_ABSOLUTE :: 0x00000010 - KEvent :: struct #align(4) { + _TIMER_FLAGS_NSECONDS :: Timer_Flags{.Seconds, .USeconds} + + KEvent :: struct { // Value used to identify this event. The exact interpretation is determined by the attached filter. ident: uintptr, // Filter for event. @@ -194,18 +221,17 @@ when ODIN_OS == .Darwin { flags: Flags, // Filter specific flags. fflags: struct #raw_union { - rw: RW_Flags, - vnode: VNode_Flags, - fproc: Proc_Flags, + rw: RW_Flags `raw_union_tag:"filter=.Read, filter=.Write"`, + vnode: VNode_Flags `raw_union_tag:"filter=.VNode"`, + fproc: Proc_Flags `raw_union_tag:"filter=.Proc"`, // vm: VM_Flags, - timer: Timer_Flags, + timer: Timer_Flags `raw_union_tag:"filter=.Timer"`, + user: User_Flags `raw_union_tag:"filter=.User"`, }, // Filter specific data. data: i64, // Opaque user data passed through the kernel unchanged. udata: rawptr, - // Extensions. - ext: [4]u64, } } else when ODIN_OS == .OpenBSD { @@ -219,10 +245,14 @@ when ODIN_OS == .Darwin { _FILTER_PROC :: -5 _FILTER_SIGNAL :: -6 _FILTER_TIMER :: -7 + _FILTER_USER :: -10 - _NOTE_NSECONDS :: 0x00000003 + _NOTE_USECONDS :: 0x00000002 + _NOTE_NSECONDS :: 0x00000004 _NOTE_ABSOLUTE :: 0x00000010 + _TIMER_FLAGS_NSECONDS :: Timer_Flags{Timer_Flag(log2(_NOTE_NSECONDS))} + KEvent :: struct #align(4) { // Value used to identify this event. The exact interpretation is determined by the attached filter. ident: uintptr, @@ -232,11 +262,12 @@ when ODIN_OS == .Darwin { flags: Flags, // Filter specific flags. fflags: struct #raw_union { - rw: RW_Flags, - vnode: VNode_Flags, - fproc: Proc_Flags, + rw: RW_Flags `raw_union_tag:"filter=.Read, filter=.Write"`, + vnode: VNode_Flags `raw_union_tag:"filter=.VNode"`, + fproc: Proc_Flags `raw_union_tag:"filter=.Proc"`, // vm: VM_Flags, - timer: Timer_Flags, + timer: Timer_Flags `raw_union_tag:"filter=.Timer"`, + user: User_Flags `raw_union_tag:"filter=.User"`, }, // Filter specific data. data: i64, @@ -245,12 +276,20 @@ when ODIN_OS == .Darwin { } } +when ODIN_OS == .NetBSD { + @(private) + LKEVENT :: "__kevent50" +} else { + @(private) + LKEVENT :: "kevent" +} + @(private) log2 :: intrinsics.constant_log2 foreign lib { @(link_name="kqueue") _kqueue :: proc() -> KQ --- - @(link_name="kevent") + @(link_name=LKEVENT) _kevent :: proc(kq: KQ, change_list: [^]KEvent, n_changes: c.int, event_list: [^]KEvent, n_events: c.int, timeout: ^posix.timespec) -> c.int --- } diff --git a/tests/core/normal.odin b/tests/core/normal.odin index e8b61fee8..42da389d2 100644 --- a/tests/core/normal.odin +++ b/tests/core/normal.odin @@ -45,6 +45,7 @@ download_assets :: proc "contextless" () { @(require) import "sync" @(require) import "sync/chan" @(require) import "sys/posix" +@(require) import "sys/kqueue" @(require) import "sys/windows" @(require) import "text/i18n" @(require) import "text/match" diff --git a/tests/core/sys/kqueue/structs.odin b/tests/core/sys/kqueue/structs.odin new file mode 100644 index 000000000..edf1fdd1e --- /dev/null +++ b/tests/core/sys/kqueue/structs.odin @@ -0,0 +1,56 @@ +#+build darwin, freebsd, openbsd, netbsd +package tests_core_sys_kqueue + +import "core:strings" +import "core:testing" +import os "core:os/os2" + +@(test) +structs :: proc(t: ^testing.T) { + { + c_compiler := os.get_env("CC", context.temp_allocator) + if c_compiler == "" { + c_compiler = "clang" + } + + c_compilation, c_start_err := os.process_start({ + command = {c_compiler, #directory + "/structs/structs.c", "-o", #directory + "/structs/c_structs"}, + stdout = os.stdout, + stderr = os.stderr, + }) + testing.expect_value(t, c_start_err, nil) + + o_compilation, o_start_err := os.process_start({ + command = {ODIN_ROOT + "/odin", "build", #directory + "/structs", "-out:" + #directory + "/structs/odin_structs"}, + stdout = os.stdout, + stderr = os.stderr, + }) + testing.expect_value(t, o_start_err, nil) + + c_status, c_err := os.process_wait(c_compilation) + testing.expect_value(t, c_err, nil) + testing.expect_value(t, c_status.exit_code, 0) + + o_status, o_err := os.process_wait(o_compilation) + testing.expect_value(t, o_err, nil) + testing.expect_value(t, o_status.exit_code, 0) + } + + c_status, c_stdout, c_stderr, c_err := os.process_exec({command={#directory + "/structs/c_structs"}}, context.temp_allocator) + testing.expect_value(t, c_err, nil) + testing.expect_value(t, c_status.exit_code, 0) + testing.expect_value(t, string(c_stderr), "") + + o_status, o_stdout, o_stderr, o_err := os.process_exec({command={#directory + "/structs/odin_structs"}}, context.temp_allocator) + testing.expect_value(t, o_err, nil) + testing.expect_value(t, o_status.exit_code, 0) + testing.expect_value(t, string(o_stderr), "") + + testing.expect(t, strings.trim_space(string(c_stdout)) != "") + + testing.expect_value( + t, + strings.trim_space(string(o_stdout)), + strings.trim_space(string(c_stdout)), + ) +} diff --git a/tests/core/sys/kqueue/structs/structs.c b/tests/core/sys/kqueue/structs/structs.c new file mode 100644 index 000000000..e7620c994 --- /dev/null +++ b/tests/core/sys/kqueue/structs/structs.c @@ -0,0 +1,63 @@ +#include <stddef.h> +#include <stdio.h> +#include <sys/event.h> + +int main(int argc, char *argv[]) +{ + printf("kevent %zu %zu\n", sizeof(struct kevent), _Alignof(struct kevent)); + printf("kevent.ident %zu\n", offsetof(struct kevent, ident)); + printf("kevent.filter %zu\n", offsetof(struct kevent, filter)); + printf("kevent.flags %zu\n", offsetof(struct kevent, flags)); + printf("kevent.fflags %zu\n", offsetof(struct kevent, fflags)); + printf("kevent.data %zu\n", offsetof(struct kevent, data)); + printf("kevent.udata %zu\n", offsetof(struct kevent, udata)); + + printf("EV_ADD %d\n", EV_ADD); + printf("EV_DELETE %d\n", EV_DELETE); + printf("EV_ENABLE %d\n", EV_ENABLE); + printf("EV_DISABLE %d\n", EV_DISABLE); + printf("EV_ONESHOT %d\n", EV_ONESHOT); + printf("EV_CLEAR %d\n", EV_CLEAR); + printf("EV_RECEIPT %d\n", EV_RECEIPT); + printf("EV_DISPATCH %d\n", EV_DISPATCH); + printf("EV_ERROR %d\n", EV_ERROR); + printf("EV_EOF %d\n", EV_EOF); + + printf("EVFILT_READ %d\n", EVFILT_READ); + printf("EVFILT_WRITE %d\n", EVFILT_WRITE); + printf("EVFILT_AIO %d\n", EVFILT_AIO); + printf("EVFILT_VNODE %d\n", EVFILT_VNODE); + printf("EVFILT_PROC %d\n", EVFILT_PROC); + printf("EVFILT_SIGNAL %d\n", EVFILT_SIGNAL); + printf("EVFILT_TIMER %d\n", EVFILT_TIMER); + printf("EVFILT_USER %d\n", EVFILT_USER); + + printf("NOTE_SECONDS %u\n", NOTE_SECONDS); + printf("NOTE_USECONDS %u\n", NOTE_USECONDS); + printf("NOTE_NSECONDS %u\n", NOTE_NSECONDS); +#if defined(NOTE_ABSOLUTE) + printf("NOTE_ABSOLUTE %u\n", NOTE_ABSOLUTE); +#else + printf("NOTE_ABSOLUTE %u\n", NOTE_ABSTIME); +#endif + + printf("NOTE_LOWAT %u\n", NOTE_LOWAT); + + printf("NOTE_DELETE %u\n", NOTE_DELETE); + printf("NOTE_WRITE %u\n", NOTE_WRITE); + printf("NOTE_EXTEND %u\n", NOTE_EXTEND); + printf("NOTE_ATTRIB %u\n", NOTE_ATTRIB); + printf("NOTE_LINK %u\n", NOTE_LINK); + printf("NOTE_RENAME %u\n", NOTE_RENAME); + printf("NOTE_REVOKE %u\n", NOTE_REVOKE); + + printf("NOTE_EXIT %u\n", NOTE_EXIT); + printf("NOTE_FORK %u\n", NOTE_FORK); + printf("NOTE_EXEC %u\n", NOTE_EXEC); + + printf("NOTE_TRIGGER %u\n", NOTE_TRIGGER); + printf("NOTE_FFAND %u\n", NOTE_FFAND); + printf("NOTE_FFOR %u\n", NOTE_FFOR); + printf("NOTE_FFCOPY %u\n", NOTE_FFCOPY); + return 0; +} diff --git a/tests/core/sys/kqueue/structs/structs.odin b/tests/core/sys/kqueue/structs/structs.odin new file mode 100644 index 000000000..4886f63e4 --- /dev/null +++ b/tests/core/sys/kqueue/structs/structs.odin @@ -0,0 +1,58 @@ +package main + +import "core:fmt" +import "core:sys/kqueue" + +main :: proc() { + fmt.println("kevent", size_of(kqueue.KEvent), align_of(kqueue.KEvent)) + fmt.println("kevent.ident", offset_of(kqueue.KEvent, ident)) + fmt.println("kevent.filter", offset_of(kqueue.KEvent, filter)) + fmt.println("kevent.flags", offset_of(kqueue.KEvent, flags)) + fmt.println("kevent.fflags", offset_of(kqueue.KEvent, fflags)) + fmt.println("kevent.data", offset_of(kqueue.KEvent, data)) + fmt.println("kevent.udata", offset_of(kqueue.KEvent, udata)) + + fmt.println("EV_ADD", transmute(kqueue._Flags_Backing)kqueue.Flags{.Add}) + fmt.println("EV_DELETE", transmute(kqueue._Flags_Backing)kqueue.Flags{.Delete}) + fmt.println("EV_ENABLE", transmute(kqueue._Flags_Backing)kqueue.Flags{.Enable}) + fmt.println("EV_DISABLE", transmute(kqueue._Flags_Backing)kqueue.Flags{.Disable}) + fmt.println("EV_ONESHOT", transmute(kqueue._Flags_Backing)kqueue.Flags{.One_Shot}) + fmt.println("EV_CLEAR", transmute(kqueue._Flags_Backing)kqueue.Flags{.Clear}) + fmt.println("EV_RECEIPT", transmute(kqueue._Flags_Backing)kqueue.Flags{.Receipt}) + fmt.println("EV_DISPATCH", transmute(kqueue._Flags_Backing)kqueue.Flags{.Dispatch}) + fmt.println("EV_ERROR", transmute(kqueue._Flags_Backing)kqueue.Flags{.Error}) + fmt.println("EV_EOF", transmute(kqueue._Flags_Backing)kqueue.Flags{.EOF}) + + fmt.println("EVFILT_READ", int(kqueue.Filter.Read)) + fmt.println("EVFILT_WRITE", int(kqueue.Filter.Write)) + fmt.println("EVFILT_AIO", int(kqueue.Filter.AIO)) + fmt.println("EVFILT_VNODE", int(kqueue.Filter.VNode)) + fmt.println("EVFILT_PROC", int(kqueue.Filter.Proc)) + fmt.println("EVFILT_SIGNAL", int(kqueue.Filter.Signal)) + fmt.println("EVFILT_TIMER", int(kqueue.Filter.Timer)) + fmt.println("EVFILT_USER", int(kqueue.Filter.User)) + + fmt.println("NOTE_SECONDS", transmute(u32)kqueue.Timer_Flags{.Seconds}) + fmt.println("NOTE_USECONDS", transmute(u32)kqueue.Timer_Flags{.USeconds}) + fmt.println("NOTE_NSECONDS", transmute(u32)kqueue.TIMER_FLAGS_NSECONDS) + fmt.println("NOTE_ABSOLUTE", transmute(u32)kqueue.Timer_Flags{.Absolute}) + + fmt.println("NOTE_LOWAT", transmute(u32)kqueue.RW_Flags{.Low_Water_Mark}) + + fmt.println("NOTE_DELETE", transmute(u32)kqueue.VNode_Flags{.Delete}) + fmt.println("NOTE_WRITE", transmute(u32)kqueue.VNode_Flags{.Write}) + fmt.println("NOTE_EXTEND", transmute(u32)kqueue.VNode_Flags{.Extend}) + fmt.println("NOTE_ATTRIB", transmute(u32)kqueue.VNode_Flags{.Attrib}) + fmt.println("NOTE_LINK", transmute(u32)kqueue.VNode_Flags{.Link}) + fmt.println("NOTE_RENAME", transmute(u32)kqueue.VNode_Flags{.Rename}) + fmt.println("NOTE_REVOKE", transmute(u32)kqueue.VNode_Flags{.Revoke}) + + fmt.println("NOTE_EXIT", transmute(u32)kqueue.Proc_Flags{.Exit}) + fmt.println("NOTE_FORK", transmute(u32)kqueue.Proc_Flags{.Fork}) + fmt.println("NOTE_EXEC", transmute(u32)kqueue.Proc_Flags{.Exec}) + + fmt.println("NOTE_TRIGGER", transmute(u32)kqueue.User_Flags{.Trigger}) + fmt.println("NOTE_FFAND", transmute(u32)kqueue.User_Flags{.FFAnd}) + fmt.println("NOTE_FFOR", transmute(u32)kqueue.User_Flags{.FFOr}) + fmt.println("NOTE_FFCOPY", transmute(u32)kqueue.USER_FLAGS_COPY) +} |