uringmachine 1.0.1 → 1.0.4

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: 6c4607791ec8b3aefe794167dc5a0ec500fe4ec4a58b56962d90d27fdd4c41a7
4
- data.tar.gz: 151205d0887aef4cc0cf022baf295b9be8934f2bb895fd87bd98680a837349a0
3
+ metadata.gz: dea49e5d6f13c7919ccaa9765428bfc81058ad1fab0b3f1df07d0fd60a5b1cd3
4
+ data.tar.gz: 3cd06ca52b40e891807b589bc7d7f97cfc67be2d491e5855a9624de959f160a1
5
5
  SHA512:
6
- metadata.gz: a4f580578fabf57dbc7c9c8d609a000b4b2ebc56ffb1273e3496137b7ba01f4ca71767e37d84d978dbf49b5012a211374cceb774b787f748c4b3351531a42c42
7
- data.tar.gz: 0cc8a8d93345894b2f2b3570390ae829dea4bac1e975a0d777c7359e74161273df9658b0797bd2ecbe45a262efbd7588dd8f9ec673577d91e7e8030bd9ab15e4
6
+ metadata.gz: 06ef57453cc0986a0b047b9d91ab5add06cf64145d5fd17ba5d85edf1b01f1bde0cea060d3a54bc87cadc9fc4c0137350ad704b0f0c1421f51c9ef9158eed8c5
7
+ data.tar.gz: 65a8701e871902c7e726cdaebacebef4bde5c111b6a3393b92d40a9b5a3005535f240cb30574f34fa41048206430b8faa41203d23a2fab603285c88eb9e766b4
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ # 1.0.4 2026-08-24
2
+
3
+ - Add `IO#read_uint_be` method
4
+
5
+ # 1.0.3 2026-08-24
6
+
7
+ - Add `IO#read_int_be` method
8
+ - Don't schedule fiber for async multishot ops (fixes potential segfault when using UM::IO)
9
+
10
+ # 1.0.2 2026-05-25
11
+
12
+ - Fix DNS resolver
13
+
1
14
  # 1.0.1 2026-05-12
2
15
 
3
16
  - Fix Net::HTTP performance regression when running on fiber scheduler
data/TODO.md CHANGED
@@ -1,5 +1,43 @@
1
1
  ## immediate
2
2
 
3
+ - IO methods:
4
+ ```ruby
5
+ io.read_int_be(length)
6
+ io.read_uint_be(length)
7
+ io.read_int_le(length)
8
+ io.read_uint_le(length)
9
+ ```
10
+
11
+ - It might be interesting to have a DSL where we can define a frame/header structure:
12
+
13
+ ```ruby
14
+ http2_transform = UM::IO.transform do
15
+ length uint(24)
16
+ type uint(8)
17
+ flags uint(8)
18
+ stream_id uint(32)
19
+ end
20
+
21
+ frame = io.read(http2_transform)
22
+ raise if frame[:length] > MAX_LENGTH
23
+ raise if stream_id & 0x80000000
24
+ ```
25
+
26
+ - SSL kTLS:
27
+ - discussion: https://share.gemini.google/tPWiFpr3bWeF
28
+ - API:
29
+
30
+ ```ruby
31
+ machine.ssl_setup_ktls(fd)
32
+
33
+ # then we can just use normal send/recv
34
+ machine.send(fd, "foo!")
35
+ ```
36
+
37
+ - SSL custom BIO:
38
+ - release GVL before calling `SSL_read`, `SSL_write`
39
+ - reacquire GVL (`rb_thread_call_with_gvl`) in `um_bio_read`, `um_bio_write`
40
+
3
41
  - Add `IO#http_xxx` methods
4
42
  - `#http_read_request_headers()`
5
43
  - `#http_read_body(content_length)` (-1 means chunked TE)
@@ -84,21 +122,6 @@
84
122
  - madvise
85
123
  - getxattr / setxattr
86
124
 
