uringmachine 0.5 → 0.5.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: 2067cd0f88d6dbc341410ca6911916f510132ee23ce3f6d5fb5978259eeaef11
4
- data.tar.gz: f816a9478f714a3a9770740bc14fc03ae2687f2fe7d0e29cbc0b8632994bad1f
3
+ metadata.gz: 0b063db46fac29472c42866eb1864c731b24fc0846fe424eec06213bba8fde0a
4
+ data.tar.gz: a535fabf5d16107de8d3823766d9ff0d7d22d0702c5401e263eb925eb222dd5d
5
5
  SHA512:
6
- metadata.gz: 6e0a9ed6e6f131bf6d67a56622786893532a525f4427e134670499997c0461a4df6de3ca1cb9e70851769c1135575986468a83edfd9b5d75c7d592e41bb0ca8c
7
- data.tar.gz: 2c73ed9a3268ea96b3a167064251d16279c531ee50e5f9c6de9aa2c56f6a70973ef04ce5d6c44f46e39309f7bf8cb3f3ff5e61bf1512ca07cc5f35b91f7303dc
6
+ metadata.gz: 94ad9e942e84e87f0acbd75007962c3df5ea259de77ef4e590ff91dc4ea614dcd22e7db3da1954be05b11951e621f0cf58858391b677e109e97590bb0f8e3dee
7
+ data.tar.gz: 4c77e826fb33fc879de748b877eec9a6d49f6927b8167377b4191c30b9aa2b638dbd0cc76a77ec0a17daec648d2587ca4357f367501391aad06c7532606e85b2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,5 @@
1
+ - Add `#prep_timeout` and `AsyncOp`
2
+
1
3
  # 2024-11-14 Version 0.5
2
4
 
3
5
  - Add `#waitpid`
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/inline'
4
+
5
+ gemfile do
6
+ source 'https://rubygems.org'
7
+ gem 'uringmachine', path: '..'
8
+ gem 'extralite'
9
+ gem 'benchmark-ips'
10
+ end
11
+
12
+ require 'uringmachine'
13
+ require 'extralite'
14
+
15
+ class UM::Actor < Fiber
16
+ def initialize(machine, target)
17
+ @machine = machine
18
+ @target = target
19
+ @mailbox = UM::Queue.new
20
+ super { act }
21
+ end
22
+
23
+ def act
24
+ while (sym, a, k, peer = @machine.shift(@mailbox))
25
+
26
+ begin
27
+ ret = @target.send(sym, *a, **k)
28
+ @machine.schedule(peer, ret)
29
+ rescue => e
30
+ @machine.schedule(peer, e)
31
+ end
32
+ end
33
+ rescue Exception => e
34
+ # handle unhandled exceptions
35
+ ensure
36
+ @machine.fiber_map.delete(self)
37
+ @machine.yield
38
+ end
39
+
40
+ def method_missing(sym, *a, **k)
41
+ @machine.push(@mailbox, [sym, a, k, Fiber.current])
42
+ ret = @machine.yield
43
+ raise(ret) if ret.is_a?(Exception)
44
+ ret
45
+ end
46
+ end
47
+
48
+ class UM
49
+ def spin_actor(target)
50
+ f = UM::Actor.new(self, target)
51
+ schedule(f, nil)
52
+ @@fiber_map[f] = true
53
+ f
54
+ end
55
+ end
56
+
57
+ class Locker
58
+ def initialize(machine, target)
59
+ @machine = machine
60
+ @target = target
61
+ @mutex = UM::Mutex.new
62
+ end
63
+
64
+ def method_missing(sym, *a, **k)
65
+ @machine.synchronize(@mutex) { @target.send(sym, *a, **k) }
66
+ end
67
+ end
68
+
69
+
70
+ PATH = '/tmp/foo'
71
+
72
+ $machine = UM.new
73
+ $raw_db = Extralite::Database.new(PATH)
74
+ $actor_db = $machine.spin_actor(Extralite::Database.new(PATH))
75
+ $locker_db = Locker.new($machine, Extralite::Database.new(PATH))
76
+
77
+ [$raw_db, $actor_db, $locker_db].each do |db|
78
+ p db.query('select 1')
79
+ end
80
+
81
+ bm = Benchmark.ips do |x|
82
+ x.config(:time => 5, :warmup => 2)
83
+
84
+ x.report("raw") { $raw_db.query('select 1') }
85
+ x.report("actor") { $actor_db.query('select 1') }
86
+ x.report("locker") { $locker_db.query('select 1') }
87
+
88
+ x.compare!
89
+ end
@@ -35,7 +35,7 @@ end
35
35
 
