uringmachine 0.7 → 0.8.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: d7c0b7cfea4bdb08c954cf8dcef6cc776e4eb73e64fc366a43042f7a62902430
4
- data.tar.gz: 83f60da253aa4ab69ef98a96205995b258c0d4a6925385818e34d2cb9fd26a0f
3
+ metadata.gz: 7fdbc29b1e4c910d05eef327345a52faafd92566d7bdddaad81396bcf1f4767d
4
+ data.tar.gz: 4729a2ca7640fd58d0d83cc008db6e3431edfcdf04e396bce9981465a8e4e0a9
5
5
  SHA512:
6
- metadata.gz: 54d58ae7aedc3f7444cff6d8b4afbf4845ba0ddf6ae5a662b60b04b83c81b7e2af35c1e46a1f3827adb07bf95c4677b4253774bd50c590541177a5a251d7f2b4
7
- data.tar.gz: 9a6d69766b3d17c5b1593bd153608f846370c32bd7219796f4fbe677c379724aa1186f70a5cf4cbb797817e4564bdff01ab5a5a3514e803ac8ead5b949899fde
6
+ metadata.gz: 56c03563909bd5acd8466a0ef255cd2a791c9907e7fa9ab0a88d9be22f4db895cfb85f0ed5fc0883d0e583c216e238e954a74623f6d231f38c51de2f65316af8
7
+ data.tar.gz: 1471eea52c8edb8bab45e4fa058191c13e1c88a39bd728c1be5f72998058724ab7fb8a699d279fe5ace36fec6dc5eaca5361a4265b909d078cf5549dd3a19af2
@@ -13,7 +13,7 @@ jobs:
13
13
  matrix:
14
14
  # macos-latest uses arm64, macos-13 uses x86
15
15
  os: [ubuntu-latest]
16
- ruby: ['3.3', '3.4', 'head']
16
+ ruby: ['3.4', 'head']
17
17
 
18
18
  name: ${{matrix.os}}, ${{matrix.ruby}}
19
19
 
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # 2025-05-05 Version 0.8.1
2
+
3
+ - Fix `#snooze` to not block waiting for CQE
4
+
5
+ # 2025-05-03 Version 0.8
6
+
7
+ - Add `#join`
8
+
1
9
  # 2025-04-28 Version 0.7
2
10
 
3
11
  - Add `#shutdown`
data/TODO.md CHANGED
@@ -1,3 +1,5 @@
1
+ # ops
2
+
1
3
  - [ ] multishot timeout
2
4
  - [v] machine.periodically(interval) { ... }
3
5
  - [ ] machine.prep_timeout_multishot(interval)
@@ -15,3 +17,9 @@
15
17
  - madvise
16
18
  - getxattr / setxattr
17
19
  - send_bundle / recv_bundle (kernel >= 6.10)
20
+
21
+ # actors
22
+
23
+ When doing a `call`, we need to provide a mailbox for the response. can this be
24
+ automatic?
25
+
@@ -43,7 +43,7 @@ def parse_http_parser
43
43
  headers['protocol'] = parser.http_version
44
44
  $machine.schedule(current_fiber, headers)
45
45
  end
46
-
46
+
47
47
  $machine.write(w.fileno, HTTP_MSG)
48
48
  $machine.yield
49
49
  ensure
@@ -89,7 +89,7 @@ def parse_headers(fd)
89
89
  while true
90
90
  line = get_line(fd, sio, buffer)
91
91
  break if line.empty?
92
-
92
+
93
93
  m = line.match(RE_HEADER_LINE)
94
94
  raise "Invalid header" if !m
95
95
 
@@ -111,7 +111,7 @@ def parse_http_stringio
111
111
  puts e.backtrace.join("\n")
112
112
  exit!
113
113
  end
114
-
114
+
115
115
  $machine.write(w.fileno, HTTP_MSG)
116
116
  $machine.yield
117
117
  ensure
data/examples/bm_queue.rb CHANGED
@@ -18,14 +18,14 @@ NUM_CONSUMERS = 10
18
18
  def run_threads
