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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec418d3289f8648ac13d7808bbe8d56b50a3b4cf518f61da116c56c3c345daa9
4
- data.tar.gz: 8de6423981f2bfb2da54e0cd99e493787253a2c2c83bc644f72fffdcf52a295c
3
+ metadata.gz: 15970f96a2af0de9aa23889ee6ff50217b53216334de39ad5b4692ebf945942b
4
+ data.tar.gz: 5f44f0dbcdf09106e342e3fda71f56f8a0208dc5a435e82f8c80b1fdb8826fae
5
5
  SHA512:
6
- metadata.gz: 9fd70cf075b1703fd8e4659fa2adaf7381f7d5523e47e8700eeda233a01679c671ebb8197fd06f525da17b7a30332e996dd087874503f8ff7a594b8f88268554
7
- data.tar.gz: c42e23c74e1069fb199fd1044b7fc487b8f1587294703a19362242bbbfa23c21fbba36a6a114fafb978ddc4cfcd2a21d2a6c7b7c3d3dc1c4a08094ca9b51bd2a
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
- $CFLAGS << " -Wall -Wno-unknown-pragmas -std=c99"
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
- $CFLAGS << " -DRUBY_DEBUG -O0"
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
- $CFLAGS << " -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer"
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
@@ -18,3 +18,7 @@ void Init_IO_Event(void);
18
18
  #ifdef HAVE_SYS_EVENT_H
19
19
  #include "selector/kqueue.h"
20
20
  #endif
21
+
22
+ #ifdef HAVE_IO_EVENT_WORKER_POOL
23
+ #include "worker_pool.h"
24
+ #endif
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
- static VALUE IO_Event_Fiber_current(void) {
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
- u_long nonblock = 1;
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
- DEBUG_IO_READ = 1,
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
- if (arguments->waiting->result) {
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 (DEBUG) fprintf(stderr, "select_process_completions: cqe res=%d user_data=%p\n", cqe->res, (void*)cqe->user_data);
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(completed=%d)\n", completed);
1114
+ if (DEBUG && completed > 0) fprintf(stderr, "select_process_completions: completed=%d\n", completed);
1094
1115
 
1095
1116
  return completed;
1096
1117
  }