polyphony 0.43.6 → 0.43.8

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: b048cfa1e0d7cd542840ab9f2198349b2474d40a6d943b584d1bcb57593ca41d
4
- data.tar.gz: 0d9ed9fc45ec2165018e61f8befbdefb5668cfcf4b7628a648a055f5b6052e4c
3
+ metadata.gz: bea457e28d23f96570d448855d00cf76250a55fcb02e12c4305cb551cb55faf4
4
+ data.tar.gz: dc97409e61ce82c20eef25101a2c53046635461ef81302e54b121e5bb9c25aa1
5
5
  SHA512:
6
- metadata.gz: a962cb0db032fd58a8db4024d9a6ad2d1be5307caff72d9508bdf879bafbd6c55a5c79e9bc69a9d3bda96d2f72fda51ed2f978e96fa0ca636458284e9e9dbbaf
7
- data.tar.gz: 2b419313309e898003399f780abcf7641de34c3350ae5d7c10c785209e3e6c12e4d741b01676a49ec1ec2f3f15ea8f9001636ec44880e13583d68168f82c9cb7
6
+ metadata.gz: 53d345ee472bc77fc993880a1a725064bb934ec2789fa72bd97ec07b558942369860fb7077e0fcff6c95cfcff0c0d712090f70b1d9ff7f652160d15ddfc77de4
7
+ data.tar.gz: 513a79eeb8a7766078d159cf85d367a6f3f50a0870825275408081d9cfe4e446e7f0b0120bd712f6281ccd0787779ec464b20c9e17c3c60178ceb39c061033c7
@@ -1,3 +1,17 @@
1
+ ## 0.43.8 2020-07-21
2
+
3
+ * Rename `LibevQueue` to `Queue`
4
+ * Reimplement Event using `Agent#wait_event`
5
+ * Improve Queue shift queue performance
6
+ * Introduce `Agent#wait_event` API for waiting on asynchronous events
7
+ * Minimize `fcntl` syscalls in IO operations
8
+
9
+ ## 0.43.7 2020-07-20
10
+
11
+ * Fix memory leak in ResourcePool (#31)
12
+ * Check and adjust file position before reading (#30)
13
+ * Minor documentation fixes
14
+
1
15
  ## 0.43.6 2020-07-18
2
16
 
3
17
  * Allow brute-force interrupting with second Ctrl-C
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.43.6)
4
+ polyphony (0.43.8)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/TODO.md CHANGED
@@ -1,6 +1,5 @@
1
- ## 0.43
2
-
3
- - Reimplement ResourcePool, Channel, Mutex using LibevQueue
1
+ - Implement `LibevAgent#connect` API
2
+ - Reimplement ResourcePool, Channel, Mutex using Queue
4
3
  -- Add `Fiber#schedule_with_priority` method, aliased by `Fiber#wakeup`
5
4
  - Implement agent interface is virtual function table
6
5
  - Implement proxy agent for plugging in a user-provided agent class
@@ -113,8 +113,8 @@ active concurrent connections, each advancing at its own pace, consuming only a
113
113
  single CPU core.
114
114
 
115
115
  Nevertheless, Polyphony fully supports multithreading, with each thread having
116
- its own fiber run queue and its own libev event loop. In addition, Polyphony
117
- enables cross-thread communication using
116
+ its own fiber run queue and its own libev event loop. Polyphony even enables
117
+ cross-thread communication using [fiber messaging](#message-passing).
118
118
 
119
119
  ## Fibers vs Callbacks
120
120
 
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fiber'
4
+
5
+ class Fiber
6
+ attr_accessor :next
7
+ end
8
+
9
+ # This program shows how the performance
10
+
11
+ def run(num_fibers)
12
+ count = 0
13
+
14
+ GC.disable
15
+
16
+ first = nil
17
+ last = nil
18
+ supervisor = Fiber.current
19
+ num_fibers.times do
20
+ fiber = Fiber.new do
21
+ loop do
22
+ count += 1
23
+ if count == 1_000_000
24
+ supervisor.transfer
25
+ else
26
+ Fiber.current.next.transfer
27
+ end
28
+ end
29
+ end
30
+ first ||= fiber
31
+ last.next = fiber if last
32
+ last = fiber
33
+ end
34
+
35
+ last.next = first
36
+
37
+ t0 = Time.now
38
+ first.transfer
39
+ elapsed = Time.now - t0
40
+
41
+ puts "fibers: #{num_fibers} count: #{count} rate: #{count / elapsed}"
42
+ GC.start
43
+ end
44
+
45
+ run(100)
46
+ run(1000)
47
+ run(10000)
@@ -1,6 +1,8 @@
1
1
  #include <netdb.h>
2
2
  #include <sys/socket.h>
3
3
  #include <sys/uio.h>
4
+ #include <unistd.h>
5
+ #include <fcntl.h>
4
6
 
5
7
  #include "polyphony.h"
6
8
  #include "../libev/ev.h"
@@ -131,7 +133,7 @@ VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue
131
133
  GetLibevAgent(self, agent);
132
134
 
133
135
  if (is_nowait) {
134
- long runnable_count = LibevQueue_len(queue);
136
+ long runnable_count = Queue_len(queue);
135
137
  agent->run_no_wait_count++;
136
138
  if (agent->run_no_wait_count < runnable_count || agent->run_no_wait_count < 10)
137
139
  return self;
@@ -278,6 +280,31 @@ VALUE libev_snooze() {
278
280
  return Thread_switch_fiber(rb_thread_current());
279
281
  }
280
282
 
283
+ ID ID_ivar_is_nonblocking;
284
+
285
+ // Since we need to ensure that fd's are non-blocking before every I/O
286
+ // operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
287
+ // "nonblock" state in an instance variable. Calling rb_ivar_get on every read
288
+ // is still much cheaper than doing a fcntl syscall on every read! Preliminary
289
+ // benchmarks (with a "hello world" HTTP server) show throughput is improved
290
+ // by 10-13%.
291
+ inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
292
+ #ifdef _WIN32
293
+ return rb_w32_set_nonblock(fptr->fd);
294
+ #elif defined(F_GETFL)
295
+ VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
296
+ if (is_nonblocking == Qnil) {
297
+ rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
298
+ int oflags = fcntl(fptr->fd, F_GETFL);
299
+ if (oflags == -1) return;
300
+ if (oflags & O_NONBLOCK) return;
301
+ oflags |= O_NONBLOCK;
302
+ fcntl(fptr->fd, F_SETFL, oflags);
303
+ }
304
+ #endif
305
+ return;
306
+ }
307
+
281
308
  VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
282
309
  struct LibevAgent_t *agent;
283
310
  struct libev_io watcher;
@@ -295,11 +322,20 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
295
322
  if (underlying_io != Qnil) io = underlying_io;
296
323
  GetOpenFile(io, fptr);
297
324
  rb_io_check_byte_readable(fptr);
298
- rb_io_set_nonblock(fptr);
325
+ io_set_nonblock(fptr, io);
299
326
  watcher.fiber = Qnil;
300
327
 
301
328
  OBJ_TAINT(str);
302
329
 
330
+ // Apparently after reopening a closed file, the file position is not reset,
331
+ // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
332
+ // find out if that's the case.
333
+ // See: https://github.com/digital-fabric/polyphony/issues/30
334
+ if (fptr->rbuf.len > 0) {
335
+ lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
336
+ fptr->rbuf.len = 0;
337
+ }
338
+
303
339
  while (1) {
304
340
  ssize_t n = read(fptr->fd, buf, len - total);
305
341
  if (n < 0) {
@@ -349,6 +385,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
349
385
  shrinkable = io_setstrbuf(&str, len); \
350
386
  buf = RSTRING_PTR(str); \
351
387
  total = 0; \
388
+ OBJ_TAINT(str); \
352
389
  }
353
390
 
354
391
  #define YIELD_STR() { \
@@ -375,10 +412,17 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
375
412
  if (underlying_io != Qnil) io = underlying_io;
376
413
  GetOpenFile(io, fptr);
377
414
  rb_io_check_byte_readable(fptr);
378
- rb_io_set_nonblock(fptr);
415
+ io_set_nonblock(fptr, io);
379
416
  watcher.fiber = Qnil;
380
417
 
381
- OBJ_TAINT(str);
418
+ // Apparently after reopening a closed file, the file position is not reset,
419
+ // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
420
+ // find out if that's the case.
421
+ // See: https://github.com/digital-fabric/polyphony/issues/30
422
+ if (fptr->rbuf.len > 0) {
423
+ lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
424
+ fptr->rbuf.len = 0;
425
+ }
382
426
 
383
427
  while (1) {
384
428
  ssize_t n = read(fptr->fd, buf, len);
@@ -553,7 +597,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
553
597
 
554
598
  GetLibevAgent(self, agent);
555
599
  GetOpenFile(sock, fptr);
556
- rb_io_set_nonblock(fptr);
600
+ io_set_nonblock(fptr, sock);
557
601
  watcher.fiber = Qnil;
558
602
  while (1) {
559
603
  fd = accept(fptr->fd, &addr, &len);
@@ -579,7 +623,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
579
623
  fp->fd = fd;
580
624
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
581
625
  rb_io_ascii8bit_binmode(socket);
582
- rb_io_set_nonblock(fp);
626
+ io_set_nonblock(fp, socket);
583
627
  rb_io_synchronized(fp);
584
628
 
585
629
  // if (rsock_do_not_reverse_lookup) {
@@ -608,7 +652,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
608
652
 
609
653
  GetLibevAgent(self, agent);
610
654
  GetOpenFile(sock, fptr);
611
- rb_io_set_nonblock(fptr);
655
+ io_set_nonblock(fptr, sock);
612
656
  watcher.fiber = Qnil;
613
657
 
614
658
  while (1) {
@@ -634,7 +678,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
634
678
  fp->fd = fd;
635
679
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
636
680
  rb_io_ascii8bit_binmode(socket);
637
- rb_io_set_nonblock(fp);
681
+ io_set_nonblock(fp, socket);
638
682
  rb_io_synchronized(fp);
639
683
 
640
684
  rb_yield(socket);
@@ -662,7 +706,7 @@ error:
662
706
 
663
707
  // GetLibevAgent(self, agent);
664
708
  // GetOpenFile(sock, fptr);
665
- // rb_io_set_nonblock(fptr);
709
+ // io_set_nonblock(fptr, sock);
666
710
  // watcher.fiber = Qnil;
667
711
 
668
712
  // addr.sin_family = AF_INET;
@@ -737,6 +781,7 @@ VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
737
781
  ev_timer_start(agent->ev_loop, &watcher.timer);
738
782
 
739
783
  switchpoint_result = libev_await(agent);
784
+
740
785
  ev_timer_stop(agent->ev_loop, &watcher.timer);
741
786
 
742
787
  TEST_RESUME_EXCEPTION(switchpoint_result);
@@ -785,6 +830,25 @@ struct ev_loop *LibevAgent_ev_loop(VALUE self) {
785
830
  return agent->ev_loop;
786
831
  }
787
832
 
833
+ void LibevAgent_async_callback(EV_P_ ev_async *w, int revents) { }
834
+
835
+ VALUE LibevAgent_wait_event(VALUE self, VALUE raise) {
836
+ struct LibevAgent_t *agent;
837
+ struct ev_async async;
838
+ VALUE switchpoint_result = Qnil;
839
+ GetLibevAgent(self, agent);
840
+
841
+ ev_async_init(&async, LibevAgent_async_callback);
842
+ ev_async_start(agent->ev_loop, &async);
843
+
844
+ switchpoint_result = libev_await(agent);
845
+ ev_async_stop(agent->ev_loop, &async);
846
+
847
+ if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
848
+ RB_GC_GUARD(switchpoint_result);
849
+ return switchpoint_result;
850
+ }
851
+
788
852
  void Init_LibevAgent() {
789
853
  rb_require("socket");
790
854
  cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
@@ -812,4 +876,7 @@ void Init_LibevAgent() {
812
876
  rb_define_method(cLibevAgent, "wait_io", LibevAgent_wait_io, 2);
813
877
  rb_define_method(cLibevAgent, "sleep", LibevAgent_sleep, 1);
814
878
  rb_define_method(cLibevAgent, "waitpid", LibevAgent_waitpid, 1);
879
+ rb_define_method(cLibevAgent, "wait_event", LibevAgent_wait_event, 1);
880
+
881
+ ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
815
882
  }
@@ -2,7 +2,6 @@
2
2
 
3
3
  VALUE mPolyphony;
4
4
 
5
- ID ID_await_no_raise;
6
5
  ID ID_call;
7
6
  ID ID_caller;
8
7
  ID ID_clear;
@@ -54,7 +53,6 @@ void Init_Polyphony() {
54
53
  rb_define_global_function("snooze", Polyphony_snooze, 0);
55
54
  rb_define_global_function("suspend", Polyphony_suspend, 0);
56
55
 
57
- ID_await_no_raise = rb_intern("await_no_raise");
58
56
  ID_call = rb_intern("call");
59
57
  ID_caller = rb_intern("caller");
60
58
  ID_clear = rb_intern("clear");
@@ -19,10 +19,9 @@
19
19
  }
20
20
 
21
21
  extern VALUE mPolyphony;
22
- extern VALUE cLibevQueue;
22
+ extern VALUE cQueue;
23
23
  extern VALUE cEvent;
24
24
 
25
- extern ID ID_await_no_raise;
26
25
  extern ID ID_call;
27
26
  extern ID ID_caller;
28
27
  extern ID ID_clear;
@@ -75,15 +74,16 @@ VALUE LibevAgent_ref(VALUE self);
75
74
  VALUE LibevAgent_unref(VALUE self);
76
75
  int LibevAgent_ref_count(VALUE self);
77
76
  void LibevAgent_reset_ref_count(VALUE self);
78
-
79
- VALUE LibevQueue_push(VALUE self, VALUE value);
80
- VALUE LibevQueue_unshift(VALUE self, VALUE value);
81
- VALUE LibevQueue_shift(VALUE self);
82
- VALUE LibevQueue_shift_no_wait(VALUE self);
83
- VALUE LibevQueue_clear(VALUE self);
84
- VALUE LibevQueue_delete(VALUE self, VALUE value);
85
- long LibevQueue_len(VALUE self);
86
- void LibevQueue_trace(VALUE self);
77
+ VALUE LibevAgent_wait_event(VALUE self, VALUE raise);
78
+
79
+ VALUE Queue_push(VALUE self, VALUE value);
80
+ VALUE Queue_unshift(VALUE self, VALUE value);
81
+ VALUE Queue_shift(VALUE self);
82
+ VALUE Queue_shift_no_wait(VALUE self);
83
+ VALUE Queue_clear(VALUE self);
84
+ VALUE Queue_delete(VALUE self, VALUE value);
85
+ long Queue_len(VALUE self);
86
+ void Queue_trace(VALUE self);
87
87
 
88
88
  VALUE Polyphony_snooze(VALUE self);
89
89
 
@@ -3,7 +3,7 @@
3
3
  void Init_Fiber();
4
4
  void Init_Polyphony();
5
5
  void Init_LibevAgent();
6
- void Init_LibevQueue();
6
+ void Init_Queue();
7
7
  void Init_Thread();
8
8
  void Init_Tracing();
9
9
 
@@ -12,7 +12,7 @@ void Init_polyphony_ext() {
12
12
 
13
13
  Init_Polyphony();
14
14
  Init_LibevAgent();
15
- Init_LibevQueue();
15
+ Init_Queue();
16
16
 
17
17
  Init_Fiber();
18
18
  Init_Thread();
@@ -0,0 +1,168 @@
1
+ #include "polyphony.h"
2
+ #include "ring_buffer.h"
3
+
4
+ typedef struct queue {
5
+ ring_buffer values;
6
+ ring_buffer shift_queue;
7
+ } Queue_t;
8
+
9
+ VALUE cQueue = Qnil;
10
+
11
+ static void Queue_mark(void *ptr) {
12
+ Queue_t *queue = ptr;
13
+ ring_buffer_mark(&queue->values);
14
+ ring_buffer_mark(&queue->shift_queue);
15
+ }
16
+
17
+ static void Queue_free(void *ptr) {
18
+ Queue_t *queue = ptr;
19
+ ring_buffer_free(&queue->values);
20
+ ring_buffer_free(&queue->shift_queue);
21
+ xfree(ptr);
22
+ }
23
+
24
+ static size_t Queue_size(const void *ptr) {
25
+ return sizeof(Queue_t);
26
+ }
27
+
28
+ static const rb_data_type_t Queue_type = {
29
+ "Queue",
30
+ {Queue_mark, Queue_free, Queue_size,},
31
+ 0, 0, 0
32
+ };
33
+
34
+ static VALUE Queue_allocate(VALUE klass) {
35
+ Queue_t *queue;
36
+
37
+ queue = ALLOC(Queue_t);
38
+ return TypedData_Wrap_Struct(klass, &Queue_type, queue);
39
+ }
40
+
41
+ #define GetQueue(obj, queue) \
42
+ TypedData_Get_Struct((obj), Queue_t, &Queue_type, (queue))
43
+
44
+ static VALUE Queue_initialize(VALUE self) {
45
+ Queue_t *queue;
46
+ GetQueue(self, queue);
47
+
48
+ ring_buffer_init(&queue->values);
49
+ ring_buffer_init(&queue->shift_queue);
50
+
51
+ return self;
52
+ }
53
+
54
+ VALUE Queue_push(VALUE self, VALUE value) {
55
+ Queue_t *queue;
56
+ GetQueue(self, queue);
57
+ if (queue->shift_queue.count > 0) {
58
+ VALUE fiber = ring_buffer_shift(&queue->shift_queue);
59
+ if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
60
+ }
61
+ ring_buffer_push(&queue->values, value);
62
+ return self;
63
+ }
64
+
65
+ VALUE Queue_unshift(VALUE self, VALUE value) {
66
+ Queue_t *queue;
67
+ GetQueue(self, queue);
68
+ if (queue->shift_queue.count > 0) {
69
+ VALUE fiber = ring_buffer_shift(&queue->shift_queue);
70
+ if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
71
+ }
72
+ ring_buffer_unshift(&queue->values, value);
73
+ return self;
74
+ }
75
+
76
+ VALUE Queue_shift(VALUE self) {
77
+ Queue_t *queue;
78
+ GetQueue(self, queue);
79
+
80
+ if (queue->values.count == 0) {
81
+ VALUE agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
82
+ VALUE fiber = rb_fiber_current();
83
+ VALUE switchpoint_result = Qnil;
84
+ ring_buffer_push(&queue->shift_queue, fiber);
85
+ switchpoint_result = LibevAgent_wait_event(agent, Qnil);
86
+ if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException))) {
87
+ ring_buffer_delete(&queue->shift_queue, fiber);
88
+ return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
89
+ }
90
+ RB_GC_GUARD(agent);
91
+ RB_GC_GUARD(switchpoint_result);
92
+ }
93
+
94
+ return ring_buffer_shift(&queue->values);
95
+ }
96
+
97
+ VALUE Queue_shift_no_wait(VALUE self) {
98
+ Queue_t *queue;
99
+ GetQueue(self, queue);
100
+
101
+ return ring_buffer_shift(&queue->values);
102
+ }
103
+
104
+ VALUE Queue_delete(VALUE self, VALUE value) {
105
+ Queue_t *queue;
106
+ GetQueue(self, queue);
107
+
108
+ ring_buffer_delete(&queue->values, value);
109
+ return self;
110
+ }
111
+
112
+ VALUE Queue_clear(VALUE self) {
113
+ Queue_t *queue;
114
+ GetQueue(self, queue);
115
+
116
+ ring_buffer_clear(&queue->values);
117
+ return self;
118
+ }
119
+
120
+ long Queue_len(VALUE self) {
121
+ Queue_t *queue;
122
+ GetQueue(self, queue);
123
+
124
+ return queue->values.count;
125
+ }
126
+
127
+ VALUE Queue_shift_each(VALUE self) {
128
+ Queue_t *queue;
129
+ GetQueue(self, queue);
130
+
131
+ ring_buffer_shift_each(&queue->values);
132
+ return self;
133
+ }
134
+
135
+ VALUE Queue_shift_all(VALUE self) {
136
+ Queue_t *queue;
137
+ GetQueue(self, queue);
138
+
139
+ return ring_buffer_shift_all(&queue->values);
140
+ }
141
+
142
+ VALUE Queue_empty_p(VALUE self) {
143
+ Queue_t *queue;
144
+ GetQueue(self, queue);
145
+
146
+ return (queue->values.count == 0) ? Qtrue : Qfalse;
147
+ }
148
+
149
+ void Init_Queue() {
150
+ cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cData);
151
+ rb_define_alloc_func(cQueue, Queue_allocate);
152
+
153
+ rb_define_method(cQueue, "initialize", Queue_initialize, 0);
154
+ rb_define_method(cQueue, "push", Queue_push, 1);
155
+ rb_define_method(cQueue, "<<", Queue_push, 1);
156
+ rb_define_method(cQueue, "unshift", Queue_unshift, 1);
157
+
158
+ rb_define_method(cQueue, "shift", Queue_shift, 0);
159
+ rb_define_method(cQueue, "pop", Queue_shift, 0);
160
+ rb_define_method(cQueue, "shift_no_wait", Queue_shift_no_wait, 0);
161
+ rb_define_method(cQueue, "delete", Queue_delete, 1);
162
+
163
+ rb_define_method(cQueue, "shift_each", Queue_shift_each, 0);
164
+ rb_define_method(cQueue, "shift_all", Queue_shift_all, 0);
165
+ rb_define_method(cQueue, "empty?", Queue_empty_p, 0);
166
+ }
167
+
168
+
@@ -17,18 +17,7 @@ int ring_buffer_empty_p(ring_buffer *buffer) {
17
17
  return buffer->count == 0;
18
18
  }
19
19
 
20
- #define TRACE_RING_BUFFER(func, buffer) printf( \
21
- "%s size: %d count: %d head: %d tail: %d\n", \
22
- func, \
23
- buffer->size, \
24
- buffer->count, \
25
- buffer->head, \
26
- buffer->tail \
27
- )
28
-
29
20
  VALUE ring_buffer_shift(ring_buffer *buffer) {
30
- // TRACE_RING_BUFFER("ring_buffer_shift", buffer);
31
-
32
21
  VALUE value;
33
22
  if (buffer->count == 0) return Qnil;
34
23
 
@@ -40,11 +29,8 @@ VALUE ring_buffer_shift(ring_buffer *buffer) {
40
29
  }
41
30
 
42
31
  void ring_buffer_resize(ring_buffer *buffer) {
43
- // TRACE_RING_BUFFER("ring_buffer_resize", buffer);
44
-
45
32
  unsigned int old_size = buffer->size;
46
33
  buffer->size = old_size == 1 ? 4 : old_size * 2;
47
- // printf("new size: %d\n", buffer->size);
48
34
  buffer->entries = realloc(buffer->entries, buffer->size * sizeof(VALUE));
49
35
  for (unsigned int idx = 0; idx < buffer->head && idx < buffer->tail; idx++)
50
36
  buffer->entries[old_size + idx] = buffer->entries[idx];
@@ -52,9 +38,6 @@ void ring_buffer_resize(ring_buffer *buffer) {
52
38
  }
53
39
 
54
40
  void ring_buffer_unshift(ring_buffer *buffer, VALUE value) {
55
- // TRACE_RING_BUFFER("ring_buffer_unshift", buffer);
56
- // INSPECT(value);
57
-
58
41
  if (buffer->count == buffer->size) ring_buffer_resize(buffer);
59
42
 
60
43
  buffer->head = (buffer->head - 1) % buffer->size;
@@ -63,8 +46,6 @@ void ring_buffer_unshift(ring_buffer *buffer, VALUE value) {
63
46
  }
64
47
 
65
48
  void ring_buffer_push(ring_buffer *buffer, VALUE value) {
66
- // TRACE_RING_BUFFER("ring_buffer_push", buffer);
67
- // INSPECT(value);
68
49
  if (buffer->count == buffer->size) ring_buffer_resize(buffer);
69
50
 
70
51
  buffer->entries[buffer->tail] = value;
@@ -78,8 +59,6 @@ void ring_buffer_mark(ring_buffer *buffer) {
78
59
  }
79
60
 
80
61
  void ring_buffer_shift_each(ring_buffer *buffer) {
81
- // TRACE_RING_BUFFER("ring_buffer_shift_each", buffer);
82
-
83
62
  for (unsigned int i = 0; i < buffer->count; i++)
84
63
  rb_yield(buffer->entries[(buffer->head + i) % buffer->size]);
85
64
 
@@ -87,7 +66,6 @@ void ring_buffer_shift_each(ring_buffer *buffer) {
87
66
  }
88
67
 
89
68
  VALUE ring_buffer_shift_all(ring_buffer *buffer) {
90
- // TRACE_RING_BUFFER("ring_buffer_all", buffer);
91
69
  VALUE array = rb_ary_new_capa(buffer->count);
92
70
  for (unsigned int i = 0; i < buffer->count; i++)
93
71
  rb_ary_push(array, buffer->entries[(buffer->head + i) % buffer->size]);
@@ -104,7 +82,6 @@ void ring_buffer_delete_at(ring_buffer *buffer, unsigned int idx) {
104
82
  }
105
83
 
106
84
  void ring_buffer_delete(ring_buffer *buffer, VALUE value) {
107
- // TRACE_RING_BUFFER("ring_buffer_delete", buffer);
108
85
  for (unsigned int i = 0; i < buffer->count; i++) {
109
86
  unsigned int idx = (buffer->head + i) % buffer->size;
110
87
  if (buffer->entries[idx] == value) {
@@ -115,6 +92,5 @@ void ring_buffer_delete(ring_buffer *buffer, VALUE value) {
115
92
  }
116
93
 
117
94
  void ring_buffer_clear(ring_buffer *buffer) {
118
- // TRACE_RING_BUFFER("ring_buffer_clear", buffer);
119
95
  buffer->count = buffer->head = buffer->tail = 0;
120
96
  }
@@ -11,7 +11,7 @@ ID ID_runnable_next;
11
11
  ID ID_stop;
12
12
 
13
13
  static VALUE Thread_setup_fiber_scheduling(VALUE self) {
14
- VALUE queue = rb_funcall(cLibevQueue, ID_new, 0);
14
+ VALUE queue = rb_funcall(cQueue, ID_new, 0);
15
15
 
16
16
  rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
17
17
  rb_ivar_set(self, ID_run_queue, queue);
@@ -60,7 +60,7 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
60
60
  }
61
61
 
62
62
  queue = rb_ivar_get(self, ID_run_queue);
63
- LibevQueue_push(queue, fiber);
63
+ Queue_push(queue, fiber);
64
64
  rb_ivar_set(fiber, ID_runnable, Qtrue);
65
65
 
66
66
  if (rb_thread_current() != self) {
@@ -87,13 +87,13 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
87
87
 
88
88
  // if fiber is already scheduled, remove it from the run queue
89
89
  if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
90
- LibevQueue_delete(queue, fiber);
90
+ Queue_delete(queue, fiber);
91
91
  } else {
92
92
  rb_ivar_set(fiber, ID_runnable, Qtrue);
93
93
  }
94
94
 
95
95
  // the fiber is given priority by putting it at the front of the run queue
96
- LibevQueue_unshift(queue, fiber);
96
+ Queue_unshift(queue, fiber);
97
97
 
98
98
  if (rb_thread_current() != self) {
99
99
  // if the fiber scheduling is done across threads, we need to make sure the
@@ -114,6 +114,7 @@ VALUE Thread_switch_fiber(VALUE self) {
114
114
  VALUE value;
115
115
  VALUE agent = rb_ivar_get(self, ID_ivar_agent);
116
116
  int ref_count;
117
+ int agent_was_polled = 0;1;
117
118
 
118
119
  if (__tracing_enabled__) {
119
120
  if (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse) {
@@ -123,9 +124,9 @@ VALUE Thread_switch_fiber(VALUE self) {
123
124
 
124
125
  ref_count = LibevAgent_ref_count(agent);
125
126
  while (1) {
126
- next_fiber = LibevQueue_shift_no_wait(queue);
127
+ next_fiber = Queue_shift_no_wait(queue);
127
128
  if (next_fiber != Qnil) {
128
- if (ref_count > 0) {
129
+ if (agent_was_polled == 0 && ref_count > 0) {
129
130
  // this mechanism prevents event starvation in case the run queue never
130
131
  // empties
131
132
  LibevAgent_poll(agent, Qtrue, current_fiber, queue);
@@ -135,6 +136,7 @@ VALUE Thread_switch_fiber(VALUE self) {
135
136
  if (ref_count == 0) break;
136
137
 
137
138
  LibevAgent_poll(agent, Qnil, current_fiber, queue);
139
+ agent_was_polled = 1;
138
140
  }
139
141
 
140
142
  if (next_fiber == Qnil) return Qnil;
@@ -152,13 +154,13 @@ VALUE Thread_switch_fiber(VALUE self) {
152
154
 
153
155
  VALUE Thread_run_queue_trace(VALUE self) {
154
156
  VALUE queue = rb_ivar_get(self, ID_run_queue);
155
- LibevQueue_trace(queue);
157
+ Queue_trace(queue);
156
158
  return self;
157
159
  }
158
160
 
159
161
  VALUE Thread_reset_fiber_scheduling(VALUE self) {
160
162
  VALUE queue = rb_ivar_get(self, ID_run_queue);
161
- LibevQueue_clear(queue);
163
+ Queue_clear(queue);
162
164
  Thread_fiber_reset_ref_count(self);
163
165
  return self;
164
166
  }
@@ -4,9 +4,6 @@ require 'fiber'
4
4
  require_relative './polyphony_ext'
5
5
 
6
6
  module Polyphony
7
- # Map Queue to Libev queue implementation
8
- Queue = LibevQueue
9
-
10
7
  # replace core Queue class with our own
11
8
  verbose = $VERBOSE
12
9
  $VERBOSE = nil
@@ -36,7 +36,7 @@ module Polyphony
36
36
  @acquired_resources[fiber] = resource
37
37
  yield resource
38
38
  ensure
39
- @acquired_resources[fiber] = nil
39
+ @acquired_resources.delete fiber
40
40
  Thread.current.agent.unref
41
41
  release(resource) if resource
42
42
  end
@@ -3,25 +3,15 @@
3
3
  module Polyphony
4
4
  # Event watcher for thread-safe synchronisation
5
5
  class Event
6
- def initialize
7
- @i, @o = IO.pipe
8
- end
9
-
10
6
  def await
11
- Thread.current.agent.read(@i, +'', 8192, false)
12
- raise @value if @value.is_a?(Exception)
13
-
14
- @value
15
- end
16
-
17
- def await_no_raise
18
- Thread.current.agent.read(@i, +'', 8192, false)
19
- @value
7
+ @fiber = Fiber.current
8
+ Thread.current.agent.wait_event(true)
20
9
  end
21
10
 
22
11
  def signal(value = nil)
23
- @value = value
24
- Thread.current.agent.write(@o, '1')
12
+ @fiber&.schedule(value)
13
+ ensure
14
+ @fiber = nil
25
15
  end
26
16
  end
27
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.43.6'
4
+ VERSION = '0.43.8'
5
5
  end
@@ -32,7 +32,7 @@ class MiniTest::Test
32
32
  Fiber.current.setup_main_fiber
33
33
  Fiber.current.instance_variable_set(:@auto_watcher, nil)
34
34
  Thread.current.agent = Polyphony::LibevAgent.new
35
- sleep 0
35
+ sleep 0 # apparently this helps with timer accuracy
36
36
  end
37
37
 
38
38
  def teardown
@@ -97,7 +97,7 @@ class AgentTest < MiniTest::Test
97
97
  o.close
98
98
 
99
99
  # read_loop will snooze after every read
100
- 4.times { snooze }
100
+ 6.times { snooze }
101
101
 
102
102
  assert_equal [:ready, 'foo', 'bar', :done], buf
103
103
  end
@@ -45,4 +45,15 @@ class EventTest < MiniTest::Test
45
45
  t&.kill
46
46
  t&.join
47
47
  end
48
+
49
+ def test_exception_while_waiting_for_event
50
+ e = Polyphony::Event.new
51
+
52
+ f = spin { e.await }
53
+ g = spin { f.raise 'foo' }
54
+
55
+ assert_raises(RuntimeError) do
56
+ f.await
57
+ end
58
+ end
48
59
  end
@@ -91,6 +91,20 @@ class IOTest < MiniTest::Test
91
91
 
92
92
  assert_raises(EOFError) { i.readpartial(1) }
93
93
  end
94
+
95
+ # see https://github.com/digital-fabric/polyphony/issues/30
96
+ def test_reopened_tempfile
97
+ file = Tempfile.new
98
+ file << 'hello: world'
99
+ file.close
100
+
101
+ buf = nil
102
+ File.open(file, 'r:bom|utf-8') do |f|
103
+ buf = f.read(16384)
104
+ end
105
+
106
+ assert_equal 'hello: world', buf
107
+ end
94
108
  end
95
109
 
96
110
  class IOClassMethodsTest < MiniTest::Test
@@ -96,4 +96,17 @@ class QueueTest < MiniTest::Test
96
96
  assert_nil f2.await
97
97
  assert_equal :bar, f3.await
98
98
  end
99
+
100
+ def test_fiber_removal_from_queue_simple
101
+ f1 = spin { @queue.shift }
102
+
103
+ # let fibers run
104
+ snooze
105
+
106
+ f1.stop
107
+ snooze
108
+
109
+ @queue << :foo
110
+ assert_nil f1.await
111
+ end
99
112
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'helper'
4
- require 'localhost/authority'
5
4
 
6
5
  class SocketTest < MiniTest::Test
7
6
  def setup
@@ -32,46 +31,4 @@ class SocketTest < MiniTest::Test
32
31
  server_fiber&.await
33
32
  server&.close
34
33
  end
35
-
36
- def test_openssl
37
- authority = Localhost::Authority.fetch
38
- context = authority.server_context
39
- port = rand(1234..5678)
40
- sock = TCPServer.new('127.0.0.1', port)
41
- server = OpenSSL::SSL::SSLServer.new(sock, context)
42
-
43
- server_fiber = spin do
44
- while (socket = server.accept)
45
- puts "accepted: #{socket.inspect}"
46
- spin do
47
- while (data = socket.gets(8192))
48
- socket << data
49
- end
50
- end
51
- end
52
- end
53
-
54
- snooze
55
- p 1
56
- sock = TCPSocket.new('127.0.0.1', port)
57
- p 2
58
- sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
59
- context = OpenSSL::SSL::SSLContext.new
60
- context.verify_mode = OpenSSL::SSL::VERIFY_NONE
61
- client = OpenSSL::SSL::SSLSocket.new(sock, context)
62
- client.sync_close = true
63
- client.hostname = 'localhost'
64
- p 3
65
- client.connect
66
- p 4
67
- client.write("GET / HTTP/1.0\r\nHost: realiteq.net\r\n\r\n")#("1234\n")
68
- p 5
69
- assert_equal "1234\n", client.readpartial(8192)
70
- p 6
71
- client.close
72
- ensure
73
- server_fiber&.stop
74
- server_fiber&.await
75
- server&.close
76
- end
77
34
  end
@@ -34,7 +34,8 @@ class TraceTest < MiniTest::Test
34
34
 
35
35
  def test_2_fiber_trace
36
36
  records = []
37
- t = Polyphony::Trace.new(:fiber_all) { |r| records << r if r[:event] =~ /^fiber_/ }
37
+ thread = Thread.current
38
+ t = Polyphony::Trace.new(:fiber_all) { |r| records << r if Thread.current == thread && r[:event] =~ /^fiber_/ }
38
39
  t.enable
39
40
  Polyphony.trace(true)
40
41
 
@@ -42,23 +43,23 @@ class TraceTest < MiniTest::Test
42
43
  suspend
43
44
  sleep 0
44
45
 
45
- events = records.map { |r| [r[:fiber], r[:event]] }
46
+ events = records.map { |r| [r[:fiber] == f ? :f : :current, r[:event]] }
46
47
  assert_equal [
47
- [f, :fiber_create],
48
- [f, :fiber_schedule],
49
- [Fiber.current, :fiber_switchpoint],
50
- [f, :fiber_run],
51
- [f, :fiber_switchpoint],
52
- [f, :fiber_ev_loop_enter],
53
- [f, :fiber_schedule],
54
- [f, :fiber_ev_loop_leave],
55
- [f, :fiber_run],
56
- [f, :fiber_terminate],
57
- [Fiber.current, :fiber_switchpoint],
58
- [Fiber.current, :fiber_ev_loop_enter],
59
- [Fiber.current, :fiber_schedule],
60
- [Fiber.current, :fiber_ev_loop_leave],
61
- [Fiber.current, :fiber_run]
48
+ [:f, :fiber_create],
49
+ [:f, :fiber_schedule],
50
+ [:current, :fiber_switchpoint],
51
+ [:f, :fiber_run],
52
+ [:f, :fiber_switchpoint],
53
+ [:f, :fiber_ev_loop_enter],
54
+ [:f, :fiber_schedule],
55
+ [:f, :fiber_ev_loop_leave],
56
+ [:f, :fiber_run],
57
+ [:f, :fiber_terminate],
58
+ [:current, :fiber_switchpoint],
59
+ [:current, :fiber_ev_loop_enter],
60
+ [:current, :fiber_schedule],
61
+ [:current, :fiber_ev_loop_leave],
62
+ [:current, :fiber_run]
62
63
  ], events
63
64
  ensure
64
65
  t&.disable
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polyphony
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.43.6
4
+ version: 0.43.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-18 00:00:00.000000000 Z
11
+ date: 2020-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -354,6 +354,7 @@ files:
354
354
  - examples/io/xx-tcpserver.rb
355
355
  - examples/io/xx-tcpsocket.rb
356
356
  - examples/io/xx-zip.rb
357
+ - examples/performance/fiber_transfer.rb
357
358
  - examples/performance/fs_read.rb
358
359
  - examples/performance/mem-usage.rb
359
360
  - examples/performance/messaging.rb
@@ -392,10 +393,10 @@ files:
392
393
  - ext/polyphony/libev.c
393
394
  - ext/polyphony/libev.h
394
395
  - ext/polyphony/libev_agent.c
395
- - ext/polyphony/libev_queue.c
396
396
  - ext/polyphony/polyphony.c
397
397
  - ext/polyphony/polyphony.h
398
398
  - ext/polyphony/polyphony_ext.c
399
+ - ext/polyphony/queue.c
399
400
  - ext/polyphony/ring_buffer.c
400
401
  - ext/polyphony/ring_buffer.h
401
402
  - ext/polyphony/thread.c
@@ -474,7 +475,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
474
475
  - !ruby/object:Gem::Version
475
476
  version: '0'
476
477
  requirements: []
477
- rubygems_version: 3.0.6
478
+ rubygems_version: 3.1.2
478
479
  signing_key:
479
480
  specification_version: 4
480
481
  summary: Fine grained concurrency for Ruby
@@ -1,288 +0,0 @@
1
- #include "polyphony.h"
2
- #include "ring_buffer.h"
3
-
4
- struct async_watcher {
5
- ev_async async;
6
- struct ev_loop *ev_loop;
7
- VALUE fiber;
8
- };
9
-
10
- struct async_watcher_queue {
11
- struct async_watcher **queue;
12
- unsigned int length;
13
- unsigned int count;
14
- unsigned int push_idx;
15
- unsigned int shift_idx;
16
- };
17
-
18
- void async_watcher_queue_init(struct async_watcher_queue *queue) {
19
- queue->length = 1;
20
- queue->count = 0;
21
- queue->queue = malloc(sizeof(struct async_watcher *) * queue->length);
22
- queue->push_idx = 0;
23
- queue->shift_idx = 0;
24
- }
25
-
26
- void async_watcher_queue_free(struct async_watcher_queue *queue) {
27
- free(queue->queue);
28
- }
29
-
30
- void async_watcher_queue_realign(struct async_watcher_queue *queue) {
31
- memmove(
32
- queue->queue,
33
- queue->queue + queue->shift_idx,
34
- queue->count * sizeof(struct async_watcher *)
35
- );
36
- queue->push_idx = queue->push_idx - queue->shift_idx;
37
- queue->shift_idx = 0;
38
- }
39
-
40
- #define QUEUE_REALIGN_THRESHOLD 32
41
-
42
- void async_watcher_queue_push(struct async_watcher_queue *queue, struct async_watcher *watcher) {
43
- if (queue->count == 0) {
44
- queue->push_idx = 0;
45
- queue->shift_idx = 0;
46
- }
47
- if (queue->push_idx == queue->length) {
48
- // prevent shift idx moving too much away from zero
49
- if (queue->length >= QUEUE_REALIGN_THRESHOLD && queue->shift_idx >= (queue->length / 2))
50
- async_watcher_queue_realign(queue);
51
- else {
52
- queue->length = (queue->length == 1) ? 4 : queue->length * 2;
53
- queue->queue = realloc(queue->queue, sizeof(struct async_watcher *) * queue->length);
54
- }
55
- }
56
- queue->count++;
57
- queue->queue[queue->push_idx++] = watcher;
58
- }
59
-
60
- struct async_watcher *async_watcher_queue_shift(struct async_watcher_queue *queue) {
61
- if (queue->count == 0) return 0;
62
-
63
- queue->count--;
64
-
65
- return queue->queue[queue->shift_idx++];
66
- }
67
-
68
- void async_watcher_queue_remove_at_idx(struct async_watcher_queue *queue, unsigned int remove_idx) {
69
- queue->count--;
70
- queue->push_idx--;
71
- if (remove_idx < queue->push_idx)
72
- memmove(
73
- queue->queue + remove_idx,
74
- queue->queue + remove_idx + 1,
75
- (queue->push_idx - remove_idx) * sizeof(struct async_watcher *)
76
- );
77
- }
78
-
79
- void async_watcher_queue_remove_by_fiber(struct async_watcher_queue *queue, VALUE fiber) {
80
- if (queue->count == 0) return;
81
-
82
- for (unsigned idx = queue->shift_idx; idx < queue->push_idx; idx++) {
83
- if (queue->queue[idx]->fiber == fiber) {
84
- async_watcher_queue_remove_at_idx(queue, idx);
85
- return;
86
- }
87
- }
88
- }
89
-
90
- typedef struct queue {
91
- ring_buffer values;
92
- struct async_watcher_queue shift_queue;
93
- } LibevQueue_t;
94
-
95
- VALUE cLibevQueue = Qnil;
96
-
97
- static void LibevQueue_mark(void *ptr) {
98
- LibevQueue_t *queue = ptr;
99
- ring_buffer_mark(&queue->values);
100
- }
101
-
102
- static void LibevQueue_free(void *ptr) {
103
- LibevQueue_t *queue = ptr;
104
- ring_buffer_free(&queue->values);
105
- async_watcher_queue_free(&queue->shift_queue);
106
- xfree(ptr);
107
- }
108
-
109
- static size_t LibevQueue_size(const void *ptr) {
110
- return sizeof(LibevQueue_t);
111
- }
112
-
113
- static const rb_data_type_t LibevQueue_type = {
114
- "Queue",
115
- {LibevQueue_mark, LibevQueue_free, LibevQueue_size,},
116
- 0, 0, 0
117
- };
118
-
119
- static VALUE LibevQueue_allocate(VALUE klass) {
120
- LibevQueue_t *queue;
121
-
122
- queue = ALLOC(LibevQueue_t);
123
- return TypedData_Wrap_Struct(klass, &LibevQueue_type, queue);
124
- }
125
-
126
- #define GetQueue(obj, queue) \
127
- TypedData_Get_Struct((obj), LibevQueue_t, &LibevQueue_type, (queue))
128
-
129
- static VALUE LibevQueue_initialize(VALUE self) {
130
- LibevQueue_t *queue;
131
- GetQueue(self, queue);
132
-
133
- ring_buffer_init(&queue->values);
134
- async_watcher_queue_init(&queue->shift_queue);
135
-
136
- return self;
137
- }
138
-
139
- VALUE LibevQueue_push(VALUE self, VALUE value) {
140
- LibevQueue_t *queue;
141
- GetQueue(self, queue);
142
- if (queue->shift_queue.count > 0) {
143
- struct async_watcher *watcher = async_watcher_queue_shift(&queue->shift_queue);
144
- if (watcher) {
145
- ev_async_send(watcher->ev_loop, &watcher->async);
146
- }
147
- }
148
- ring_buffer_push(&queue->values, value);
149
- return self;
150
- }
151
-
152
- VALUE LibevQueue_unshift(VALUE self, VALUE value) {
153
- LibevQueue_t *queue;
154
- GetQueue(self, queue);
155
- if (queue->shift_queue.count > 0) {
156
- struct async_watcher *watcher = async_watcher_queue_shift(&queue->shift_queue);
157
- if (watcher) {
158
- ev_async_send(watcher->ev_loop, &watcher->async);
159
- }
160
- }
161
- ring_buffer_unshift(&queue->values, value);
162
- return self;
163
- }
164
-
165
- struct ev_loop *LibevAgent_ev_loop(VALUE self);
166
-
167
- void async_watcher_queue_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
168
- struct async_watcher *watcher = (struct async_watcher *)ev_async;
169
- Fiber_make_runnable(watcher->fiber, Qnil);
170
- }
171
-
172
- VALUE libev_agent_await(VALUE self);
173
-
174
- VALUE LibevQueue_shift(VALUE self) {
175
- LibevQueue_t *queue;
176
- GetQueue(self, queue);
177
-
178
- if (queue->values.count == 0) {
179
- struct async_watcher watcher;
180
- VALUE agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
181
- VALUE switchpoint_result = Qnil;
182
-
183
- watcher.ev_loop = LibevAgent_ev_loop(agent);
184
- watcher.fiber = rb_fiber_current();
185
- async_watcher_queue_push(&queue->shift_queue, &watcher);
186
- ev_async_init(&watcher.async, async_watcher_queue_callback);
187
- ev_async_start(watcher.ev_loop, &watcher.async);
188
-
189
- switchpoint_result = libev_agent_await(agent);
190
- ev_async_stop(watcher.ev_loop, &watcher.async);
191
-
192
- if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException))) {
193
- async_watcher_queue_remove_by_fiber(&queue->shift_queue, watcher.fiber);
194
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
195
- }
196
- RB_GC_GUARD(watcher.fiber);
197
- RB_GC_GUARD(agent);
198
- RB_GC_GUARD(switchpoint_result);
199
- }
200
-
201
- return ring_buffer_shift(&queue->values);
202
- }
203
-
204
- VALUE LibevQueue_shift_no_wait(VALUE self) {
205
- LibevQueue_t *queue;
206
- GetQueue(self, queue);
207
-
208
- return ring_buffer_shift(&queue->values);
209
- }
210
-
211
- VALUE LibevQueue_delete(VALUE self, VALUE value) {
212
- LibevQueue_t *queue;
213
- GetQueue(self, queue);
214
-
215
- ring_buffer_delete(&queue->values, value);
216
- return self;
217
- }
218
-
219
- VALUE LibevQueue_clear(VALUE self) {
220
- LibevQueue_t *queue;
221
- GetQueue(self, queue);
222
-
223
- ring_buffer_clear(&queue->values);
224
- return self;
225
- }
226
-
227
- long LibevQueue_len(VALUE self) {
228
- LibevQueue_t *queue;
229
- GetQueue(self, queue);
230
-
231
- return queue->values.count;
232
- }
233
-
234
- VALUE LibevQueue_shift_each(VALUE self) {
235
- LibevQueue_t *queue;
236
- GetQueue(self, queue);
237
-
238
- ring_buffer_shift_each(&queue->values);
239
- return self;
240
- }
241
-
242
- VALUE LibevQueue_shift_all(VALUE self) {
243
- LibevQueue_t *queue;
244
- GetQueue(self, queue);
245
-
246
- return ring_buffer_shift_all(&queue->values);
247
- }
248
-
249
- VALUE LibevQueue_empty_p(VALUE self) {
250
- LibevQueue_t *queue;
251
- GetQueue(self, queue);
252
-
253
- return (queue->values.count == 0) ? Qtrue : Qfalse;
254
- }
255
-
256
- void LibevQueue_trace(VALUE self) {
257
- LibevQueue_t *queue;
258
- GetQueue(self, queue);
259
-
260
- printf(
261
- "queue size: %d count: %d head: %d tail: %d\n",
262
- queue->values.size,
263
- queue->values.count,
264
- queue->values.head,
265
- queue->values.tail
266
- );
267
- }
268
-
269
- void Init_LibevQueue() {
270
- cLibevQueue = rb_define_class_under(mPolyphony, "LibevQueue", rb_cData);
271
- rb_define_alloc_func(cLibevQueue, LibevQueue_allocate);
272
-
273
- rb_define_method(cLibevQueue, "initialize", LibevQueue_initialize, 0);
274
- rb_define_method(cLibevQueue, "push", LibevQueue_push, 1);
275
- rb_define_method(cLibevQueue, "<<", LibevQueue_push, 1);
276
- rb_define_method(cLibevQueue, "unshift", LibevQueue_unshift, 1);
277
-
278
- rb_define_method(cLibevQueue, "shift", LibevQueue_shift, 0);
279
- rb_define_method(cLibevQueue, "pop", LibevQueue_shift, 0);
280
- rb_define_method(cLibevQueue, "shift_no_wait", LibevQueue_shift_no_wait, 0);
281
- rb_define_method(cLibevQueue, "delete", LibevQueue_delete, 1);
282
-
283
- rb_define_method(cLibevQueue, "shift_each", LibevQueue_shift_each, 0);
284
- rb_define_method(cLibevQueue, "shift_all", LibevQueue_shift_all, 0);
285
- rb_define_method(cLibevQueue, "empty?", LibevQueue_empty_p, 0);
286
- }
287
-
288
-