uringmachine 0.6 → 0.7

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: dc849d9467e24fa296a104e5b1caf71907495a3ed9cc68370bc6eb134a474cbf
4
- data.tar.gz: 4fcdf5cd6055321f37375094aa11b56696c894d6a2c47ed9f756fb9a40d79753
3
+ metadata.gz: d7c0b7cfea4bdb08c954cf8dcef6cc776e4eb73e64fc366a43042f7a62902430
4
+ data.tar.gz: 83f60da253aa4ab69ef98a96205995b258c0d4a6925385818e34d2cb9fd26a0f
5
5
  SHA512:
6
- metadata.gz: 3d83d7419000bd434bac101d72c6850274a71dd0324bd1732043cbdb08ef659b683e1010dc31093fe7a77e37368698db56592685bf7fe8a2f76ee4cd70cee52f
7
- data.tar.gz: dcb79682bbdc20b99bd312aad22145642c4479009250f01f2e0c83f506026218d973b5c6e8b0aecff4e993acbe3998822bccbdf1de7c77a025b7560b5860ca77
6
+ metadata.gz: 54d58ae7aedc3f7444cff6d8b4afbf4845ba0ddf6ae5a662b60b04b83c81b7e2af35c1e46a1f3827adb07bf95c4677b4253774bd50c590541177a5a251d7f2b4
7
+ data.tar.gz: 9a6d69766b3d17c5b1593bd153608f846370c32bd7219796f4fbe677c379724aa1186f70a5cf4cbb797817e4564bdff01ab5a5a3514e803ac8ead5b949899fde
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # 2025-04-28 Version 0.7
2
+
3
+ - Add `#shutdown`
4
+
5
+ # 2025-04-23 Version 0.6.1
6
+
7
+ - Improve `#snooze` to prevent op completion starvation
8
+
1
9
  # 2025-04-23 Version 0.6
2
10
 
3
11
  - Add `#periodically` for multishot timeout
data/TODO.md CHANGED
@@ -14,5 +14,4 @@
14
14
  - fadvise
15
15
  - madvise
16
16
  - getxattr / setxattr
17
- - shutdown
18
17
  - send_bundle / recv_bundle (kernel >= 6.10)
