polyphony 0.43.6 → 0.43.8

Sign up to get free protection for your applications and to get access to all the features.
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
-