Skip to content

Conversation

nabijaczleweli
Copy link
Contributor

Using pipes works, but is unnecessarily thick for a one-shot application like this, and leaves 2 largely-useless pipe fds in the process permanently

sem_post(3) is explicitly allowed to be used in signal handlers, this is practically the target application of POSIX semaphores

readme_example/glibc before:

3735433 rt_sigaction(SIGBUS, {sa_handler=0x56412e4350e0, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7f57bb5dd050}, NULL, 8) = 0
3735433 pipe2([3, 4], O_CLOEXEC)        = 0
3735433 fcntl(4, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
3735433 rt_sigaction(SIGINT, {sa_handler=0x56412e40b410, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f57bb5dd050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
3735433 rt_sigaction(SIGRT_1, {sa_handler=0x7f57bb627720, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f57bb5dd050}, NULL, 8) = 0
3735433 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
3735433 mmap(NULL, 2101248, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f57bb39d000
3735433 mprotect(0x7f57bb39e000, 2097152, PROT_READ|PROT_WRITE) = 0
3735433 rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0
3735433 clone3({flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, child_tid=0x7f57bb59d990, parent_tid=0x7f57bb59d990, exit_signal=0, stack=0x7f57bb39d000, stack_size=0x1fff00, tls=0x7f57bb59d6c0} => {parent_tid=[3735438]}, 88) = 3735438
3735438 rseq(0x7f57bb59dfe0, 0x20, 0, 0x53053053 <unfinished ...>
3735433 rt_sigprocmask(SIG_SETMASK, [],  <unfinished ...>
3735438 <... rseq resumed>)             = 0
3735433 <... rt_sigprocmask resumed>NULL, 8) = 0
3735438 set_robust_list(0x7f57bb59d9a0, 24) = 0
3735433 write(1, "Waiting for Ctrl-C...\n", 22 <unfinished ...>
3735438 rt_sigprocmask(SIG_SETMASK, [],  <unfinished ...>
3735433 <... write resumed>)            = 22
3735438 <... rt_sigprocmask resumed>NULL, 8) = 0
3735438 mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0 <unfinished ...>
3735433 futex(0x56415f72c028, FUTEX_WAIT_BITSET_PRIVATE, 4294967295, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...>
3735438 <... mmap resumed>)             = 0x7f57b339d000
3735438 munmap(0x7f57b339d000, 12988416) = 0
3735438 munmap(0x7f57b8000000, 54120448) = 0
3735438 mprotect(0x7f57b4000000, 135168, PROT_READ|PROT_WRITE) = 0
3735438 sched_getaffinity(3735438, 32, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]) = 8
3735438 sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
3735438 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f57bb7b5000
3735438 mprotect(0x7f57bb7b5000, 4096, PROT_NONE) = 0
3735438 sigaltstack({ss_sp=0x7f57bb7b6000, ss_flags=0, ss_size=8192}, NULL) = 0
3735438 prctl(PR_SET_NAME, "ctrl-c")    = 0
3735438 read(3,  <unfinished ...>
3735433 <... futex resumed>)            = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
3735433 --- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
3735433 write(4, "\0", 1)               = 1
3735438 <... read resumed>"\0", 1)      = 1
3735433 rt_sigreturn({mask=[]} <unfinished ...>
3735438 futex(0x56415f72c028, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
3735433 <... rt_sigreturn resumed>)     = 202
3735438 <... futex resumed>)            = 0
3735433 futex(0x56415f72c028, FUTEX_WAIT_BITSET_PRIVATE, 4294967295, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...>
3735438 read(3,  <unfinished ...>
3735433 <... futex resumed>)            = -1 EAGAIN (Resource temporarily unavailable)
3735433 write(1, "Got it! Exiting...\n", 19) = 19
3735433 sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
3735433 munmap(0x7f57bb7b8000, 12288)   = 0
3735433 exit_group(0)                   = ?
3735438 <... read resumed> <unfinished ...>) = ?
3735438 +++ exited with 0 +++
3735433 +++ exited with 0 +++

After:

3864619 rt_sigaction(SIGBUS, {sa_handler=0x55d614829ed0, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7f8750f22050}, NULL, 8) = 0
3864619 rt_sigaction(SIGINT, {sa_handler=0x55d6148008c0, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f8750f22050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
3864619 rt_sigaction(SIGRT_1, {sa_handler=0x7f8750f6c720, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f8750f22050}, NULL, 8) = 0
3864619 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
3864619 mmap(NULL, 2101248, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f8750ce2000
3864619 mprotect(0x7f8750ce3000, 2097152, PROT_READ|PROT_WRITE) = 0
3864619 rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0
3864619 clone3({flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, child_tid=0x7f8750ee2990, parent_tid=0x7f8750ee2990, exit_signal=0, stack=0x7f8750ce2000, stack_size=0x1fff00, tls=0x7f8750ee26c0} => {parent_tid=[3864624]}, 88) = 3864624
3864624 rseq(0x7f8750ee2fe0, 0x20, 0, 0x53053053 <unfinished ...>
3864619 rt_sigprocmask(SIG_SETMASK, [],  <unfinished ...>
3864624 <... rseq resumed>)             = 0
3864619 <... rt_sigprocmask resumed>NULL, 8) = 0
3864624 set_robust_list(0x7f8750ee29a0, 24) = 0
3864619 write(1, "Waiting for Ctrl-C...\n", 22 <unfinished ...>
3864624 rt_sigprocmask(SIG_SETMASK, [],  <unfinished ...>
3864619 <... write resumed>)            = 22
3864624 <... rt_sigprocmask resumed>NULL, 8) = 0
3864624 mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0 <unfinished ...>
3864619 futex(0x55d64f9de028, FUTEX_WAIT_BITSET_PRIVATE, 4294967295, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...>
3864624 <... mmap resumed>)             = 0x7f8748ce2000
3864624 munmap(0x7f8748ce2000, 53600256) = 0
3864624 munmap(0x7f8750000000, 13508608) = 0
3864624 mprotect(0x7f874c000000, 135168, PROT_READ|PROT_WRITE) = 0
3864624 sched_getaffinity(3864624, 32, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]) = 8
3864624 sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
3864624 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f87510fa000
3864624 mprotect(0x7f87510fa000, 4096, PROT_NONE) = 0
3864624 sigaltstack({ss_sp=0x7f87510fb000, ss_flags=0, ss_size=8192}, NULL) = 0
3864624 prctl(PR_SET_NAME, "ctrl-c")    = 0
3864624 futex(0x55d614867988, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...>
3864619 <... futex resumed>)            = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
3864619 --- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
3864619 futex(0x55d614867988, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
3864624 <... futex resumed>)            = 0
3864619 <... futex resumed>)            = 1
3864624 futex(0x55d64f9de028, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
3864619 rt_sigreturn({mask=[]} <unfinished ...>
3864624 <... futex resumed>)            = 0
3864624 futex(0x55d614867988, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...>
3864619 <... rt_sigreturn resumed>)     = 202
3864619 futex(0x55d64f9de028, FUTEX_WAIT_BITSET_PRIVATE, 4294967295, NULL, FUTEX_BITSET_MATCH_ANY) = -1 EAGAIN (Resource temporarily unavailable)
3864619 write(1, "Got it! Exiting...\n", 19) = 19
3864619 sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
3864619 munmap(0x7f87510fd000, 12288)   = 0
3864619 exit_group(0)                   = ?
3864624 <... futex resumed>)            = ?
3864624 +++ exited with 0 +++
3864619 +++ exited with 0 +++

@nabijaczleweli nabijaczleweli changed the title Implement unix with sem_*(3) instead of pipes Implement unix with sem_*(3) instead of pipes (dispatch_semaphore_*() on apple where POSIX semaphores are broken-by-design) Jul 9, 2025
@nabijaczleweli nabijaczleweli force-pushed the master branch 2 times, most recently from cbab790 to 77a6080 Compare July 9, 2025 15:41
@nabijaczleweli
Copy link
Contributor Author

nabijaczleweli commented Jul 9, 2025

...and with dispatch_semaphore_*()s on apple since apple refuses to ship POSIX semaphores, still comes out to net -39 :)

@nabijaczleweli
Copy link
Contributor Author

nabijaczleweli commented Jul 9, 2025

(ftr I did originally cook up an even-thinner implementation since you don't need any sync primitives if you sigsetmask+SIGINT in the main thread/SIGSETMASK-SIGINT in the waiter/pause BUT the signal mask must be set in all threads, so it must be set before any non-default threads are spawned; would be nice, but not a viable restriction)

Using pipes works, but is unnecessarily thick
for a one-shot application like this,
and leaves 2 largely-useless pipe fds in the process permanently

sem_post(3) is explicitly allowed to be used in signal handlers,
this is practically the target application of POSIX semaphores

readme_example/glibc before:
  3735433 rt_sigaction(SIGBUS, {sa_handler=0x56412e4350e0, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7f57bb5dd050}, NULL, 8) = 0
  3735433 pipe2([3, 4], O_CLOEXEC)        = 0
  3735433 fcntl(4, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
  3735433 rt_sigaction(SIGINT, {sa_handler=0x56412e40b410, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f57bb5dd050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
  3735433 rt_sigaction(SIGRT_1, {sa_handler=0x7f57bb627720, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f57bb5dd050}, NULL, 8) = 0
  3735433 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
  3735433 mmap(NULL, 2101248, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f57bb39d000
  3735433 mprotect(0x7f57bb39e000, 2097152, PROT_READ|PROT_WRITE) = 0
  3735433 rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0
  3735433 clone3({flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, child_tid=0x7f57bb59d990, parent_tid=0x7f57bb59d990, exit_signal=0, stack=0x7f57bb39d000, stack_size=0x1fff00, tls=0x7f57bb59d6c0} => {parent_tid=[3735438]}, 88) = 3735438
  3735438 rseq(0x7f57bb59dfe0, 0x20, 0, 0x53053053 <unfinished ...>
  3735433 rt_sigprocmask(SIG_SETMASK, [],  <unfinished ...>
  3735438 <... rseq resumed>)             = 0
  3735433 <... rt_sigprocmask resumed>NULL, 8) = 0
  3735438 set_robust_list(0x7f57bb59d9a0, 24) = 0
  3735433 write(1, "Waiting for Ctrl-C...\n", 22 <unfinished ...>
  3735438 rt_sigprocmask(SIG_SETMASK, [],  <unfinished ...>
  3735433 <... write resumed>)            = 22
  3735438 <... rt_sigprocmask resumed>NULL, 8) = 0
  3735438 mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0 <unfinished ...>
  3735433 futex(0x56415f72c028, FUTEX_WAIT_BITSET_PRIVATE, 4294967295, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...>
  3735438 <... mmap resumed>)             = 0x7f57b339d000
  3735438 munmap(0x7f57b339d000, 12988416) = 0
  3735438 munmap(0x7f57b8000000, 54120448) = 0
  3735438 mprotect(0x7f57b4000000, 135168, PROT_READ|PROT_WRITE) = 0
  3735438 sched_getaffinity(3735438, 32, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]) = 8
  3735438 sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
  3735438 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f57bb7b5000
  3735438 mprotect(0x7f57bb7b5000, 4096, PROT_NONE) = 0
  3735438 sigaltstack({ss_sp=0x7f57bb7b6000, ss_flags=0, ss_size=8192}, NULL) = 0
  3735438 prctl(PR_SET_NAME, "ctrl-c")    = 0
  3735438 read(3,  <unfinished ...>
  3735433 <... futex resumed>)            = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
  3735433 --- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
  3735433 write(4, "\0", 1)               = 1
  3735438 <... read resumed>"\0", 1)      = 1
  3735433 rt_sigreturn({mask=[]} <unfinished ...>
  3735438 futex(0x56415f72c028, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
  3735433 <... rt_sigreturn resumed>)     = 202
  3735438 <... futex resumed>)            = 0
  3735433 futex(0x56415f72c028, FUTEX_WAIT_BITSET_PRIVATE, 4294967295, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...>
  3735438 read(3,  <unfinished ...>
  3735433 <... futex resumed>)            = -1 EAGAIN (Resource temporarily unavailable)
  3735433 write(1, "Got it! Exiting...\n", 19) = 19
  3735433 sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
  3735433 munmap(0x7f57bb7b8000, 12288)   = 0
  3735433 exit_group(0)                   = ?
  3735438 <... read resumed> <unfinished ...>) = ?
  3735438 +++ exited with 0 +++
  3735433 +++ exited with 0 +++

After:
  3864619 rt_sigaction(SIGBUS, {sa_handler=0x55d614829ed0, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7f8750f22050}, NULL, 8) = 0
  3864619 rt_sigaction(SIGINT, {sa_handler=0x55d6148008c0, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f8750f22050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
  3864619 rt_sigaction(SIGRT_1, {sa_handler=0x7f8750f6c720, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f8750f22050}, NULL, 8) = 0
  3864619 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
  3864619 mmap(NULL, 2101248, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f8750ce2000
  3864619 mprotect(0x7f8750ce3000, 2097152, PROT_READ|PROT_WRITE) = 0
  3864619 rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0
  3864619 clone3({flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, child_tid=0x7f8750ee2990, parent_tid=0x7f8750ee2990, exit_signal=0, stack=0x7f8750ce2000, stack_size=0x1fff00, tls=0x7f8750ee26c0} => {parent_tid=[3864624]}, 88) = 3864624
  3864624 rseq(0x7f8750ee2fe0, 0x20, 0, 0x53053053 <unfinished ...>
  3864619 rt_sigprocmask(SIG_SETMASK, [],  <unfinished ...>
  3864624 <... rseq resumed>)             = 0
  3864619 <... rt_sigprocmask resumed>NULL, 8) = 0
  3864624 set_robust_list(0x7f8750ee29a0, 24) = 0
  3864619 write(1, "Waiting for Ctrl-C...\n", 22 <unfinished ...>
  3864624 rt_sigprocmask(SIG_SETMASK, [],  <unfinished ...>
  3864619 <... write resumed>)            = 22
  3864624 <... rt_sigprocmask resumed>NULL, 8) = 0
  3864624 mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0 <unfinished ...>
  3864619 futex(0x55d64f9de028, FUTEX_WAIT_BITSET_PRIVATE, 4294967295, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...>
  3864624 <... mmap resumed>)             = 0x7f8748ce2000
  3864624 munmap(0x7f8748ce2000, 53600256) = 0
  3864624 munmap(0x7f8750000000, 13508608) = 0
  3864624 mprotect(0x7f874c000000, 135168, PROT_READ|PROT_WRITE) = 0
  3864624 sched_getaffinity(3864624, 32, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]) = 8
  3864624 sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
  3864624 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f87510fa000
  3864624 mprotect(0x7f87510fa000, 4096, PROT_NONE) = 0
  3864624 sigaltstack({ss_sp=0x7f87510fb000, ss_flags=0, ss_size=8192}, NULL) = 0
  3864624 prctl(PR_SET_NAME, "ctrl-c")    = 0
  3864624 futex(0x55d614867988, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...>
  3864619 <... futex resumed>)            = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
  3864619 --- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
  3864619 futex(0x55d614867988, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
  3864624 <... futex resumed>)            = 0
  3864619 <... futex resumed>)            = 1
  3864624 futex(0x55d64f9de028, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
  3864619 rt_sigreturn({mask=[]} <unfinished ...>
  3864624 <... futex resumed>)            = 0
  3864624 futex(0x55d614867988, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, FUTEX_BITSET_MATCH_ANY <unfinished ...>
  3864619 <... rt_sigreturn resumed>)     = 202
  3864619 futex(0x55d64f9de028, FUTEX_WAIT_BITSET_PRIVATE, 4294967295, NULL, FUTEX_BITSET_MATCH_ANY) = -1 EAGAIN (Resource temporarily unavailable)
  3864619 write(1, "Got it! Exiting...\n", 19) = 19
  3864619 sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
  3864619 munmap(0x7f87510fd000, 12288)   = 0
  3864619 exit_group(0)                   = ?
  3864624 <... futex resumed>)            = ?
  3864624 +++ exited with 0 +++
  3864619 +++ exited with 0 +++
@Detegr
Copy link
Owner

Detegr commented Sep 6, 2025

Sorry for missing this PR. I like this, I never liked using pipes anyway.

@Detegr Detegr merged commit 1dfec09 into Detegr:master Sep 6, 2025
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants