io-event 1.10.0 → 1.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/agent.md +47 -0
- data/ext/extconf.rb +17 -6
- data/ext/io/event/event.c +5 -1
- data/ext/io/event/event.h +4 -0
- data/ext/io/event/fiber.c +1 -1
- data/ext/io/event/selector/epoll.c +4 -0
- data/ext/io/event/selector/kqueue.c +4 -0
- data/ext/io/event/selector/selector.c +1 -6
- data/ext/io/event/selector/uring.c +25 -4
- data/ext/io/event/worker_pool.c +477 -0
- data/ext/io/event/worker_pool.h +8 -0
- data/ext/io/event/worker_pool_test.c +199 -0
- data/ext/io/event/worker_pool_test.h +9 -0
- data/lib/io/event/debug/selector.rb +2 -2
- data/lib/io/event/priority_heap.rb +1 -1
- data/lib/io/event/selector/select.rb +36 -105
- data/lib/io/event/support.rb +11 -18
- data/lib/io/event/timers.rb +1 -2
- data/lib/io/event/version.rb +1 -1
- data/license.md +2 -1
- data/readme.md +16 -0
- data/releases.md +38 -0
- data.tar.gz.sig +0 -0
- metadata +11 -5
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15970f96a2af0de9aa23889ee6ff50217b53216334de39ad5b4692ebf945942b
|
4
|
+
data.tar.gz: 5f44f0dbcdf09106e342e3fda71f56f8a0208dc5a435e82f8c80b1fdb8826fae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b72815ede642358071d66b9720935603493b6a09a34fcf2eec6930c8648a7d2b6498358f630554f40c6537f2bd0fb3cb6b58334ad58dfbaacc5d6c1f0e407cf
|
7
|
+
data.tar.gz: a5eb4e8396cd510aa71e8ee45f8ebf60829ca707a96bded1104ce0c3f74c47e9343225d826c58e4ec7bcfed6aed81051d7c14a10f304c42fa61fe930e1099fef
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/agent.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Agent
|
2
|
+
|
3
|
+
## Context
|
4
|
+
|
5
|
+
This section provides links to documentation from installed packages. It is automatically generated and may be updated by running `bake agent:context:install`.
|
6
|
+
|
7
|
+
**Important:** Before performing any code, documentation, or analysis tasks, always read and apply the full content of any relevant documentation referenced in the following sections. These context files contain authoritative standards and best practices for documentation, code style, and project-specific workflows. **Do not proceed with any actions until you have read and incorporated the guidance from relevant context files.**
|
8
|
+
|
9
|
+
### agent-context
|
10
|
+
|
11
|
+
Install and manage context files from Ruby gems.
|
12
|
+
|
13
|
+
#### [Usage Guide](.context/agent-context/usage.md)
|
14
|
+
|
15
|
+
`agent-context` is a tool that helps you discover and install contextual information from Ruby gems for AI agents. Gems can provide additional documentation, examples, and guidance in a `context/` ...
|
16
|
+
|
17
|
+
### decode
|
18
|
+
|
19
|
+
Code analysis for documentation generation.
|
20
|
+
|
21
|
+
#### [Getting Started with Decode](.context/decode/getting-started.md)
|
22
|
+
|
23
|
+
The Decode gem provides programmatic access to Ruby code structure and metadata. It can parse Ruby files and extract definitions, comments, and documentation pragmas, enabling code analysis, docume...
|
24
|
+
|
25
|
+
#### [Documentation Coverage](.context/decode/coverage.md)
|
26
|
+
|
27
|
+
This guide explains how to test and monitor documentation coverage in your Ruby projects using the Decode gem's built-in bake tasks.
|
28
|
+
|
29
|
+
#### [Ruby Documentation](.context/decode/ruby-documentation.md)
|
30
|
+
|
31
|
+
This guide covers documentation practices and pragmas supported by the Decode gem for documenting Ruby code. These pragmas provide structured documentation that can be parsed and used to generate A...
|
32
|
+
|
33
|
+
### sus
|
34
|
+
|
35
|
+
A fast and scalable test runner.
|
36
|
+
|
37
|
+
#### [Using Sus Testing Framework](.context/sus/usage.md)
|
38
|
+
|
39
|
+
Sus is a modern Ruby testing framework that provides a clean, BDD-style syntax for writing tests. It's designed to be fast, simple, and expressive.
|
40
|
+
|
41
|
+
#### [Mocking](.context/sus/mocking.md)
|
42
|
+
|
43
|
+
There are two types of mocking in sus: `receive` and `mock`. The `receive` matcher is a subset of full mocking and is used to set expectations on method calls, while `mock` can be used to replace m...
|
44
|
+
|
45
|
+
#### [Shared Test Behaviors and Fixtures](.context/sus/shared.md)
|
46
|
+
|
47
|
+
Sus provides shared test contexts which can be used to define common behaviours or tests that can be reused across one or more test files.
|
data/ext/extconf.rb
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
# Released under the MIT License.
|
5
5
|
# Copyright, 2021-2025, by Samuel Williams.
|
6
6
|
# Copyright, 2023, by Math Ieu.
|
7
|
+
# Copyright, 2025, by Stanislav (Stas) Katkov.
|
7
8
|
|
8
9
|
return if RUBY_DESCRIPTION =~ /jruby/
|
9
10
|
|
@@ -14,12 +15,12 @@ extension_name = "IO_Event"
|
|
14
15
|
|
15
16
|
# dir_config(extension_name)
|
16
17
|
|
17
|
-
|
18
|
+
append_cflags(["-Wall", "-Wno-unknown-pragmas", "-std=c99"])
|
18
19
|
|
19
20
|
if ENV.key?("RUBY_DEBUG")
|
20
21
|
$stderr.puts "Enabling debug mode..."
|
21
|
-
|
22
|
-
|
22
|
+
|
23
|
+
append_cflags(["-DRUBY_DEBUG", "-O0"])
|
23
24
|
end
|
24
25
|
|
25
26
|
$srcs = ["io/event/event.c", "io/event/time.c", "io/event/fiber.c", "io/event/selector/selector.c"]
|
@@ -32,7 +33,7 @@ have_func("&rb_fiber_transfer")
|
|
32
33
|
if have_library("uring") and have_header("liburing.h")
|
33
34
|
# We might want to consider using this in the future:
|
34
35
|
# have_func("io_uring_submit_and_wait_timeout", "liburing.h")
|
35
|
-
|
36
|
+
|
36
37
|
$srcs << "io/event/selector/uring.c"
|
37
38
|
end
|
38
39
|
|
@@ -57,11 +58,21 @@ have_func("epoll_pwait2")
|
|
57
58
|
|
58
59
|
have_header("ruby/io/buffer.h")
|
59
60
|
|
61
|
+
# Feature detection for blocking operation support
|
62
|
+
if have_func("rb_fiber_scheduler_blocking_operation_extract")
|
63
|
+
# Feature detection for pthread support (needed for WorkerPool)
|
64
|
+
if have_header("pthread.h")
|
65
|
+
append_cflags(["-DHAVE_IO_EVENT_WORKER_POOL"])
|
66
|
+
$srcs << "io/event/worker_pool.c"
|
67
|
+
$srcs << "io/event/worker_pool_test.c"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
60
71
|
if ENV.key?("RUBY_SANITIZE")
|
61
72
|
$stderr.puts "Enabling sanitizers..."
|
62
|
-
|
73
|
+
|
63
74
|
# Add address and undefined behaviour sanitizers:
|
64
|
-
|
75
|
+
append_cflags(["-fsanitize=address", "-fsanitize=undefined", "-fno-omit-frame-pointer"])
|
65
76
|
$LDFLAGS << " -fsanitize=address -fsanitize=undefined"
|
66
77
|
end
|
67
78
|
|
data/ext/io/event/event.c
CHANGED
@@ -14,7 +14,11 @@ void Init_IO_Event(void)
|
|
14
14
|
VALUE IO_Event = rb_define_module_under(rb_cIO, "Event");
|
15
15
|
|
16
16
|
Init_IO_Event_Fiber(IO_Event);
|
17
|
-
|
17
|
+
|
18
|
+
#ifdef HAVE_IO_EVENT_WORKER_POOL
|
19
|
+
Init_IO_Event_WorkerPool(IO_Event);
|
20
|
+
#endif
|
21
|
+
|
18
22
|
VALUE IO_Event_Selector = rb_define_module_under(IO_Event, "Selector");
|
19
23
|
Init_IO_Event_Selector(IO_Event_Selector);
|
20
24
|
|
data/ext/io/event/event.h
CHANGED
data/ext/io/event/fiber.c
CHANGED
@@ -35,7 +35,7 @@ VALUE IO_Event_Fiber_raise(VALUE fiber, int argc, VALUE *argv) {
|
|
35
35
|
#ifndef HAVE_RB_FIBER_CURRENT
|
36
36
|
static ID id_current;
|
37
37
|
|
38
|
-
|
38
|
+
VALUE IO_Event_Fiber_current(void) {
|
39
39
|
return rb_funcall(rb_cFiber, id_current, 0);
|
40
40
|
}
|
41
41
|
#endif
|
@@ -38,6 +38,10 @@ struct IO_Event_Selector_EPoll
|
|
38
38
|
{
|
39
39
|
struct IO_Event_Selector backend;
|
40
40
|
int descriptor;
|
41
|
+
|
42
|
+
// Flag indicating whether the selector is currently blocked in a system call.
|
43
|
+
// Set to 1 when blocked in epoll_wait() without GVL, 0 otherwise.
|
44
|
+
// Used by wakeup() to determine if an interrupt signal is needed.
|
41
45
|
int blocked;
|
42
46
|
|
43
47
|
struct timespec idle_duration;
|
@@ -47,6 +47,10 @@ struct IO_Event_Selector_KQueue
|
|
47
47
|
{
|
48
48
|
struct IO_Event_Selector backend;
|
49
49
|
int descriptor;
|
50
|
+
|
51
|
+
// Flag indicating whether the selector is currently blocked in a system call.
|
52
|
+
// Set to 1 when blocked in kevent() without GVL, 0 otherwise.
|
53
|
+
// Used by wakeup() to determine if an interrupt signal is needed.
|
50
54
|
int blocked;
|
51
55
|
|
52
56
|
struct timespec idle_duration;
|
@@ -29,10 +29,7 @@ VALUE IO_Event_Selector_process_status_wait(rb_pid_t pid, int flags)
|
|
29
29
|
int IO_Event_Selector_nonblock_set(int file_descriptor)
|
30
30
|
{
|
31
31
|
#ifdef _WIN32
|
32
|
-
|
33
|
-
ioctlsocket(file_descriptor, FIONBIO, &nonblock);
|
34
|
-
// Windows does not provide any way to know this, so we always restore it back to unset:
|
35
|
-
return 0;
|
32
|
+
rb_w32_set_nonblock(file_descriptor);
|
36
33
|
#else
|
37
34
|
// Get the current mode:
|
38
35
|
int flags = fcntl(file_descriptor, F_GETFL, 0);
|
@@ -50,8 +47,6 @@ void IO_Event_Selector_nonblock_restore(int file_descriptor, int flags)
|
|
50
47
|
{
|
51
48
|
#ifdef _WIN32
|
52
49
|
// Yolo...
|
53
|
-
u_long nonblock = flags;
|
54
|
-
ioctlsocket(file_descriptor, FIONBIO, &nonblock);
|
55
50
|
#else
|
56
51
|
// The flags didn't have O_NONBLOCK set, so it would have been set, so we need to restore it:
|
57
52
|
if (!(flags & O_NONBLOCK)) {
|
@@ -18,7 +18,7 @@
|
|
18
18
|
enum {
|
19
19
|
DEBUG = 0,
|
20
20
|
DEBUG_COMPLETION = 0,
|
21
|
-
|
21
|
+
DEBUG_CQE = 0,
|
22
22
|
};
|
23
23
|
|
24
24
|
enum {URING_ENTRIES = 64};
|
@@ -30,6 +30,10 @@ struct IO_Event_Selector_URing
|
|
30
30
|
struct IO_Event_Selector backend;
|
31
31
|
struct io_uring ring;
|
32
32
|
size_t pending;
|
33
|
+
|
34
|
+
// Flag indicating whether the selector is currently blocked in a system call.
|
35
|
+
// Set to 1 when blocked in io_uring_wait_cqe_timeout() without GVL, 0 otherwise.
|
36
|
+
// Used by wakeup() to determine if an interrupt signal is needed.
|
33
37
|
int blocked;
|
34
38
|
|
35
39
|
struct timespec idle_duration;
|
@@ -552,7 +556,10 @@ VALUE io_wait_transfer(VALUE _arguments) {
|
|
552
556
|
|
553
557
|
if (DEBUG) fprintf(stderr, "io_wait_transfer:waiting=%p, result=%d\n", (void*)arguments->waiting, arguments->waiting->result);
|
554
558
|
|
555
|
-
|
559
|
+
int32_t result = arguments->waiting->result;
|
560
|
+
if (result < 0) {
|
561
|
+
rb_syserr_fail(-result, "io_wait_transfer:io_uring_poll_add");
|
562
|
+
} else if (result > 0) {
|
556
563
|
// We explicitly filter the resulting events based on the requested events.
|
557
564
|
// In some cases, poll will report events we didn't ask for.
|
558
565
|
return RB_INT2NUM(events_from_poll_flags(arguments->waiting->result & arguments->flags));
|
@@ -704,6 +711,20 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
|
|
704
711
|
off_t from = io_seekable(descriptor);
|
705
712
|
|
706
713
|
size_t maximum_size = size - offset;
|
714
|
+
|
715
|
+
// Are we performing a non-blocking read?
|
716
|
+
if (!length) {
|
717
|
+
// If the (maximum) length is zero, that indicates we just want to read whatever is available without blocking.
|
718
|
+
// If we schedule this read into the URing, it will block until data is available, rather than returning immediately.
|
719
|
+
int state = IO_Event_Selector_nonblock_set(descriptor);
|
720
|
+
|
721
|
+
int result = read(descriptor, (char*)base+offset, maximum_size);
|
722
|
+
int error = errno;
|
723
|
+
|
724
|
+
IO_Event_Selector_nonblock_restore(descriptor, state);
|
725
|
+
return rb_fiber_scheduler_io_result(result, error);
|
726
|
+
}
|
727
|
+
|
707
728
|
while (maximum_size) {
|
708
729
|
int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
709
730
|
|
@@ -1059,7 +1080,7 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) {
|
|
1059
1080
|
}
|
1060
1081
|
|
1061
1082
|
io_uring_for_each_cqe(ring, head, cqe) {
|
1062
|
-
if (
|
1083
|
+
if (DEBUG_CQE) fprintf(stderr, "select_process_completions: cqe res=%d user_data=%p\n", cqe->res, (void*)cqe->user_data);
|
1063
1084
|
|
1064
1085
|
++completed;
|
1065
1086
|
|
@@ -1090,7 +1111,7 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) {
|
|
1090
1111
|
}
|
1091
1112
|
}
|
1092
1113
|
|
1093
|
-
if (DEBUG && completed > 0) fprintf(stderr, "select_process_completions
|
1114
|
+
if (DEBUG && completed > 0) fprintf(stderr, "select_process_completions: completed=%d\n", completed);
|
1094
1115
|
|
1095
1116
|
return completed;
|
1096
1117
|
}
|