36
36
  server_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
37
37
  @machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
38
- @machine.bind(server_fd, '127.0.0.1', 1234)
38
+ @machine.bind(server_fd, '0.0.0.0', 1234)
39
39
  @machine.listen(server_fd, UM::SOMAXCONN)
40
40
  puts 'Listening on port 1234'
41
41
 
data/examples/pg.rb ADDED
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/uringmachine'
4
+
5
+ @machine = UM.new
6
+
7
+ class UM::Stream
8
+ def initialize(machine, fd)
9
+ @machine, @fd, @bgid = machine, fd
10
+ @buffer = +''
11
+ @ofs_head = 0
12
+ @ofs_tail = 0
13
+ end
14
+
15
+ def feed
16
+ if (@ofs_head == @ofs_tail) && (@ofs_head >= 4096)
17
+ @buffer = +''
18
+ @ofs_head = @ofs_tail = 0
19
+ end
20
+ ret = @machine.read(@fd, @buffer, 65536, @ofs_tail)
21
+ if ret == 0
22
+ @eof = true
23
+ return false
24
+ end
25
+
26
+ @ofs_tail += ret
27
+ true
28
+ end
29
+
30
+ def read(len)
31
+ if @ofs_head + len > @ofs_tail
32
+ feed
33
+ end
34
+
35
+ str = @buffer[@ofs_head, len]
36
+ @ofs_head += str.bytesize
37
+ str
38
+ end
39
+
40
+ def gets(sep = $/, _limit = nil, _chomp: nil)
41
+ if sep.is_a?(Integer)
42
+ sep = $/
43
+ _limit = sep
44
+ end
45
+ sep_size = sep.bytesize
46
+
47
+ while true
48
+ idx = @buffer.index(sep, @ofs_head)
49
+ if idx
50
+ str = @buffer[@ofs_head, idx + sep_size]
51
+ @ofs_head += str.bytesize
52
+ return str
53
+ end
54
+
55
+ return nil if !feed
56
+ end
57
+ end
58
+ end
59
+
60
+ $machine = UringMachine.new
61
+
62
+ server_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
63
+ $machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
64
+ $machine.bind(server_fd, '127.0.0.1', 1234)
65
+ $machine.listen(server_fd, UM::SOMAXCONN)
66
+ puts 'Listening on port 1234'
67
+
68
+ def handle_connection(fd)
69
+ stream = UM::Stream.new($machine, fd)
70
+
71
+ while (l = stream.gets)
72
+ $machine.write(fd, "You said: #{l}")
73
+ end
74
+ rescue Exception => e
75
+ puts "Got error #{e.inspect}, closing connection"
76
+ $machine.close(fd) rescue nil
77
+ end
78
+
79
+ main = Fiber.current
80
+ trap('SIGINT') { $machine.spin { $machine.schedule(main, SystemExit.new) } }
81
+
82
+ $machine.accept_each(server_fd) do |fd|
83
+ puts "Connection accepted fd #{fd}"
84
+ $machine.spin(fd) { handle_connection(_1) }
85
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/uringmachine'
4
+
5
+ @machine = UM.new
6
+
7
+ class UM::Stream
8
+ def initialize(machine, fd)
9
+ @machine, @fd, @bgid = machine, fd
10
+ @buffer = +''
11
+ @ofs_head = 0
12
+ @ofs_tail = 0
13
+ end
14
+
15
+ def feed
16
+ if (@ofs_head == @ofs_tail) && (@ofs_head >= 4096)
17
+ @buffer = +''
18
+ @ofs_head = @ofs_tail = 0
19
+ end
20
+ ret = @machine.read(@fd, @buffer, 65536, @ofs_tail)
21
+ if ret == 0
22
+ @eof = true
23
+ return false
24
+ end
25
+
26
+ @ofs_tail += ret
27
+ true
28
+ end
29
+
30
+ def read(len)
31
+ if @ofs_head + len > @ofs_tail
32
+ feed
33
+ end
34
+
35
+ str = @buffer[@ofs_head, len]
36
+ @ofs_head += str.bytesize
37
+ str
38
+ end
39
+
40
+ def gets(sep = $/, _limit = nil, _chomp: nil)
41
+ if sep.is_a?(Integer)
42
+ sep = $/
43
+ _limit = sep
44
+ end
45
+ sep_size = sep.bytesize
46
+
47
+ while true
48
+ idx = @buffer.index(sep, @ofs_head)
49
+ if idx
50
+ str = @buffer[@ofs_head, idx + sep_size]
51
+ @ofs_head += str.bytesize
52
+ return str
53
+ end
54
+
55
+ return nil if !feed
56
+ end
57
+ end
58
+ end
59
+
60
+ $machine = UringMachine.new
61
+
62
+ server_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
63
+ $machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
64
+ $machine.bind(server_fd, '127.0.0.1', 1234)
65
+ $machine.listen(server_fd, UM::SOMAXCONN)
66
+ puts 'Listening on port 1234'
67
+
68
+ def handle_connection(fd)
69
+ stream = UM::Stream.new($machine, fd)
70
+
71
+ while (l = stream.gets)
72
+ $machine.write(fd, "You said: #{l}")
73
+ end
74
+ rescue Exception => e
75
+ puts "Got error #{e.inspect}, closing connection"
76
+ $machine.close(fd) rescue nil
77
+ end
78
+
79
+ main = Fiber.current
80
+ trap('SIGINT') { $machine.spin { $machine.schedule(main, SystemExit.new) } }
81
+
82
+ $machine.accept_each(server_fd) do |fd|
83
+ puts "Connection accepted fd #{fd}"
84
+ $machine.spin(fd) { handle_connection(_1) }
85
+ end
data/ext/um/extconf.rb CHANGED
@@ -6,6 +6,61 @@ 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
+
9
64
  KERNEL_INFO_RE = /Linux (\d)\.(\d+)(?:\.)?((?:\d+\.?)*)(?:\-)?([\w\-]+)?/
