io-event 1.5.1 → 1.6.1

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: 92dd9a674f53250bb7307226c47bfa3fc4853c8a363509fc02d99a23fcd39194
4
- data.tar.gz: 6aca8cca51546cccb4696c4744e2be05b251cb81058bc9c44ca1deb42bb1930a
3
+ metadata.gz: ea935760f7d99c16037d0737c0b67e7fa7f407f502e3035abced4396d4d90ca5
4
+ data.tar.gz: 28c4387ae3ad0dec06a0bbd13de4db04a0277b826a500a2fa12840dc2fb7ce9f
5
5
  SHA512:
6
- metadata.gz: b12806d9e7e9c184bec13d81792f4f77ed68162e90ada118e89015aeba5571a66f6a8aa7395d40bf923c95d61438c4121cd238433ca6b3874320b60a44d09929
7
- data.tar.gz: 27c2c7d7de7b94bddb174298996192495f8edab54b42ed58cb54f61623210504db5101924dda15e6fd7dc54df81d6fdbaff44ca42608c662febba2c17d260555
6
+ metadata.gz: 4423aa96cfda0606a089d920459f4a2011ea40b0f32680f729a72d0ff253c946a0e01251eb18e15c35e95813aa3df34fe8d99fe2f8dee1027041c858f2adc1be
7
+ data.tar.gz: 53a942a3af8f56f1a029777917a0198555355382837c91f5f754a2d8b73a9ada750d605c524fbc9fd0c544e35c527e1d240a1c078b3299258656980feffa11c7
checksums.yaml.gz.sig CHANGED
Binary file
@@ -152,6 +152,7 @@ void close_internal(struct IO_Event_Selector_EPoll *selector)
152
152
  IO_Event_Interrupt_close(&selector->interrupt);
153
153
  }
154
154
  }
155
+
155
156
  static
156
157
  void IO_Event_Selector_EPoll_Type_free(void *_selector)
157
158
  {
@@ -802,7 +803,7 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
802
803
  return storage;
803
804
  }
804
805
 
805
- rb_raise(rb_eRuntimeError, "unable to convert timeout");
806
+ rb_raise(rb_eArgError, "unable to convert timeout: %"PRIsVALUE, rb_inspect(duration));
806
807
  }
807
808
 
808
809
  static
@@ -371,7 +371,7 @@ VALUE IO_Event_Selector_KQueue_loop(VALUE self) {
371
371
 
372
372
  VALUE IO_Event_Selector_KQueue_idle_duration(VALUE self) {
373
373
  struct IO_Event_Selector_KQueue *selector = NULL;
374
- TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_KQueue_Type, selector);
374
+ TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
375
375
 
376
376
  double duration = selector->idle_duration.tv_sec + (selector->idle_duration.tv_nsec / 1000000000.0);
377
377
 
@@ -816,7 +816,7 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
816
816
  return storage;
817
817
  }
818
818
 
819
- rb_raise(rb_eRuntimeError, "unable to convert timeout");
819
+ rb_raise(rb_eArgError, "unable to convert timeout: %"PRIsVALUE, rb_inspect(duration));
820
820
  }
821
821
 
822
822
  static
@@ -918,7 +918,7 @@ struct __kernel_timespec * make_timeout(VALUE duration, struct __kernel_timespec
918
918
  return storage;
919
919
  }
920
920
 
921
- rb_raise(rb_eRuntimeError, "unable to convert timeout");
921
+ rb_raise(rb_eArgError, "unable to convert timeout: %"PRIsVALUE, rb_inspect(duration));
922
922
  }
923
923
 