19
19
  queue = Queue.new
20
20
  done = Queue.new
21
-
21
+
22
22
  NUM_PRODUCERS.times do
23
23
  Thread.new do
24
24
  COUNT.times { queue << rand(1000) }
25
25
  done << true
26
26
  end
27
27
  end
28
-
28
+
29
29
  total = 0
30
30
  NUM_CONSUMERS.times do
31
31
  Thread.new do
data/examples/bm_write.rb CHANGED
@@ -18,7 +18,7 @@ FN = '/tmp/bm_write'
18
18
  def run_io_write(num_threads)
19
19
  FileUtils.rm(FN) rescue nil
20
20
  fio = File.open(FN, 'w')
21
-
21
+
22
22
  threads = num_threads.times.map do |i|
23
23
  Thread.new do
24
24
  ITERATIONS.times { fio.write(BUF) }
@@ -33,13 +33,13 @@ def run_um_write(num_fibers)
33
33
  FileUtils.rm(FN) rescue nil
34
34
  fio = File.open(FN, 'w')
35
35
  fd = fio.fileno
36
-
36
+
37
37
  machine = UringMachine.new
38
38
  done = UringMachine::Queue.new
39
39
  num_fibers.times do
40
40
  machine.spin do
41
41
  ITERATIONS.times { machine.write(fd, BUF) }
42
- machine.push(done, true)
42
+ machine.push(done, true)
43
43
  end
44
44
  end
45
45
  num_fibers.times { machine.pop(done) }
data/examples/pg.rb CHANGED
@@ -22,7 +22,7 @@ class UM::Stream
22
22
  @eof = true
23
23
  return false
24
24
  end
25
-
25
+
26
26
  @ofs_tail += ret
27
27
  true
28
28
  end
@@ -31,7 +31,7 @@ def run_client
31
31
  @machine.send(fd, msg, msg.bytesize, 0)
32
32
  res = @machine.recv(fd, buf, 8192, 0)
33
33
  @counter += 2
34
-
34
+
35
35
  break if res == 0
36
36
  raise "Got #{res} bytes instead of #{msg.bytesize}" if res != msg.bytesize
37
37
  end
@@ -59,6 +59,6 @@ end
59
59
 
60
60
  t0 = Time.now
61
61
  @machine.sleep 3
62
- t1 = Time.now
62
+ t1 = Time.now
63
63
  elapsed = t1 - t0
64
64
  puts "Did #{@counter} ops in #{elapsed} seconds (#{(@counter / elapsed)} ops/s)"
data/examples/snooze.rb CHANGED
@@ -32,7 +32,7 @@ loop do
32
32
  break if (Time.now - t0) > MAX_TIME
33
33
  start_fiber while @fiber_count < MAX_FIBERS
34
34
  end
35
- t1 = Time.now
35
+ t1 = Time.now
36
36
  elapsed = t1 - t0
37
37
  rate = @counter / elapsed
38
38
  puts "Did #{@counter} ops in #{elapsed} seconds (#{rate} ops/s)"
data/examples/stream.rb CHANGED
@@ -22,7 +22,7 @@ class UM::Stream
22
22
  @eof = true
23
23
  return false
24
24
  end
25
-
25
+
26
26
  @ofs_tail += ret
27
27
  true
28
28
  end
data/ext/um/extconf.rb CHANGED
@@ -6,61 +6,6 @@ require 'rbconfig'
6
6
 
7
7
  dir_config 'um_ext'
8
8
 