10
65
  def get_config
11
66
  if RUBY_PLATFORM !~ /linux/
@@ -35,6 +90,8 @@ def get_config
35
90
  }
36
91
  end
37
92
 
93
+ config_ssl
94
+
38
95
  config = get_config
39
96
  puts "Building UringMachine (\n#{config.map { |(k, v)| " #{k}: #{v}\n"}.join})"
40
97
 
data/ext/um/um.c CHANGED
@@ -5,7 +5,6 @@ void um_setup(VALUE self, struct um *machine) {
5
5
  memset(machine, 0, sizeof(struct um));
6
6
 
7
7
  RB_OBJ_WRITE(self, &machine->self, self);
8
- RB_OBJ_WRITE(self, &machine->poll_fiber, Qnil);
9
8
 
10
9
  unsigned prepared_limit = 4096;
11
10
  unsigned flags = IORING_SETUP_SUBMIT_ALL | IORING_SETUP_COOP_TASKRUN;
@@ -76,7 +75,7 @@ static inline void um_process_cqe(struct um *machine, struct io_uring_cqe *cqe)
76
75
  if (unlikely((cqe->res == -ECANCELED) && (op->flags & OP_F_IGNORE_CANCELED))) return;
77
76
 
78
77
  op->flags |= OP_F_COMPLETED;
79
- if (unlikely(op->flags & OP_F_TRANSIENT))
78
+ if (op->flags & OP_F_TRANSIENT)
80
79
  um_op_transient_remove(machine, op);
81
80
 
82
81
  if (op->flags & OP_F_MULTISHOT) {
@@ -89,7 +88,8 @@ static inline void um_process_cqe(struct um *machine, struct io_uring_cqe *cqe)
89
88
  op->result.flags = cqe->flags;
90
89
  }
91
90
 
92
- um_runqueue_push(machine, op);
91
+ if (!(op->flags & OP_F_ASYNC))
92
+ um_runqueue_push(machine, op);
93
93
  }
94
94
 
95
95
  // copied from liburing/queue.c