924
924
  static
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2021, by Wander Hillen.
5
+ # Copyright, 2021-2024, by Samuel Williams.
6
+
7
+ class IO
8
+ module Event
9
+ # A priority queue implementation using a standard binary minheap. It uses straight comparison
10
+ # of its contents to determine priority.
11
+ # See <https://en.wikipedia.org/wiki/Binary_heap> for explanations of the main methods.
12
+ class PriorityHeap
13
+ def initialize
14
+ # The heap is represented with an array containing a binary tree. See
15
+ # https://en.wikipedia.org/wiki/Binary_heap#Heap_implementation for how this array
16
+ # is built up.
17
+ @contents = []
18
+ end
19
+
20
+ # Returns the earliest timer or nil if the heap is empty.
21
+ def peek
22
+ @contents[0]
23
+ end
24
+
25
+ # Returns the number of elements in the heap
26
+ def size
27
+ @contents.size
28
+ end
29
+
30
+ # Returns the earliest timer if the heap is non-empty and removes it from the heap.
31
+ # Returns nil if the heap is empty. (and doesn't change the heap in that case)
32
+ def pop
33
+ # If the heap is empty:
34
+ if @contents.empty?
35
+ return nil
36
+ end
37
+
38
+ # If we have only one item, no swapping is required:
39
+ if @contents.size == 1
40
+ return @contents.pop
41
+ end
42
+
43
+ # Take the root of the tree:
44
+ value = @contents[0]
45
+
46
+ # Remove the last item in the tree:
47
+ last = @contents.pop
48
+
49
+ # Overwrite the root of the tree with the item:
50
+ @contents[0] = last
51
+
52
+ # Bubble it down into place:
53
+ bubble_down(0)
54
+
55
+ # validate!
56
+
57
+ return value
58
+ end
59
+
60
+ # Inserts a new timer into the heap, then rearranges elements until the heap invariant is true again.
61
+ def push(element)
62
+ # Insert the item at the end of the heap:
63
+ @contents.push(element)
64
+
65
+ # Bubble it up into position:
66
+ bubble_up(@contents.size - 1)
67
+
68
+ # validate!
69
+
70
+ return self
71
+ end
72
+
73
+ # Empties out the heap, discarding all elements
74
+ def clear!
75
+ @contents = []
76
+ end
77
+
78
+ # Validate the heap invariant. Every element except the root must not be smaller than
79
+ # its parent element. Note that it MAY be equal.
80
+ def valid?
81
+ # notice we skip index 0 on purpose, because it has no parent
82
+ (1..(@contents.size - 1)).all? { |e| @contents[e] >= @contents[(e - 1) / 2] }
83
+ end
84
+
85
+ private
86
+
87
+ # Left here for reference, but unused.
88
+ # def swap(i, j)
89
+ # @contents[i], @contents[j] = @contents[j], @contents[i]
90
+ # end
91
+
92
+ def bubble_up(index)
93
+ parent_index = (index - 1) / 2 # watch out, integer division!
94
+
95
+ while index > 0 && @contents[index] < @contents[parent_index]
96
+ # if the node has a smaller value than its parent, swap these nodes
97
+ # to uphold the minheap invariant and update the index of the 'current'
98
+ # node. If the node is already at index 0, we can also stop because that
99
+ # is the root of the heap.
100
+ # swap(index, parent_index)
101
+ @contents[index], @contents[parent_index] = @contents[parent_index], @contents[index]
102
+
103
+ index = parent_index
104
+ parent_index = (index - 1) / 2 # watch out, integer division!
105
+ end
106
+ end
107
+
108
+ def bubble_down(index)
109
+ swap_value = 0
110
+ swap_index = nil
111
+
112
+ while true
113
+ left_index = (2 * index) + 1
114
+ left_value = @contents[left_index]
115
+
116
+ if left_value.nil?
117
+ # This node has no children so it can't bubble down any further.
118
+ # We're done here!
119
+ return
120
+ end
121
+
122
+ # Determine which of the child nodes has the smallest value:
123
+ right_index = left_index + 1
124
+ right_value = @contents[right_index]
125
+
126
+ if right_value.nil? or right_value > left_value
127
+ swap_value = left_value
128
+ swap_index = left_index
129
+ else
130
+ swap_value = right_value
131
+ swap_index = right_index
132
+ end
133
+
134
+ if @contents[index] < swap_value
135
+ # No need to swap, the minheap invariant is already satisfied:
136
+ return
137
+ else
138
+ # At least one of the child node has a smaller value than the current node, swap current node with that child and update current node for if it might need to bubble down even further:
139
+ # swap(index, swap_index)
140
+ @contents[index], @contents[swap_index] = @contents[swap_index], @contents[index]
141
+
142
+ index = swap_index
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2023, by Samuel Williams.
4
+ # Copyright, 2021-2024, by Samuel Williams.
5
5
  # Copyright, 2023, by Math Ieu.
6
6
 