9
- def config_ssl
10
- # don't use pkg_config('openssl') if '--with-openssl-dir' is used
11
- has_openssl_dir = dir_config('openssl').any? ||
12
- RbConfig::CONFIG['configure_args']&.include?('openssl')
13
-
14
- found_pkg_config = !has_openssl_dir && pkg_config('openssl')
15
-
16
- found_ssl = if !$mingw && found_pkg_config
17
- puts '──── Using OpenSSL pkgconfig (openssl.pc) ────'
18
- true
19
- elsif have_library('libcrypto', 'BIO_read') && have_library('libssl', 'SSL_CTX_new')
20
- true
21
- elsif %w'crypto libeay32'.find {|crypto| have_library(crypto, 'BIO_read')} &&
22
- %w'ssl ssleay32'.find {|ssl| have_library(ssl, 'SSL_CTX_new')}
23
- true
24
- else
25
- puts '** Puma will be compiled without SSL support'
26
- false
27
- end
28
-
29
- if found_ssl
30
- have_header "openssl/bio.h"
31
-
32
- ssl_h = "openssl/ssl.h".freeze
33
-
34
- puts "\n──── Below are yes for 1.0.2 & later ────"
35
- have_func "DTLS_method" , ssl_h
36
- have_func "SSL_CTX_set_session_cache_mode(NULL, 0)", ssl_h
37
-
38
- puts "\n──── Below are yes for 1.1.0 & later ────"
39
- have_func "TLS_server_method" , ssl_h
40
- have_func "SSL_CTX_set_min_proto_version(NULL, 0)" , ssl_h
41
-
42
- puts "\n──── Below is yes for 1.1.0 and later, but isn't documented until 3.0.0 ────"
43
- # https://github.com/openssl/openssl/blob/OpenSSL_1_1_0/include/openssl/ssl.h#L1159
44
- have_func "SSL_CTX_set_dh_auto(NULL, 0)" , ssl_h
45
-
46
- puts "\n──── Below is yes for 1.1.1 & later ────"
47
- have_func "SSL_CTX_set_ciphersuites(NULL, \"\")" , ssl_h
48
-
49
- puts "\n──── Below is yes for 3.0.0 & later ────"
50
- have_func "SSL_get1_peer_certificate" , ssl_h
51
-
52
- puts ''
53
-
54
- # Random.bytes available in Ruby 2.5 and later, Random::DEFAULT deprecated in 3.0
55
- if Random.respond_to?(:bytes)
56
- $defs.push "-DHAVE_RANDOM_BYTES"
57
- puts "checking for Random.bytes... yes"
58
- else
59
- puts "checking for Random.bytes... no"
60
- end
61
- end
62
- end
63
-
64
9
  KERNEL_INFO_RE = /Linux (\d)\.(\d+)(?:\.)?((?:\d+\.?)*)(?:\-)?([\w\-]+)?/
65
10
  def get_config
66
11
  if RUBY_PLATFORM !~ /linux/
@@ -90,8 +35,6 @@ def get_config
90
35
  }
91
36
  end
92
37
 
93
- # config_ssl
94
-
95
38
  config = get_config
96
39
  puts "Building UringMachine (\n#{config.map { |(k, v)| " #{k}: #{v}\n"}.join})"
97
40
 
data/ext/um/um.c CHANGED
@@ -89,7 +89,7 @@ static inline void um_process_cqe(struct um *machine, struct io_uring_cqe *cqe)
89
89
  }
90
90
 
91
91
  if (op->flags & OP_F_ASYNC) return;
92
-
92
+
93
93
  um_runqueue_push(machine, op);
94
94
  }
95
95
 
@@ -127,6 +127,7 @@ done:
127
127
  struct wait_for_cqe_ctx {
128
128
  struct um *machine;
129
129
  struct io_uring_cqe *cqe;
130
+ int wait_nr;
130
131
  int result;
131
132
  };
132
133
 
@@ -140,16 +141,19 @@ void *um_wait_for_cqe_without_gvl(void *ptr) {
140
141
  // NULL.
141
142
  //
142
143
  // https://github.com/axboe/liburing/issues/1280
143
- int res = io_uring_submit_and_wait_timeout(&ctx->machine->ring, &ctx->cqe, 1, NULL, NULL);
144
+ int res = io_uring_submit_and_wait_timeout(&ctx->machine->ring, &ctx->cqe, ctx->wait_nr, NULL, NULL);
144
145
  ctx->result = (res > 0 && !ctx->cqe) ? -EINTR : res;
145
146
  }
146
147
  else