@@ -181,7 +181,7 @@ inline VALUE um_fiber_switch(struct um *machine) {
181
181
  }
182
182
  }
183
183
 
184
- static inline void um_submit_cancel_op(struct um *machine, struct um_op *op) {
184
+ void um_submit_cancel_op(struct um *machine, struct um_op *op) {
185
185
  struct io_uring_sqe *sqe = um_get_sqe(machine, NULL);
186
186
  io_uring_prep_cancel64(sqe, (long long)op, 0);
187
187
  }
@@ -261,7 +261,7 @@ VALUE um_timeout(struct um *machine, VALUE interval, VALUE class) {
261
261
  static ID ID_new = 0;
262
262
  if (!ID_new) ID_new = rb_intern("new");
263
263
 
264
- struct um_op *op = malloc(sizeof(struct um_op));
264
+ struct um_op *op = um_op_alloc(machine);
265
265
  um_prep_op(machine, op, OP_TIMEOUT);
266
266
  op->ts = um_double_to_timespec(NUM2DBL(interval));
267
267
  RB_OBJ_WRITE(machine->self, &op->fiber, rb_fiber_current());
data/ext/um/um.h CHANGED
@@ -1,7 +1,7 @@
1
1
  #ifndef UM_H
2
2
  #define UM_H
3
3
 
4
- #include "ruby.h"
4
+ #include <ruby.h>
5
5
  #include <liburing.h>
6
6
 
7
7
  // debugging
@@ -48,8 +48,9 @@ enum op_kind {
48
48
 
49
49
  #define OP_F_COMPLETED (1U << 0)
50
50
  #define OP_F_TRANSIENT (1U << 1)
51
- #define OP_F_IGNORE_CANCELED (1U << 2)
52
- #define OP_F_MULTISHOT (1U << 3)
51
+ #define OP_F_ASYNC (1U << 2)
52
+ #define OP_F_IGNORE_CANCELED (1U << 3)
53
+ #define OP_F_MULTISHOT (1U << 4)
53
54
 
54
55
  struct um_op_result {
55
56
  __s32 res;
@@ -66,6 +67,7 @@ struct um_op {
66
67
 
67
68
  VALUE fiber;
68
69
  VALUE value;
70
+ VALUE async_op;
69
71
 
70
72
  struct um_op_result result;
71
73
  struct um_op_result *multishot_result_tail;
@@ -93,7 +95,6 @@ struct buf_ring_descriptor {
93
95
 
94
96
  struct um {
95
97
  VALUE self;
96
- VALUE poll_fiber;
97
98
 
98
99
  struct um_buffer *buffer_freelist;
99
100
 
@@ -137,10 +138,19 @@ struct um_queue {
137
138
  uint32_t count;
138
139
  };
139
140
 
141
+ struct um_async_op {
142
+ VALUE self;
143
+
144
+ struct um *machine;
145
+ struct um_op *op;
146
+ };
147
+
140
148
  extern VALUE cUM;
141
149
  extern VALUE cMutex;
142
150
  extern VALUE cQueue;
151
+ extern VALUE cAsyncOp;
143
152
 
153
+ struct um *um_get_machine(VALUE self);
144
154
  void um_setup(VALUE self, struct um *machine);
145
155
  void um_teardown(struct um *machine);
146
156
 
@@ -179,6 +189,7 @@ struct io_uring_sqe *um_get_sqe(struct um *machine, struct um_op *op);
179
189
 
180
190
  VALUE um_fiber_switch(struct um *machine);
181
191
  VALUE um_await(struct um *machine);
192
+ void um_submit_cancel_op(struct um *machine, struct um_op *op);
182
193
  void um_cancel_and_wait(struct um *machine, struct um_op *op);
183
194
  int um_check_completion(struct um *machine, struct um_op *op);
184
195
 
@@ -207,6 +218,12 @@ VALUE um_listen(struct um *machine, int fd, int backlog);
207
218
  VALUE um_getsockopt(struct um *machine, int fd, int level, int opt);
208
219
  VALUE um_setsockopt(struct um *machine, int fd, int level, int opt, int value);
209
220
 
221
+ void um_async_op_set(VALUE self, struct um *machine, struct um_op *op);
222
+ VALUE um_async_op_await(struct um_async_op *async_op);
223
+ void um_async_op_cancel(struct um_async_op *async_op);
224
+
225
+ VALUE um_prep_timeout(struct um *machine, double interval);
226
+
210
227
  struct um_mutex *Mutex_data(VALUE self);
211
228
  struct um_queue *Queue_data(VALUE self);
212
229
 
@@ -224,4 +241,6 @@ VALUE um_queue_shift(struct um *machine, struct um_queue *queue);
224
241
 
225
242
  void um_define_net_constants(VALUE mod);
226
243
 
244
+ // void Init_micro_ssl(VALUE mod);
245
+
227
246
  #endif // UM_H
@@ -0,0 +1,40 @@
1
+ #include "um.h"
2
+ #include <stdlib.h>
3
+
4
+ VALUE um_prep_timeout(struct um *machine, double interval) {
5
+ static ID ID_new = 0;
6
+ if (!ID_new) ID_new = rb_intern("new");
7
+
8
+ struct um_op *op = malloc(sizeof(struct um_op));
9
+ um_prep_op(machine, op, OP_TIMEOUT);
10
+ op->ts = um_double_to_timespec(interval);
11
+ op->flags = OP_F_TRANSIENT | OP_F_ASYNC;
12
+
13
+ VALUE obj = rb_funcall(cAsyncOp, rb_intern_const("new"), 0);
14
+ um_async_op_set(obj, machine, op);
15
+
16
+ RB_OBJ_WRITE(machine->self, &op->async_op, obj);
17
+
18
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
19
+ io_uring_prep_timeout(sqe, &op->ts, 0, 0);
20
+
21
+ um_op_transient_add(machine, op);
22
+
23
+ return obj;
24
+ }
25
+
26
+ VALUE um_async_op_await(struct um_async_op *async_op) {
27
+ RB_OBJ_WRITE(async_op->machine->self, &async_op->op->fiber, rb_fiber_current());
28
+ async_op->op->flags &= ~OP_F_ASYNC;
29
+
30
+ VALUE ret = um_fiber_switch(async_op->machine);
31
+ if (!um_op_completed_p(async_op->op))
32
+ um_cancel_and_wait(async_op->machine, async_op->op);
33
+
34
+ raise_if_exception(ret);
35
+ return INT2NUM(async_op->op->result.res);
36
+ }
37
+
38
+ void um_async_op_cancel(struct um_async_op *async_op) {
39
+ um_submit_cancel_op(async_op->machine, async_op->op);
40
+ }
@@ -0,0 +1,136 @@
1
+ #include "um.h"
2
+ #include <stdlib.h>
3
+
4
+ VALUE cAsyncOp;
5
+
6
+ VALUE SYM_timeout;
7
+
8
+ static void AsyncOp_mark(void *ptr) {
9
+ struct um_async_op *async_op = ptr;
10
+ rb_gc_mark_movable(async_op->self);
11
+ rb_gc_mark_movable(async_op->machine->self);
12
+ }
13
+
14
+ static void AsyncOp_compact(void *ptr) {
15
+ struct um_async_op *async_op = ptr;
16
+ async_op->self = rb_gc_location(async_op->self);
17
+ }
18
+
19
+ static size_t AsyncOp_size(const void *ptr) {
20
+ return sizeof(struct um_async_op);
21
+ }
22
+
23
+ static void AsyncOp_free(void *ptr) {
24
+ struct um_async_op *async_op = ptr;
25
+ um_op_free(async_op->machine, async_op->op);
26
+ free(ptr);
27
+ }
28
+
29
+ static const rb_data_type_t AsyncOp_type = {
30
+ "UringMachine::AsyncOp",
31
+ {AsyncOp_mark, AsyncOp_free, AsyncOp_size, AsyncOp_compact},
32
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
33
+ };
34
+
35
+ static VALUE AsyncOp_allocate(VALUE klass) {
36
+ struct um_async_op *async_op = malloc(sizeof(struct um_async_op));
37
+ return TypedData_Wrap_Struct(klass, &AsyncOp_type, async_op);
38
+ }
39
+
40
+ inline struct um_async_op *AsyncOp_data(VALUE self) {
41
+ return RTYPEDDATA_DATA(self);
42
+ }
43
+
44
+ VALUE AsyncOp_initialize(VALUE self) {
45
+ struct um_async_op *async_op = AsyncOp_data(self);
46
+ memset(async_op, 0, sizeof(struct um_async_op));
47
+ async_op->self = self;
48
+ return self;
49
+ }
50
+
51
+ void um_async_op_set(VALUE self, struct um *machine, struct um_op *op) {
52
+ struct um_async_op *async_op = AsyncOp_data(self);
53
+ async_op->machine = machine;
54
+ async_op->op = op;
55
+ }
56
+
57
+ inline void raise_on_missing_op(struct um_async_op *async_op) {
58
+ if (!async_op->op)
59
+ rb_raise(rb_eRuntimeError, "Missing op");
60
+ }
61
+
62
+ inline int async_op_is_done(struct um_async_op *async_op) {
63
+ return (async_op->op->flags & OP_F_COMPLETED);
64
+ }
65
+
66
+ VALUE AsyncOp_kind(VALUE self) {
67
+ struct um_async_op *async_op = AsyncOp_data(self);
68
+ raise_on_missing_op(async_op);
69
+
70
+ switch(async_op->op->kind) {
71
+ case OP_TIMEOUT:
72
+ return SYM_timeout;
73
+ default:
74
+ rb_raise(rb_eRuntimeError, "Invalid op kind");
75
+ }
76
+ }
77
+
78
+ VALUE AsyncOp_done_p(VALUE self) {
79
+ struct um_async_op *async_op = AsyncOp_data(self);
80
+ raise_on_missing_op(async_op);
81
+
82
+ return async_op_is_done(async_op) ? Qtrue : Qfalse;
83
+ }
84
+
85
+ VALUE AsyncOp_result(VALUE self) {
86
+ struct um_async_op *async_op = AsyncOp_data(self);
87
+ raise_on_missing_op(async_op);
88
+
89
+ return async_op_is_done(async_op) ? INT2NUM(async_op->op->result.res) : Qnil;
90
+ }
91
+
92
+ VALUE AsyncOp_cancelled_p(VALUE self) {
93
+ struct um_async_op *async_op = AsyncOp_data(self);
94
+ raise_on_missing_op(async_op);
95
+
96
+ if (!async_op_is_done(async_op)) return Qnil;
97
+
98
+ return (async_op->op->result.res == -ECANCELED) ? Qtrue : Qfalse;
99
+ }
100
+
101
+ VALUE AsyncOp_await(VALUE self) {
102
+ struct um_async_op *async_op = AsyncOp_data(self);
103
+ raise_on_missing_op(async_op);
104
+
105
+ if (async_op_is_done(async_op))
106
+ return INT2NUM(async_op->op->result.res);
107
+
108
+ return um_async_op_await(async_op);
109
+ }
110
+
111
+ VALUE AsyncOp_cancel(VALUE self) {
112
+ struct um_async_op *async_op = AsyncOp_data(self);
113
+ raise_on_missing_op(async_op);
114
+
115
+ if (!async_op_is_done(async_op))
116
+ um_async_op_cancel(async_op);
117
+
118
+ return self;
119
+ }
120
+
121
+ void Init_AsyncOp(void) {
122
+ cAsyncOp = rb_define_class_under(cUM, "AsyncOp", rb_cObject);
123
+ rb_define_alloc_func(cAsyncOp, AsyncOp_allocate);
124
+
125
+ rb_define_method(cAsyncOp, "initialize", AsyncOp_initialize, 0);
126
+ rb_define_method(cAsyncOp, "kind", AsyncOp_kind, 0);
127
+ rb_define_method(cAsyncOp, "done?", AsyncOp_done_p, 0);
128
+ rb_define_method(cAsyncOp, "result", AsyncOp_result, 0);
129
+ rb_define_method(cAsyncOp, "cancelled?", AsyncOp_cancelled_p, 0);
130
+
131
+ rb_define_method(cAsyncOp, "await", AsyncOp_await, 0);
132
+ rb_define_method(cAsyncOp, "join", AsyncOp_await, 0);
133
+ rb_define_method(cAsyncOp, "cancel", AsyncOp_cancel, 0);
134
+
135
+ SYM_timeout = ID2SYM(rb_intern("timeout"));
136
+ }