87
- ## actors
88
-
89
- When doing a `call`, we need to provide a mailbox for the response. can this be
90
- automatic?
91
-
92
- ## Syntax / pattern for launching/supervising multiple operations
93
-
94
- Select (see above):
95
-
96
- ```ruby
97
- # select
98
- machine.join_select(*fibers) #=> [result, fiber]
99
- machine.shift_select(*queues) #=> [result, queue]
100
- ```
101
-
102
125
  ## Other abstractions
103
126
 
104
127
  - Happy eyeballs connect
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ require 'uringmachine'
6
+ require 'uringmachine/fiber_scheduler'
7
+ require 'irb'
8
+
9
+ @machine = UringMachine.new
10
+ @scheduler = UM::FiberScheduler.new(@machine)
11
+ Fiber.set_scheduler @scheduler
12
+
13
+ f = @machine.spin { IRB.start }
14
+ @machine.join(f)
data/ext/um/um.c CHANGED
@@ -192,7 +192,7 @@ static inline void um_process_cqe(struct um *machine, struct io_uring_cqe *cqe)
192
192
  um_op_multishot_results_push(machine, op, cqe->res, cqe->flags);
193
193
 
194
194
  op->flags |= done ? (OP_F_CQE_SEEN | OP_F_CQE_DONE) : OP_F_CQE_SEEN;
195
- if (!OP_SCHEDULED_P(op)) um_schedule_op(machine, op);
195
+ if (!(op->flags & (OP_F_SCHEDULED | OP_F_ASYNC))) um_schedule_op(machine, op);
196
196
  }