147
- ctx->result = io_uring_wait_cqe(&ctx->machine->ring, &ctx->cqe);
148
+ ctx->result = io_uring_wait_cqes(&ctx->machine->ring, &ctx->cqe, ctx->wait_nr, NULL, NULL);
148
149
  return NULL;
149
150
  }
150
151
 
151
- static inline void um_wait_for_and_process_ready_cqes(struct um *machine) {
152
- struct wait_for_cqe_ctx ctx = { .machine = machine, .cqe = NULL };
152
+ // Waits for the given minimum number of completion entries. The wait_nr is
153
+ // either 1 - where we wait for at least one CQE to be ready, or 0, where we
154
+ // don't wait, and just process any CQEs that already ready.
155
+ static inline void um_wait_for_and_process_ready_cqes(struct um *machine, int wait_nr) {
156
+ struct wait_for_cqe_ctx ctx = { .machine = machine, .cqe = NULL, .wait_nr = wait_nr };
153
157
  rb_thread_call_without_gvl(um_wait_for_cqe_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
154
158
 
155
159
  if (unlikely(ctx.result < 0 && ctx.result != -EINTR))
@@ -187,18 +191,19 @@ inline VALUE um_fiber_switch(struct um *machine) {
187
191
  if (machine->pending_count > 0) {
188
192
  // if yes, process completions, get runqueue head, put original op
189
193
  // back on runqueue.
190
- um_wait_for_and_process_ready_cqes(machine);
194
+ // um_process_ready_cqes(machine);
195
+ um_wait_for_and_process_ready_cqes(machine, 0);
191
196
  struct um_op *op2 = um_runqueue_shift(machine);
192
197
  if (likely(op2 && op2 != op)) {
193
198
  um_runqueue_push(machine, op);
194
- op = op2;
199
+ op = op2;
195
200
  }
196
201
  }
197
202
  }
198
203
  return process_runqueue_op(machine, op);
199
204
  }
200
205
 
201
- um_wait_for_and_process_ready_cqes(machine);
206
+ um_wait_for_and_process_ready_cqes(machine, 1);
202
207
  }
203
208
  }
204
209
 
@@ -320,45 +325,18 @@ VALUE um_sleep(struct um *machine, double duration) {
320
325
  return raise_if_exception(ret);
321
326
  }
322
327
 