data/ext/um/um.c CHANGED
@@ -175,8 +175,28 @@ inline VALUE process_runqueue_op(struct um *machine, struct um_op *op) {
175
175
  inline VALUE um_fiber_switch(struct um *machine) {
176
176
  while (true) {
177
177
  struct um_op *op = um_runqueue_shift(machine);
178
- if (op)
178
+ if (op) {
179
+ // in case of a snooze, we need to prevent a situation where completions
180
+ // are not processed because the runqueue is never empty. Theoretically,
181
+ // we can still have a situation where multiple fibers are all doing a
182
+ // snooze repeatedly, which can prevent completions from being processed.
183
+
184
+ // is the op a snooze op and is this the same fiber as the current one?
185
+ if (unlikely(op->kind == OP_SCHEDULE && op->fiber == rb_fiber_current())) {
186
+ // are there any pending ops (i.e. waiting for completion)?
187
+ if (machine->pending_count > 0) {
188
+ // if yes, process completions, get runqueue head, put original op
189
+ // back on runqueue.
190
+ um_wait_for_and_process_ready_cqes(machine);
191
+ struct um_op *op2 = um_runqueue_shift(machine);
192
+ if (likely(op2 && op2 != op)) {
193
+ um_runqueue_push(machine, op);
194
+ op = op2;
195
+ }
196
+ }
197
+ }
179
198
  return process_runqueue_op(machine, op);
199
+ }
180
200
 
181
201
  um_wait_for_and_process_ready_cqes(machine);
182
202
  }
@@ -529,6 +549,22 @@ VALUE um_setsockopt(struct um *machine, int fd, int level, int opt, int value) {
529
549
  return raise_if_exception(ret);
530
550
  }
531
551
 
552
+ VALUE um_shutdown(struct um *machine, int fd, int how) {
553
+ VALUE ret = Qnil;
554
+
555
+ struct um_op op;
556
+ um_prep_op(machine, &op, OP_SHUTDOWN);
557
+ struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
558
+ io_uring_prep_shutdown(sqe, fd, how);
559
+
560
+ ret = um_fiber_switch(machine);
561
+ if (um_check_completion(machine, &op))
562
+ ret = INT2NUM(op.result.res);
563
+
564
+ RB_GC_GUARD(ret);
565
+ return raise_if_exception(ret);
566
+ }
567
+
532
568
  VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode) {
533
569
  struct um_op op;
534
570
  um_prep_op(machine, &op, OP_BIND);
data/ext/um/um.h CHANGED
@@ -37,6 +37,7 @@ enum op_kind {
37
37
  OP_LISTEN,
38
38
  OP_GETSOCKOPT,
39
39
  OP_SETSOCKOPT,
40
+ OP_SHUTDOWN,
40
41
 
41
42
  OP_FUTEX_WAIT,
42
43
  OP_FUTEX_WAKE,
@@ -220,6 +221,7 @@ VALUE um_bind(struct um *machine, int fd, struct sockaddr *addr, socklen_t addrl
220
221
  VALUE um_listen(struct um *machine, int fd, int backlog);
221
222
  VALUE um_getsockopt(struct um *machine, int fd, int level, int opt);
222
223
  VALUE um_setsockopt(struct um *machine, int fd, int level, int opt, int value);
224
+ VALUE um_shutdown(struct um *machine, int fd, int how);
223
225
 
224
226
  void um_async_op_set(VALUE self, struct um *machine, struct um_op *op);
225
227
  VALUE um_async_op_await(struct um_async_op *async_op);
data/ext/um/um_class.c CHANGED
@@ -150,6 +150,12 @@ VALUE UM_socket(VALUE self, VALUE domain, VALUE type, VALUE protocol, VALUE flag
150
150
  return um_socket(machine, NUM2INT(domain), NUM2INT(type), NUM2INT(protocol), NUM2UINT(flags));
151
151
  }
152
152
 
153
+ VALUE UM_shutdown(VALUE self, VALUE fd, VALUE how) {
154
+ struct um *machine = um_get_machine(self);
155
+ return um_shutdown(machine, NUM2INT(fd), NUM2INT(how));
156
+
157
+ }
158
+
153
159
  VALUE UM_connect(VALUE self, VALUE fd, VALUE host, VALUE port) {
154
160
  struct um *machine = um_get_machine(self);
155
161
 
@@ -350,6 +356,7 @@ void Init_UM(void) {
350
356
  rb_define_method(cUM, "send", UM_send, 4);
351
357
  rb_define_method(cUM, "setsockopt", UM_setsockopt, 4);
352
358
  rb_define_method(cUM, "socket", UM_socket, 4);
359
+ rb_define_method(cUM, "shutdown", UM_shutdown, 2);
353
360
 
354
361
  rb_define_method(cUM, "prep_timeout", UM_prep_timeout, 1);
355
362
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UringMachine
4
- VERSION = '0.6'
4
+ VERSION = '0.7'
5
5
  end
data/test/run.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir.glob("#{__dir__}/test_*.rb").each do |path|
4
+ require(path)
5
+ end
data/test/test_um.rb CHANGED
@@ -514,7 +514,7 @@ class WriteTest < UMBaseTest
514
514
  end
515
515
  end
516
516
 
517
- class Closetest < UMBaseTest
517
+ class CloseTest < UMBaseTest
518
518
  def test_close
519
519
  r, w = IO.pipe
520
520
  machine.write(w.fileno, 'foo')
@@ -529,6 +529,54 @@ class Closetest < UMBaseTest
529
529
  end
530
530
  end
531
531
 
532
+ class ShutdownTest < UMBaseTest
533
+ def make_socket_pair
534
+ port = 10000 + rand(30000)
535
+ server_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
536
+ @machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
537
+ @machine.bind(server_fd, '127.0.0.1', port)
538
+ @machine.listen(server_fd, UM::SOMAXCONN)
539
+
540
+ client_conn_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
541
+ @machine.connect(client_conn_fd, '127.0.0.1', port)
542
+
543
+ server_conn_fd = @machine.accept(server_fd)
544
+
545
+ @machine.close(server_fd)
546
+ [client_conn_fd, server_conn_fd]
547
+ end
548
+
549
+ def test_shutdown
550
+ c_fd, s_fd = make_socket_pair
551
+ res = @machine.send(c_fd, 'abc', 3, 0)
552
+ assert_equal 3, res
553
+
554
+ buf = +''
555
+ res = @machine.recv(s_fd, buf, 256, 0)
556
+ assert_equal 3, res
557
+ assert_equal 'abc', buf
558
+
559
+ res = @machine.shutdown(c_fd, UM::SHUT_WR)
560
+ assert_equal 0, res
561
+
562
+ assert_raises(Errno::EPIPE) { @machine.send(c_fd, 'abc', 3, 0) }
563
+
564
+ res = @machine.shutdown(s_fd, UM::SHUT_RD)
565
+ assert_equal 0, res
566
+
567
+ res = @machine.recv(s_fd, buf, 256, 0)
568
+ assert_equal 0, res
569
+
570
+ res = @machine.shutdown(c_fd, UM::SHUT_RDWR)
571
+ assert_equal 0, res
572
+
573
+ assert_raises(Errno::EINVAL) { @machine.shutdown(c_fd, -9999) }
574
+ ensure
575
+ @machine.close(c_fd)
576
+ @machine.close(s_fd)
577
+ end
578
+ end
579
+
532
580
  class AcceptTest < UMBaseTest
533
581
  def setup
534
582
  super
@@ -917,32 +965,28 @@ class QueueTest < UMBaseTest
917
965
  q = UM::Queue.new
918
966
  buf = []
919
967
 
920
- f1 = Fiber.new do
968
+ machine.spin do
921
969
  buf << [1, machine.pop(q)]
922
- machine.yield
923
970
  end
924
971
 
925
- machine.schedule(f1, nil)
926
-
927
- f2 = Fiber.new do
972
+ machine.spin do
928
973
  buf << [2, machine.pop(q)]
929
- machine.yield
930
974
  end
931
975
 
932
- machine.schedule(f2, nil)
933
-
934
976
  machine.snooze
935
977
  assert_equal [], buf
978
+ assert_equal 2, machine.pending_count
936
979
 
937
980
  machine.push(q, :foo)
938
981
  assert_equal 1, q.count
939
- machine.sleep(0.02)
982
+ machine.snooze
983
+ assert_equal 1, machine.pending_count
940
984
  assert_equal [[1, :foo]], buf
941
985
 
942
986
  machine.push(q, :bar)
943
987
  assert_equal 1, q.count
944
988
 
945
- machine.sleep(0.02)
989
+ machine.snooze
946
990
  assert_equal [[1, :foo], [2, :bar]], buf
947
991
  assert_equal 0, q.count
948
992
  end
@@ -969,7 +1013,7 @@ class QueueTest < UMBaseTest
969
1013
  end
970
1014
  machine.schedule(f2, nil)
971
1015
 
972
- 3.times { machine.snooze }
1016
+ machine.snooze
973
1017
 
974
1018
  assert_equal [[1, :bar], [2, :foo]], buf.sort
975
1019
  assert_equal 0, q.count
@@ -996,12 +1040,12 @@ class QueueTest < UMBaseTest
996
1040
  end
997
1041
  machine.schedule(f2, nil)
998
1042
 
999
- machine.sleep 0.01
1043
+ machine.snooze
1000
1044
 
1001
1045
  assert_equal [[1, :foo]], buf
1002
1046
  machine.push(q, :bar)
1003
1047
 
1004
- machine.sleep 0.01
1048
+ machine.snooze
1005
1049
  assert_equal [[1, :foo], [2, :bar]], buf
1006
1050
  end
1007
1051
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uringmachine
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.6'
4
+ version: '0.7'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-23 00:00:00.000000000 Z
10
+ date: 2025-04-28 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rake-compiler
@@ -136,6 +136,7 @@ files:
136
136
  - lib/uringmachine/version.rb
137
137
  - supressions/ruby.supp
138
138
  - test/helper.rb
139
+ - test/run.rb
139
140
  - test/test_actor.rb
140
141
  - test/test_async_op.rb
141
142
  - test/test_ssl.rb