7
7
  require_relative '../interrupt'
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require_relative 'priority_heap'
7
+
8
+ class IO
9
+ module Event
10
+ class Timers
11
+ class Handle
12
+ def initialize(offset, block)
13
+ @offset = offset
14
+ @block = block
15
+ end
16
+
17
+ def < other
18
+ @offset < other.offset
19
+ end
20
+
21
+ def > other
22
+ @offset > other.offset
23
+ end
24
+
25
+ attr :offset
26
+ attr :block
27
+
28
+ def call(...)
29
+ @block.call(...)
30
+ end
31
+
32
+ def cancel!
33
+ @block = nil
34
+ end
35
+
36
+ def cancelled?
37
+ @block.nil?
38
+ end
39
+ end
40
+
41
+ def initialize
42
+ @heap = PriorityHeap.new
43
+ @scheduled = []
44
+ end
45
+
46
+ def size
47
+ flush!
48
+
49
+ return @heap.size
50
+ end
51
+
52
+ def schedule(offset, block)
53
+ handle = Handle.new(offset, block)
54
+ @scheduled << handle
55
+
56
+ return handle
57
+ end
58
+
59
+ def after(timeout, &block)
60
+ schedule(now + timeout, block)
61
+ end
62
+
63
+ def wait_interval(now = self.now)
64
+ flush!
65
+
66
+ while handle = @heap.peek
67
+ if handle.cancelled?
68
+ @heap.pop
69
+ else
70
+ return handle.offset - now
71
+ end
72
+ end
73
+ end
74
+
75
+ def now
76
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
77
+ end
78
+
79
+ def fire(now = self.now)
80
+ # Flush scheduled timers into the heap:
81
+ flush!
82
+
83
+ # Get the earliest timer:
84
+ while handle = @heap.peek
85
+ if handle.cancelled?
86
+ @heap.pop
87
+ elsif handle.offset <= now
88
+ # Remove the earliest timer from the heap:
89
+ @heap.pop
90
+
91
+ # Call the block:
92
+ handle.call(now)
93
+ else
94
+ break
95
+ end
96
+ end
97
+ end
98
+
99
+ protected def flush!
100
+ while handle = @scheduled.pop
101
+ @heap.push(handle) unless handle.cancelled?
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -5,6 +5,6 @@
5
5
 
6
6
  class IO
7
7
  module Event
8
- VERSION = "1.5.1"
8
+ VERSION = "1.6.1"
9
9
  end
10
10
  end
data/lib/io/event.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2023, by Samuel Williams.
4
+ # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
6
  require_relative 'event/version'
7
7
  require_relative 'event/selector'
8
+ require_relative 'event/timers'
8
9
 
9
10
  begin
10
11
  require 'IO_Event'
data/license.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # MIT License
2
2
 
3
+ Copyright, 2021, by Wander Hillen.
3
4
  Copyright, 2021-2024, by Samuel Williams.
4
5
  Copyright, 2021, by Delton Ding.
5
6
  Copyright, 2021-2024, by Benoit Daloze.
data/readme.md CHANGED
@@ -6,7 +6,7 @@ Provides low level cross-platform primitives for constructing event loops, with
6
6
 
7
7
  ## Motivation
8
8
 
9
- The initial proof-of-concept [Async](https://github.com/socketry/async) was built on [NIO4r](https://github.com/socketry/nio4r). It was perfectly acceptable and well tested in production, however being built on `libev` was a little bit limiting. I wanted to directly built my fiber scheduler into the fabric of the event loop, which is what this gem exposes - it is specifically implemented to support building event loops beneath the fiber scheduler interface, providing an efficient C implementation of all the core operations.
9
+ The initial proof-of-concept [Async](https://github.com/socketry/async) was built on [NIO4r](https://github.com/socketry/nio4r). It was perfectly acceptable and well tested in production, however being built on `libev` was a little bit limiting. I wanted to directly build my fiber scheduler into the fabric of the event loop, which is what this gem exposes - it is specifically implemented to support building event loops beneath the fiber scheduler interface, providing an efficient C implementation of all the core operations.
10
10
 
11
11
  ## Usage
12
12
 
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,11 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io-event
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  - Math Ieu
9
+ - Wander Hillen
9
10
  - Benoit Daloze
10
11
  - Bruno Sutic
11
12
  - Alex Matchneer
@@ -43,7 +44,7 @@ cert_chain:
43
44
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
44
45
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
45
46
  -----END CERTIFICATE-----
46
- date: 2024-03-04 00:00:00.000000000 Z
47
+ date: 2024-06-12 00:00:00.000000000 Z
47
48
  dependencies: []
48
49
  description:
49
50
  email:
@@ -72,10 +73,12 @@ files:
72
73
  - lib/io/event.rb
73
74
  - lib/io/event/debug/selector.rb
74
75
  - lib/io/event/interrupt.rb
76
+ - lib/io/event/priority_heap.rb
75
77
  - lib/io/event/selector.rb
76
78
  - lib/io/event/selector/nonblock.rb
77
79
  - lib/io/event/selector/select.rb
78
80
  - lib/io/event/support.rb
81
+ - lib/io/event/timers.rb
79
82
  - lib/io/event/version.rb
80
83
  - license.md
81
84
  - readme.md
@@ -98,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
101
  - !ruby/object:Gem::Version
99
102
  version: '0'
100
103
  requirements: []
101
- rubygems_version: 3.5.3
104
+ rubygems_version: 3.5.9
102
105
  signing_key:
103
106
  specification_version: 4
104
107
  summary: An event loop.
metadata.gz.sig CHANGED
Binary file