323
- // VALUE um_periodically(struct um *machine, double interval) {
324
- // struct um_op op;
325
- // VALUE ret = Qnil;
326
- // um_prep_op(machine, &op, OP_SLEEP_MULTISHOT);
327
- // op.ts = um_double_to_timespec(interval);
328
- // op.flags |= OP_F_MULTISHOT;
329
- // struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
330
- // io_uring_prep_timeout(sqe, &op.ts, 0, IORING_TIMEOUT_MULTISHOT);
331
-
332
- // while (true) {
333
- // ret = um_fiber_switch(machine);
334
-
335
- // if (!um_op_completed_p(&op)) {
336
- // um_cancel_and_wait(machine, &op);
337
- // break;
338
- // }
339
- // else {
340
- // if (op.result.res != -ETIME) um_raise_on_error_result(op.result.res);
341
- // ret = DBL2NUM(interval);
342
- // }
343
- // }
344
-
345
- // RB_GC_GUARD(ret);
346
- // return raise_if_exception(ret);
347
-
348
- // }
349
-
350
328
  inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset) {
351
329
  struct um_op op;
352
330
  um_prep_op(machine, &op, OP_READ);
353
331
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
354
332
  void *ptr = um_prepare_read_buffer(buffer, maxlen, buffer_offset);
355
333
  io_uring_prep_read(sqe, fd, ptr, maxlen, -1);
356
-
334
+
357
335
  VALUE ret = um_fiber_switch(machine);
358
336
  if (um_check_completion(machine, &op)) {
359
337
  um_update_read_buffer(machine, buffer, buffer_offset, op.result.res, op.result.flags);
360
338
  ret = INT2NUM(op.result.res);
361
-
339
+
362
340
  }
363
341
 
364
342
  RB_GC_GUARD(buffer);
@@ -652,7 +630,7 @@ VALUE multishot_ensure(VALUE arg) {
652
630
  VALUE um_accept_each(struct um *machine, int fd) {
653
631
  struct um_op op;
654
632
  um_prep_op(machine, &op, OP_ACCEPT_MULTISHOT);
655
-
633
+
656
634
  struct op_ctx ctx = { .machine = machine, .op = &op, .fd = fd, .read_buf = NULL };
657
635
  return rb_ensure(accept_each_begin, (VALUE)&ctx, multishot_ensure, (VALUE)&ctx);
658
636
  }
@@ -803,7 +781,7 @@ VALUE um_periodically(struct um *machine, double interval) {
803
781
  struct um_op op;
804
782
  um_prep_op(machine, &op, OP_SLEEP_MULTISHOT);
805
783
  op.ts = um_double_to_timespec(interval);
806
-
784
+
807
785
  struct op_ctx ctx = { .machine = machine, .op = &op, .ts = op.ts, .read_buf = NULL };
808
786
  return rb_ensure(periodically_begin, (VALUE)&ctx, multishot_ensure, (VALUE)&ctx);
809
787
  }
data/ext/um/um.h CHANGED
@@ -135,7 +135,7 @@ struct um_queue {
135
135
  struct um_queue_entry *head;
136
136
  struct um_queue_entry *tail;
137
137
  struct um_queue_entry *free_head;
138
-
138
+
139
139
  uint32_t num_waiters;
140
140
  uint32_t state;
141
141
  uint32_t count;
@@ -246,6 +246,4 @@ VALUE um_queue_shift(struct um *machine, struct um_queue *queue);
246
246
 
247
247
  void um_define_net_constants(VALUE mod);
248
248
 
249
- // void Init_micro_ssl(VALUE mod);
250
-
251
249
  #endif // UM_H
data/ext/um/um_class.c CHANGED
@@ -368,7 +368,5 @@ void Init_UM(void) {
368
368
  rb_define_method(cUM, "unshift", UM_queue_unshift, 2);
369
369
  #endif
370
370
 
371
- // Init_micro_ssl(cUM);
372
-
373
371
  um_define_net_constants(cUM);
374
372
  }
data/ext/um/um_const.c CHANGED
@@ -159,7 +159,7 @@ void um_define_net_constants(VALUE mod) {
159
159
  DEF_CONST_INT(mod, TCP_THIN_LINEAR_TIMEOUTS);
160
160
  DEF_CONST_INT(mod, TCP_TIMESTAMP);
161
161
  DEF_CONST_INT(mod, TCP_USER_TIMEOUT);
162
-
162
+
163
163
  DEF_CONST_INT(mod, UDP_CORK);
164
164
 
165
165
  DEF_CONST_INT(mod, AI_PASSIVE);
@@ -169,7 +169,7 @@ void um_define_net_constants(VALUE mod) {
169
169
  DEF_CONST_INT(mod, AI_ALL);
170
170
  DEF_CONST_INT(mod, AI_ADDRCONFIG);
171
171
  DEF_CONST_INT(mod, AI_V4MAPPED);
172
-
172
+
173
173
  DEF_CONST_INT(mod, NI_MAXHOST);
174
174
  DEF_CONST_INT(mod, NI_MAXSERV);
175
175
  DEF_CONST_INT(mod, NI_NOFQDN);
@@ -177,7 +177,7 @@ void um_define_net_constants(VALUE mod) {
177
177
  DEF_CONST_INT(mod, NI_NAMEREQD);
178
178
  DEF_CONST_INT(mod, NI_NUMERICSERV);
179
179
  DEF_CONST_INT(mod, NI_DGRAM);
180
-
180
+
181
181
  DEF_CONST_INT(mod, SHUT_RD);
182
182
  DEF_CONST_INT(mod, SHUT_WR);
183
183
  DEF_CONST_INT(mod, SHUT_RDWR);
data/ext/um/um_op.c CHANGED
@@ -66,7 +66,7 @@ inline void um_op_list_compact(struct um *machine, struct um_op *head) {
66
66
  }
67
67
  }
68
68
 
69
- inline struct um_op_result *multishot_result_alloc(struct um *machine) {
69
+ inline struct um_op_result *multishot_result_alloc(struct um *machine) {
70
70
  if (machine->result_freelist) {
71
71
  struct um_op_result *result = machine->result_freelist;
72
72
  machine->result_freelist = result->next;
data/ext/um/um_sync.c CHANGED
@@ -12,7 +12,7 @@ void um_futex_wait(struct um *machine, uint32_t *futex, uint32_t expect) {
12
12
  sqe, (uint32_t *)futex, expect, FUTEX_BITSET_MATCH_ANY,
13
13
  FUTEX2_SIZE_U32, 0
14
14
  );
15
-
15
+
16
16
  VALUE ret = um_fiber_switch(machine);
17
17
  if (!um_op_completed_p(&op))
18
18
  um_cancel_and_wait(machine, &op);
@@ -157,7 +157,7 @@ static inline void queue_add_head(struct um_queue *queue, VALUE value) {
157
157
  struct um_queue_entry *entry = um_queue_entry_checkout(queue);
158
158
 
159
159
  entry->next = queue->head;
160
- if (queue->head) {
160
+ if (queue->head) {
161
161
  queue->head->prev = entry;
162
162
  queue->head = entry;
163
163
  }
@@ -171,7 +171,7 @@ static inline void queue_add_tail(struct um_queue *queue, VALUE value) {
171
171
  struct um_queue_entry *entry = um_queue_entry_checkout(queue);
172
172
 
173
173
  entry->prev = queue->tail;
174
- if (queue->tail) {
174
+ if (queue->tail) {
175
175
  queue->tail->next = entry;
176
176
  queue->tail = entry;
177
177
  }
@@ -205,7 +205,7 @@ VALUE queue_remove_tail(struct um_queue *queue) {
205
205
  static inline VALUE um_queue_add(struct um *machine, struct um_queue *queue, VALUE value, int add_head) {
206
206
  if (add_head) queue_add_head(queue, value);
207
207
  else queue_add_tail(queue, value);
208
-
208
+
209
209
  queue->count++;
210
210
 
211
211
  queue->state = QUEUE_READY;
@@ -232,7 +232,7 @@ struct queue_wait_ctx {
232
232
 
233
233
  VALUE um_queue_remove_begin(VALUE arg) {
234
234
  struct queue_wait_ctx *ctx = (struct queue_wait_ctx *)arg;
235
-
235
+
236
236
  ctx->queue->num_waiters++;
237
237
  while (ctx->queue->state == QUEUE_EMPTY) {
238
238
  um_futex_wait(ctx->machine, &ctx->queue->state, QUEUE_EMPTY);
@@ -10,10 +10,23 @@ class UringMachine
10
10
  actor
11
11
  end
12
12
 
13
+ def spin_thread_actor(mod, *a, **k)
14
+ machine = UM.new
15
+ target = Object.new.extend(mod)
16
+ mailbox = UM::Queue.new
17
+ actor = Actor.new
18
+ thread = Thread.new do
19
+ actor.run(machine, target, mailbox)
20
+ end
21
+ target.setup(*a, **k)
22
+ snooze
23
+ actor
24
+ end
25
+
13
26
  class Actor < Fiber
14
27
  def run(machine, target, mailbox)
15
28
  @machine = machine
16
- @target = target
29
+ @target = target
17
30
  @mailbox = mailbox
18
31
  while (msg = machine.shift(mailbox))
19
32
  process_message(msg)
@@ -23,30 +36,26 @@ class UringMachine
23
36
  end
24
37
 
25
38
  def cast(sym, *a, **k)
26
- self << [:cast, nil, sym, a, k]
39
+ @machine.push @mailbox, [:cast, nil, sym, a, k]
27
40
  self
28
41
  end
29
42
 
30
- def call(sym, *a, **k)
31
- self << [:call, Fiber.current, sym, a, k]
32
- @machine.yield
43
+ def call(response_mailbox, sym, *a, **k)
44
+ @machine.push @mailbox, [:call, response_mailbox, sym, a, k]
45
+ @machine.shift response_mailbox
33
46
  end
34
47
 
35
48
  private
36
49
 
37
50
  def process_message(msg)
38
- type, fiber, sym, args, kwargs = msg
51
+ type, response_mailbox, sym, args, kwargs = msg
39
52
  case type
40
53
  when :cast
41
54
  @target.send(sym, *args, **kwargs)
42
55
  when :call
43
56
  res = @target.send(sym, *args, **kwargs)
44
- @machine.schedule(fiber, res)
57
+ @machine.push(response_mailbox, res)
45
58
  end
46
59
  end
47
-
48
- def <<(msg)
49
- @machine.push(@mailbox, msg)
50
- end
51
60
  end
52
61
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UringMachine
4
- VERSION = '0.7'
4
+ VERSION = '0.8.1'
5
5
  end
data/lib/uringmachine.rb CHANGED
@@ -12,30 +12,85 @@ class UringMachine
12
12
  @@fiber_map
13
13
  end
14
14
 
15
+ class Terminate < Exception
16
+ end
17
+
15
18
  def spin(value = nil, fiber_class = Fiber, &block)
16
19
  f = fiber_class.new do |resume_value|
17
- block.(resume_value)
20
+ f.set_result block.(resume_value)
18
21
  rescue Exception => e
19
- STDERR.puts "Unhandled fiber exception: #{e.inspect}"
20
- STDERR.puts e.backtrace.join("\n")
21
- exit
22
+ f.set_result e
22
23
  ensure
24
+ f.mark_as_done
25
+ # cleanup
23
26
  @@fiber_map.delete(f)
24
- # yield control
27
+ self.notify_done_listeners(f)
28
+ # transfer control to other fibers
25
29
  self.yield
26
- p :bad_bad_bad
27
30
  end
28
- schedule(f, value)
31
+ self.schedule(f, value)
29
32
  @@fiber_map[f] = true
30
33
  f
31
34
  end
32
35
 
36
+ def join(*fibers)
37
+ results = fibers.inject({}) { |h, f| h[f] = nil; h }
38
+ queue = nil
39
+ pending = nil
40
+ fibers.each do |f|
41
+ if f.done?
42
+ results[f] = f.result
43
+ else
44
+ (pending ||= []) << f
45
+ queue ||= UM::Queue.new
46
+ f.add_done_listener(queue)
47
+ end
48
+ end
49
+ return results.values if !pending
50
+
51
+ while !pending.empty?
52
+ f = self.shift(queue)
53
+ pending.delete(f)
54
+ results[f] = f.result
55
+ end
56
+ results.values
57
+ end
58
+
33
59
  def resolve(hostname, type = :A)
34
60
  @resolver ||= DNSResolver.new(self)
35
61
  @resolver.resolve(hostname, type)
36
62
  end
37
63
 
38
- def ssl_accept(fd, ssl_ctx)
39
- SSL::Connection.new(self, fd, ssl_ctx)
64
+ private
65
+
66
+ def notify_done_listeners(fiber)
67
+ listeners = fiber.done_listeners
68
+ return if !listeners
69
+
70
+ listeners.each { self.push(it, fiber) }
71
+ end
72
+
73
+ module FiberExtensions
74
+ attr_reader :result, :done, :done_listeners
75
+
76
+ def mark_as_done
77
+ @done = true
78
+ end
79
+
80
+ def set_result(value)
81
+ @result = value
82
+ end
83
+
84
+ def done?
85
+ @done
86
+ end
87
+
88
+ def add_done_listener(queue)
89
+ (@done_listeners ||= []) << queue
90
+ end
91
+ end
92
+
93
+ class ::Fiber
94
+ include UringMachine::FiberExtensions
40
95
  end
41
96
  end
data/test/helper.rb CHANGED
@@ -58,7 +58,7 @@ class UMBaseTest < Minitest::Test
58
58
  end
59
59
 
60
60
  attr_accessor :machine
61
-
61
+
62
62
  def setup
63
63
  @machine = UM.new
64
64
  end