197
197
  else {
198
198
  // single shot
data/ext/um/um.h CHANGED
@@ -426,6 +426,8 @@ VALUE io_read(struct um_io *io, VALUE out_buffer, ssize_t len, size_t inc, int s
426
426
  VALUE io_read_to_delim(struct um_io *io, VALUE out_buffer, VALUE delim, ssize_t maxlen);
427
427
  void io_skip(struct um_io *io, size_t inc, int safe_inc);
428
428
  void io_read_each(struct um_io *io);
429
+ VALUE io_read_int_be(struct um_io *io, size_t len);
430
+ VALUE io_read_uint_be(struct um_io *io, size_t len);
429
431
  size_t io_write_raw(struct um_io *io, const char *buffer, size_t len);
430
432
  VALUE io_writev(struct um_io *io, int argc, VALUE *argv);
431
433
  VALUE resp_read(struct um_io *io, VALUE out_buffer);
data/ext/um/um_io.c CHANGED
@@ -74,13 +74,12 @@ void um_io_cleanup(struct um_io *io) {
74
74
  um_segment_checkin(io->machine, io->head);
75
75
  io->head = next;
76
76
  }
77
+ io->tail = NULL;
77
78
  io->pending_bytes = 0;
78
79
  }
79
80
 
80
81
  // returns true if case of ENOBUFS error, sets more to true if more data forthcoming
81
- inline int io_process_segments(
82
- struct um_io *io, size_t *total_bytes, int *more) {
83
-
82
+ inline int io_process_segments(struct um_io *io, size_t *total_bytes, int *more) {
84
83
  *more = 0;
85
84
  struct um_op_result *result = &io->op->result;
86
85
  io->op->flags &= ~OP_F_CQE_SEEN;
@@ -125,6 +124,7 @@ void io_clear(struct um_io *io) {
125
124
  um_segment_checkin(io->machine, io->head);
126
125
  io->head = next;
127
126
  }
127
+ io->tail = NULL;
128
128
  io->pending_bytes = 0;
129
129
 
130
130
  if (io->working_buffer) {
@@ -133,6 +133,30 @@ void io_clear(struct um_io *io) {
133
133
  }
134
134
  }
135
135
 
136
+ inline void io_handle_enobufs(struct um_io *io) {
137
+ // we want to restart only if in case we don't already have lots of pending
138
+ // bytes in the stream. This heuristic might need a closer look to determine
139
+ // if this is the correct behavior.
140
+ int should_restart = io->pending_bytes < (io->machine->bp_buffer_size * 4);
141
+
142
+ // fprintf(stderr, "%p enobufs total: %ld pending: %ld threshold: %ld bc: %d (same: %d, restart: %d)\n",
143
+ // io,
144
+ // total_bytes, io->pending_bytes, io->machine->bp_commit_level,
145
+ // io->machine->bp_buffer_count,
146
+ // same_threshold, should_restart
147
+ // );
148
+
149
+ if (should_restart) {
150
+ // If multiple IO ops are happening at the same time, they'll all get
151
+ // ENOBUFS! We track the buffer pool commit level in the op in order to
152
+ // prevent running bp_handle_enobufs() more than once.
153
+ if (io->op->bp_commit_level == io->machine->bp_commit_level)
154
+ bp_handle_enobufs(io->machine);
155
+ }
156
+ um_op_release(io->machine, io->op);
157
+ io->op = NULL;
158
+ }
159
+
136
160
  inline void io_await_segments(struct um_io *io) {
137
161
  if (unlikely(!io->op)) io_multishot_op_start(io);
138
162
 
@@ -156,40 +180,12 @@ int io_get_more_segments_bp(struct um_io *io) {
156
180
  io_await_segments(io);
157
181
  enobufs = io_process_segments(io, &total_bytes, &more);
158
182
  um_op_multishot_results_clear(io->machine, io->op);
159
- if (unlikely(enobufs)) {
160
- int should_restart = io->pending_bytes < (io->machine->bp_buffer_size * 4);
161
- // int same_threshold = io->op->bp_commit_level == io->machine->bp_commit_level;
162
-
163
- // fprintf(stderr, "%p enobufs total: %ld pending: %ld threshold: %ld bc: %d (same: %d, restart: %d)\n",
164
- // io,
165
- // total_bytes, io->pending_bytes, io->machine->bp_commit_level,
166
- // io->machine->bp_buffer_count,
167
- // same_threshold, should_restart
168
- // );
169
-
170
- // If multiple IO ops are happening at the same time, they'll all
171
- // get ENOBUFS! We track the commit threshold in the op in order to
172
- // prevent running bp_handle_enobufs() more than once.
173
-
174
- if (should_restart) {
175
- if (io->op->bp_commit_level == io->machine->bp_commit_level)
176
- bp_handle_enobufs(io->machine);
177
-
178
- um_op_release(io->machine, io->op);
179
- io->op = NULL;
180
- }
181
- else {
182
- um_op_release(io->machine, io->op);
183
- io->op = NULL;
184
- }
185
183
 
186
- if (total_bytes) return total_bytes;
187
- }
188
- else {
189
- if (more)
190
- io->op->flags &= ~OP_F_CQE_SEEN;
191
- if (total_bytes || io->eof) return total_bytes;
192
- }
184
+ if (unlikely(enobufs))
185
+ io_handle_enobufs(io);
186
+ else if (more)
187
+ io->op->flags &= ~OP_F_CQE_SEEN;
188
+ if (total_bytes || io->eof) return total_bytes;
193
189
  }
194
190
  }
195
191
 
@@ -254,9 +250,7 @@ inline void io_skip(struct um_io *io, size_t inc, int safe_inc) {
254
250
  io->pending_bytes -= inc_len;
255
251
  if (io->pos == io->head->len) {
256
252
  io_shift_head(io);
257
- if (inc && safe_inc && !io->head) {
258
- if (!io_get_more_segments(io)) break;
259
- }
253
+ if (inc && safe_inc && !io->head && !io_get_more_segments(io)) break;
260
254
  }
261
255
  }
262
256
  }
@@ -285,6 +279,58 @@ inline void io_read_each(struct um_io *io) {
285
279
  RB_GC_GUARD(buffer);
286
280
  }
287
281
 
282
+ VALUE io_read_int_be(struct um_io *io, size_t len) {
283
+ if (unlikely(io->eof && !io->head)) return Qnil;
284
+ if (!io->tail && !io_get_more_segments(io)) return Qnil;
285
+
286
+ long value = 0;
287
+ struct um_segment *current = io->head;
288
+
289
+ while (true) {
290
+ size_t segment_len = current->len - io->pos;
291
+ int pos = 0;
292
+ while (segment_len > 0 && len > 0) {
293
+ value = value * 0x100 + *(unsigned char *)(current->ptr + io->pos + pos);
294
+ len--;
295
+ segment_len--;
296
+ pos++;
297
+ }
298
+ if (pos > 0) io_skip(io, pos, true);
299
+
300
+ if (len == 0) return LONG2NUM(value);
301
+
302
+ if (!io->head && !io_get_more_segments(io))
303
+ return Qnil;
304
+ current = io->head;
305
+ }
306
+ }
307
+
308
+ VALUE io_read_uint_be(struct um_io *io, size_t len) {
309
+ if (unlikely(io->eof && !io->head)) return Qnil;
310
+ if (!io->tail && !io_get_more_segments(io)) return Qnil;
311
+
312
+ unsigned long value = 0;
313
+ struct um_segment *current = io->head;
314
+
315
+ while (true) {
316
+ size_t segment_len = current->len - io->pos;
317
+ int pos = 0;
318
+ while (segment_len > 0 && len > 0) {
319
+ value = value * 0x100 + *(unsigned char *)(current->ptr + io->pos + pos);
320
+ len--;
321
+ segment_len--;
322
+ pos++;
323
+ }
324
+ if (pos > 0) io_skip(io, pos, true);
325
+
326
+ if (len == 0) return ULONG2NUM(value);
327
+
328
+ if (!io->head && !io_get_more_segments(io))
329
+ return Qnil;
330
+ current = io->head;
331
+ }
332
+ }
333
+
288
334
  inline void io_copy(struct um_io *io, char *dest, size_t len) {
289
335
  while (len) {
290
336
  char *segment_ptr = io->head->ptr + io->pos;
@@ -316,7 +362,7 @@ VALUE io_consume_string(struct um_io *io, VALUE out_buffer, size_t len, size_t i
316
362
  char *dest = RSTRING_PTR(str);
317
363
 
318
364
  io_copy(io, dest, len);
319
- io_skip(io, inc, safe_inc);
365
+ if (inc) io_skip(io, inc, safe_inc);
320
366
  return str;
321
367
  RB_GC_GUARD(str);
322
368
  }
data/ext/um/um_io_class.c CHANGED
@@ -242,6 +242,42 @@ VALUE IO_read_each(VALUE self) {
242
242
  return self;
243
243
  }
244
244
 
245
+ #define MAX_INT_SIZE 8
246
+
247
+ /* call-seq:
248
+ * conn.read_int_be(len) -> int
249
+ *
250
+ * Reads a signed integer from the target in big endian notation. The len
251
+ * parameter indicates the integer field length in bytes, with an upper limit of
252
+ * 8 bytes. If EOF is encountered before enough bytes are read, returns nil.
253
+ *
254
+ * @param len [Integer] number of bytes to read
255
+ * @return [Integer, nil] integer value or nil if EOF
256
+ */
257
+ VALUE IO_read_int_be(VALUE self, VALUE len) {
258
+ struct um_io *conn = um_get_io(self);
259
+ size_t len_i = NUM2UINT(len);
260
+ if (unlikely(len_i > MAX_INT_SIZE)) rb_raise(eUMError, "Invalid length given");
261
+ return io_read_int_be(conn, len_i);
262
+ }
263
+
264
+ /* call-seq:
265
+ * conn.read_uint_be(len) -> int
266
+ *
267
+ * Reads an unsigned integer from the target in big endian notation. The len
268
+ * parameter indicates the integer field length in bytes, with an upper limit
269
+ * of 8 bytes. If EOF is encountered before enough bytes are read, returns nil.
270
+ *
271
+ * @param len [Integer] number of bytes to read
272
+ * @return [Integer, nil] integer value or nil if EOF
273
+ */
274
+ VALUE IO_read_uint_be(VALUE self, VALUE len) {
275
+ struct um_io *conn = um_get_io(self);
276
+ size_t len_i = NUM2UINT(len);
277
+ if (unlikely(len_i > MAX_INT_SIZE)) rb_raise(eUMError, "Invalid length given");
278
+ return io_read_uint_be(conn, len_i);
279
+ }
280
+
245
281
  /* call-seq:
246
282
  * conn.write(*bufs) -> len
247
283
  *
@@ -375,6 +411,9 @@ void Init_IO(void) {
375
411
  rb_define_method(cIO, "skip", IO_skip, 1);
376
412
  rb_define_method(cIO, "read_each", IO_read_each, 0);
377
413
 
414
+ rb_define_method(cIO, "read_int_be", IO_read_int_be, 1);
415
+ rb_define_method(cIO, "read_uint_be", IO_read_uint_be, 1);
416
+
378
417
  rb_define_method(cIO, "write", IO_write, -1);
379
418
 
380
419
  rb_define_method(cIO, "resp_read", IO_resp_read, 0);
data/ext/um/um_op.c CHANGED
@@ -156,9 +156,8 @@ inline void um_op_multishot_results_push(struct um *machine, struct um_op *op, _
156
156
  result->res = res;
157
157
  result->flags = flags;
158
158
  result->next = NULL;
159
- if (op->flags & OP_F_BUFFER_POOL && res >= 0) {
159
+ if (op->flags & OP_F_BUFFER_POOL && res >= 0)
160
160
  result->segment = bp_get_op_result_segment(machine, op, res, flags);
161
- }
162
161
 
163
162
  op->multishot_result_tail = result;
164
163
  op->multishot_result_count++;
@@ -44,7 +44,7 @@ class UringMachine
44
44
  # @return [Array<String>] name servers
45
45
  def get_nameservers
46
46
  nameservers = []
47
- IO.readlines('/etc/resolv.conf').each do |line|
47
+ ::IO.readlines('/etc/resolv.conf').each do |line|
48
48
  if line =~ /^nameserver (.+)$/
49
49
  nameservers << $1.split(/\s+/).first
50
50
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UringMachine
4
- VERSION = '1.0.1'
4
+ VERSION = '1.0.4'
5
5
  end
data/test/test_io.rb CHANGED
@@ -305,6 +305,22 @@ class IOTest < IOBaseTest
305
305
  machine.terminate(f)
306
306
  machine.join(f)
307
307
  end
308
+
309
+ def test_io_read_segfault
310
+ machine.write(@wfd, "abcd")
311
+ conn.read(4)
312
+
313
+ machine.write(@wfd, "\x00\x01\x05\xdf")
314
+ conn.read(4)
315
+
316
+ f = machine.spin do
317
+ machine.write(@wfd, "\xde")
318
+ machine.snooze
319
+ machine.write(@wfd, "\x90\x03")
320
+ end
321
+ value = conn.read(3)
322
+ assert_equal "\xde\x90\x03".b, value
323
+ end
308
324
  end
309
325
 
310
326
  class IOWriteTest < UMBaseTest
@@ -806,3 +822,113 @@ class IOByteCountsTest < IOBaseTest
806
822
  assert_equal 0, conn.pending
807
823
  end
808
824
  end
825
+
826
+ class IOIntTest < IOBaseTest
827
+ def test_io_read_int_be
828
+ machine.write(@wfd, "abcd")
829
+ conn.read(4)
830
+
831
+ machine.write(@wfd, "\x00\x01\x05\xdf")
832
+ value = conn.read_int_be(2)
833
+ assert_equal 0x0001, value
834
+ value = conn.read_int_be(2)
835
+ assert_equal 0x05df, value
836
+
837
+ machine.write(@wfd, "\x00\x01\x05\xdf")
838
+ value = conn.read_int_be(4)
839
+ assert_equal 0x000105df, value
840
+
841
+ machine.write(@wfd, "\xde\xfa")
842
+ machine.snooze
843
+ machine.write(@wfd, "\xdd\x03")
844
+ machine.snooze
845
+ value = conn.read_int_be(4)
846
+ assert_equal 0xdefadd03, value
847
+
848
+ machine.write(@wfd, "\xed\xcb\xa9")
849
+ value = conn.read_int_be(3)
850
+ assert_equal 0xedcba9, value
851
+
852
+ f = machine.spin do
853
+ machine.write(@wfd, "\xde")
854
+ machine.snooze
855
+ machine.write(@wfd, "\x90\x03")
856
+ end
857
+ value = conn.read_int_be(3)
858
+ machine.join(f)
859
+ assert_equal 0xde9003, value
860
+
861
+ machine.write(@wfd, "\xde\xfa")
862
+ machine.close(@wfd)
863
+ assert_nil conn.read_int_be(4)
864
+ end
865
+
866
+ def test_io_read_int_be_invalid_length
867
+ machine.write(@wfd, (1..8).to_a.pack('C*'))
868
+ assert_raises(UM::Error) { conn.read_int_be(-1) }
869
+ assert_raises(UM::Error) { conn.read_int_be(20) }
870
+ assert_raises(UM::Error) { conn.read_int_be(9) }
871
+
872
+ # 8 is my limit on Schnitzengruben
873
+ value = conn.read_int_be(8)
874
+ assert_equal 0x0102030405060708, value
875
+ end
876
+
877
+ def test_io_read_uint_be
878
+ machine.write(@wfd, "abcd")
879
+ conn.read(4)
880
+
881
+ machine.write(@wfd, "\x00\x01\x05\xdf")
882
+ value = conn.read_uint_be(2)
883
+ assert_equal 0x0001, value
884
+ value = conn.read_uint_be(2)
885
+ assert_equal 0x05df, value
886
+
887
+ machine.write(@wfd, "\x00\x01\x05\xdf")
888
+ value = conn.read_uint_be(4)
889
+ assert_equal 0x000105df, value
890
+
891
+ machine.write(@wfd, "\xde\xfa")
892
+ machine.snooze
893
+ machine.write(@wfd, "\xdd\x03")
894
+ machine.snooze
895
+ value = conn.read_uint_be(4)
896
+ assert_equal 0xdefadd03, value
897
+
898
+ machine.write(@wfd, "\xed\xcb\xa9")
899
+ value = conn.read_uint_be(3)
900
+ assert_equal 0xedcba9, value
901
+
902
+ f = machine.spin do
903
+ machine.write(@wfd, "\xde")
904
+ machine.snooze
905
+ machine.write(@wfd, "\x90\x03")
906
+ end
907
+ value = conn.read_uint_be(3)
908
+ machine.join(f)
909
+ assert_equal 0xde9003, value
910
+
911
+ machine.write(@wfd, "\xff" * 8)
912
+ value = conn.read_uint_be(8)
913
+ assert_equal 0xffffffffffffffff, value
914
+
915
+ machine.write(@wfd, "\x80\x00\x00\x00\x00\x00\x00\x00")
916
+ value = conn.read_uint_be(8)
917
+ assert_equal 0x8000000000000000, value
918
+
919
+ machine.write(@wfd, "\xde\xfa")
920
+ machine.close(@wfd)
921
+ assert_nil conn.read_uint_be(4)
922
+ end
923
+
924
+ def test_io_read_uint_be_invalid_length
925
+ machine.write(@wfd, (0xf1..0xf8).to_a.pack('C*'))
926
+ assert_raises(UM::Error) { conn.read_uint_be(-1) }
927
+ assert_raises(UM::Error) { conn.read_uint_be(20) }
928
+ assert_raises(UM::Error) { conn.read_uint_be(9) }
929
+
930
+ # 8 is my limit on Schnitzengruben
931
+ value = conn.read_uint_be(8)
932
+ assert_equal 0xf1f2f3f4f5f6f7f8, value
933
+ end
934
+ end
data/test/test_um.rb CHANGED
@@ -3277,6 +3277,23 @@ class FileWatchTest < UMBaseTest
3277
3277
  name: 'foo.txt'
3278
3278
  }
3279
3279
  ], events
3280
+
3281
+ fn = File.join(@root, '.bar')
3282
+ IO.write(fn, 'barbar')
3283
+
3284
+ events = machine.inotify_get_events(fd)
3285
+ assert_equal [
3286
+ {
3287
+ wd: wd,
3288
+ mask: UM::IN_CREATE,
3289
+ name: '.bar'
3290
+ },
3291
+ {
3292
+ wd: wd,
3293
+ mask: UM::IN_CLOSE_WRITE,
3294
+ name: '.bar'
3295
+ }
3296
+ ], events
3280
3297
  end
3281
3298
 
3282
3299
  def write_file(fn, data)
@@ -3386,6 +3403,25 @@ class FileWatchTest < UMBaseTest
3386
3403
  { mask: UM::IN_CREATE, fn: File.join(@root, 'bar/baz.txt') },
3387
3404
  { mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'bar/baz.txt') },
3388
3405
  ], events
3406
+
3407
+ fn = File.join(@root, 'bar/.dotfile')
3408
+ write_file(fn, 'dotdot')
3409
+ machine.sleep(0.05)
3410
+ assert_equal [
3411
+ { mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'foo.txt') },
3412
+ { mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'foo/baz.txt') },
3413
+ { mask: UM::IN_CREATE | UM::IN_ISDIR, fn: File.join(@root, 'bar') },
3414
+ { mask: UM::IN_CREATE, fn: File.join(@root, 'bar/baz.txt') },
3415
+ { mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'bar/baz.txt') },
3416
+ { mask: UM::IN_DELETE, fn: File.join(@root, 'foo/baz.txt') },
3417
+ { mask: UM::IN_DELETE, fn: File.join(@root, 'bar/baz.txt') },
3418
+ { mask: UM::IN_DELETE | UM::IN_ISDIR, fn: File.join(@root, 'bar') },
3419
+ { mask: UM::IN_CREATE | UM::IN_ISDIR, fn: File.join(@root, 'bar') },
3420
+ { mask: UM::IN_CREATE, fn: File.join(@root, 'bar/baz.txt') },
3421
+ { mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'bar/baz.txt') },
3422
+ { mask: UM::IN_CREATE, fn: File.join(@root, 'bar/.dotfile') },
3423
+ { mask: UM::IN_CLOSE_WRITE, fn: File.join(@root, 'bar/.dotfile') },
3424
+ ], events
3389
3425
  ensure
3390
3426
  machine.schedule(f, UM::Terminate.new)
3391
3427
  machine.join(f)
@@ -3397,22 +3433,26 @@ class SetChildSubreaperTest < Minitest::Test
3397
3433
  r, w = IO.pipe
3398
3434
  UM.pr_set_child_subreaper(true)
3399
3435
 
3400
- child_pid = fork {
3436
+ child_pid = fork do
3401
3437
  r2, w2 = IO.pipe
3402
- pid = fork {
3438
+ pid = fork do
3403
3439
  r.close
3404
3440
  w.close
3405
3441
  w2.close
3406
3442
  r2.read
3407
3443
  r2.close
3408
3444
  sleep(0.01)
3409
- }
3445
+ ensure
3446
+ exit!
3447
+ end
3410
3448
  w << pid
3411
3449
  w.close
3412
3450
  r2.close
3413
3451
  w2 << 'done'
3414
3452
  w2.close
3415
- }
3453
+ ensure
3454
+ exit!
3455
+ end
3416
3456
  Process.wait(child_pid)
3417
3457
 
3418
3458
  w.close
@@ -3635,4 +3675,11 @@ class FsyncTest < UMBaseTest
3635
3675
  def test_fsync_bad_args
3636
3676
  assert_raises(Errno::EINVAL) { machine.fsync(1) }
3637
3677
  end
3638
- end
3678
+ end
3679
+
3680
+ class ResolveTest < UMBaseTest
3681
+ def test_resolve_ipv4
3682
+ ip = machine.resolve('localhost')
3683
+ assert_equal ['127.0.0.1'], ip
3684
+ end
3685
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uringmachine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -103,6 +103,7 @@ files:
103
103
  - examples/http_server.rb
104
104
  - examples/inout.rb
105
105
  - examples/io_uring_simple.c
106
+ - examples/irb_fiber.rb
106
107
  - examples/nc.rb
107
108
  - examples/nc_ssl.rb
108
109
  - examples/pg.rb
@@ -505,7 +506,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
505
506
  - !ruby/object:Gem::Version
506
507
  version: '0'
507
508
  requirements: []
508
- rubygems_version: 4.0.3
509
+ rubygems_version: 4.0.6
509
510
  specification_version: 4
510
511
  summary: A lean, mean io_uring machine
511
512
  test_files: []