io-event 1.2.2 → 1.3.0

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.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021, by Samuel Williams.
4
+ # Copyright, 2021-2023, by Samuel Williams.
5
5
 
6
6
  module IO::Event
7
7
  # A thread safe synchronisation primative.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022, by Samuel Williams.
4
+ # Copyright, 2022-2023, by Samuel Williams.
5
5
 
6
6
  require 'io/nonblock'
7
7
 
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2022, by Samuel Williams.
4
+ # Copyright, 2021-2023, by Samuel Williams.
5
+ # Copyright, 2023, by Math Ieu.
5
6
 
6
7
  require_relative '../interrupt'
7
8
  require_relative '../support'
@@ -103,14 +104,26 @@ module IO::Event
103
104
  self.fiber&.alive?
104
105
  end
105
106
 
106
- def transfer(events)
107
+ # Dispatch the given events to the list of waiting fibers. If the fiber was not waiting for the given events, it is reactivated by calling the given block.
108
+ def dispatch(events, &reactivate)
109
+ # We capture the tail here, because calling reactivate might modify it:
110
+ tail = self.tail
111
+
107
112
  if fiber = self.fiber
108
- self.fiber = nil
109
-
110
- fiber.transfer(events & self.events) if fiber.alive?
113
+ if fiber.alive?
114
+ revents = events & self.events
115
+ if revents.zero?
116
+ reactivate.call(self)
117
+ else
118
+ self.fiber = nil
119
+ fiber.transfer(revents)
120
+ end
121
+ else
122
+ self.fiber = nil
123
+ end
111
124
  end
112
-
113
- self.tail&.transfer(events)
125
+
126
+ tail&.dispatch(events, &reactivate)
114
127
  end
115
128
 
116
129
  def invalidate
@@ -152,8 +165,8 @@ module IO::Event
152
165
  total = 0
153
166
 
154
167
  Selector.nonblock(io) do
155
- while true
156
- maximum_size = buffer.size - offset
168
+ maximum_size = buffer.size - offset
169
+ while maximum_size > 0
157
170
  result = Fiber.blocking{buffer.read(io, maximum_size, offset)}
158
171
 
159
172
  if again?(result)
@@ -169,6 +182,8 @@ module IO::Event
169
182
  offset += result
170
183
  break if total >= length
171
184
  end
185
+
186
+ maximum_size = buffer.size - offset
172
187
  end
173
188
  end
174
189
 
@@ -179,8 +194,8 @@ module IO::Event
179
194
  total = 0
180
195
 
181
196
  Selector.nonblock(io) do
182
- while true
183
- maximum_size = buffer.size - offset
197
+ maximum_size = buffer.size - offset
198
+ while maximum_size > 0
184
199
  result = Fiber.blocking{buffer.write(io, maximum_size, offset)}
185
200
 
186
201
  if again?(result)
@@ -196,6 +211,8 @@ module IO::Event
196
211
  offset += result
197
212
  break if total >= length
198
213
  end
214
+
215
+ maximum_size = buffer.size - offset
199
216
  end
200
217
  end
201
218
 
@@ -206,9 +223,8 @@ module IO::Event
206
223
  io = IO.for_fd(_io.fileno, autoclose: false)
207
224
  total = 0
208
225
 
209
- while true
210
- maximum_size = buffer.size - offset
211
-
226
+ maximum_size = buffer.size - offset
227
+ while maximum_size > 0
212
228
  case result = blocking{io.read_nonblock(maximum_size, exception: false)}
213
229
  when :wait_readable
214
230
  if length > 0
@@ -233,6 +249,8 @@ module IO::Event
233
249
  break if size >= length
234
250
  length -= size
235
251
  end
252
+
253
+ maximum_size = buffer.size - offset
236
254
  end
237
255
 
238
256
  return total
@@ -246,9 +264,8 @@ module IO::Event
246
264
  io = IO.for_fd(_io.fileno, autoclose: false)
247
265
  total = 0
248
266
 
249
- while true
250
- maximum_size = buffer.size - offset
251
-
267
+ maximum_size = buffer.size - offset
268
+ while maximum_size > 0
252
269
  chunk = buffer.get_string(offset, maximum_size)
253
270
  case result = blocking{io.write_nonblock(chunk, exception: false)}
254
271
  when :wait_readable
@@ -269,6 +286,8 @@ module IO::Event
269
286
  break if result >= length
270
287
  length -= result
271
288
  end
289
+
290
+ maximum_size = buffer.size - offset
272
291
  end
273
292
 
274
293
  return total
@@ -329,12 +348,26 @@ module IO::Event
329
348
  end
330
349
  end
331
350
 
332
- @blocked = true
333
351
  duration = 0 unless @ready.empty?
334
- readable, writable, priority = ::IO.select(readable, writable, priority, duration)
335
- @blocked = false
352
+ error = nil
336
353
 
337
- ready = Hash.new(0)
354
+ # We need to handle interrupts on blocking IO. Every other implementation uses EINTR, but that doesn't work with `::IO.select` as it will retry the call on EINTR.
355
+ Thread.handle_interrupt(::Exception => :on_blocking) do
356
+ @blocked = true
357
+ readable, writable, priority = ::IO.select(readable, writable, priority, duration)
358
+ rescue ::Exception => error
359
+ # Requeue below...
360
+ ensure
361
+ @blocked = false
362
+ end
363
+
364
+ if error
365
+ # Requeue the error into the pending exception queue:
366
+ Thread.current.raise(error)
367
+ return 0
368
+ end
369
+
370
+ ready = Hash.new(0).compare_by_identity
338
371
 
339
372
  readable&.each do |io|
340
373
  ready[io] |= IO::READABLE
@@ -349,7 +382,11 @@ module IO::Event
349
382
  end
350
383
 
351
384
  ready.each do |io, events|
352
- @waiting.delete(io).transfer(events)
385
+ @waiting.delete(io).dispatch(events) do |waiter|
386
+ # Re-schedule the waiting IO:
387
+ waiter.tail = @waiting[io]
388
+ @waiting[io] = waiter
389
+ end
353
390
  end
354
391
 
355
392
  return ready.size
@@ -11,11 +11,7 @@ module IO::Event
11
11
  module Selector
12
12
  def self.default(env = ENV)
13
13
  if name = env['IO_EVENT_SELECTOR']&.to_sym
14
- if const_defined?(name)
15
- return const_get(name)
16
- else
17
- warn "Could not find IO_EVENT_SELECTOR=#{name}!"
18
- end
14
+ return const_get(name)
19
15
  end
20
16
 
21
17
  if self.const_defined?(:URing)
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2022, by Samuel Williams.
4
+ # Copyright, 2021-2023, by Samuel Williams.
5
5
 
6
6
  class IO
7
7
  module Event
8
- VERSION = "1.2.2"
8
+ VERSION = "1.3.0"
9
9
  end
10
10
  end
data/lib/io/event.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021, by Samuel Williams.
4
+ # Copyright, 2021-2023, by Samuel Williams.
5
5
 
6
6
  require_relative 'event/version'
7
7
  require_relative 'event/selector'
data/license.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # MIT License
2
2
 
3
- Copyright, 2021-2022, by Samuel Williams.
3
+ Copyright, 2021-2023, by Samuel Williams.
4
4
  Copyright, 2021, by Delton Ding.
5
5
  Copyright, 2021, by Benoit Daloze.
6
6
  Copyright, 2022, by Alex Matchneer.
7
7
  Copyright, 2022, by Bruno Sutic.
8
+ Copyright, 2023, by Math Ieu.
8
9
 
9
10
  Permission is hereby granted, free of charge, to any person obtaining a copy
10
11
  of this software and associated documentation files (the "Software"), to deal
data/readme.md CHANGED
@@ -16,8 +16,16 @@ Please see the [project documentation](https://socketry.github.io/io-event/).
16
16
 
17
17
  We welcome contributions to this project.
18
18
 
19
- 1. Fork it
20
- 2. Create your feature branch (`git checkout -b my-new-feature`)
21
- 3. Commit your changes (`git commit -am 'Add some feature'`)
22
- 4. Push to the branch (`git push origin my-new-feature`)
23
- 5. Create new Pull Request
19
+ 1. Fork it.
20
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
21
+ 3. Commit your changes (`git commit -am 'Add some feature'`).
22
+ 4. Push to the branch (`git push origin my-new-feature`).
23
+ 5. Create new Pull Request.
24
+
25
+ ### Developer Certificate of Origin
26
+
27
+ This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this project must agree to this document to have their contributions accepted.
28
+
29
+ ### Contributor Covenant
30
+
31
+ This project is governed by [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io-event
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  - Bruno Sutic
9
+ - Math Ieu
10
+ - Alex Matchneer
9
11
  - Benoit Daloze
10
12
  - Delton Ding
11
- - machty
12
13
  autorequire:
13
14
  bindir: bin
14
15
  cert_chain:
@@ -41,64 +42,8 @@ cert_chain:
41
42
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
42
43
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
43
44
  -----END CERTIFICATE-----
44
- date: 2023-05-14 00:00:00.000000000 Z
45
- dependencies:
46
- - !ruby/object:Gem::Dependency
47
- name: bake
48
- requirement: !ruby/object:Gem::Requirement
49
- requirements:
50
- - - ">="
51
- - !ruby/object:Gem::Version
52
- version: '0'
53
- type: :development
54
- prerelease: false
55
- version_requirements: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- version: '0'
60
- - !ruby/object:Gem::Dependency
61
- name: bundler
62
- requirement: !ruby/object:Gem::Requirement
63
- requirements:
64
- - - ">="
65
- - !ruby/object:Gem::Version
66
- version: '0'
67
- type: :development
68
- prerelease: false
69
- version_requirements: !ruby/object:Gem::Requirement
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- version: '0'
74
- - !ruby/object:Gem::Dependency
75
- name: covered
76
- requirement: !ruby/object:Gem::Requirement
77
- requirements:
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- version: '0'
81
- type: :development
82
- prerelease: false
83
- version_requirements: !ruby/object:Gem::Requirement
84
- requirements:
85
- - - ">="
86
- - !ruby/object:Gem::Version
87
- version: '0'
88
- - !ruby/object:Gem::Dependency
89
- name: sus
90
- requirement: !ruby/object:Gem::Requirement
91
- requirements:
92
- - - "~>"
93
- - !ruby/object:Gem::Version
94
- version: '0.6'
95
- type: :development
96
- prerelease: false
97
- version_requirements: !ruby/object:Gem::Requirement
98
- requirements:
99
- - - "~>"
100
- - !ruby/object:Gem::Version
101
- version: '0.6'
45
+ date: 2023-08-23 00:00:00.000000000 Z
46
+ dependencies: []
102
47
  description:
103
48
  email:
104
49
  executables: []
@@ -112,10 +57,12 @@ files:
112
57
  - ext/io/event/event.h
113
58
  - ext/io/event/interrupt.c
114
59
  - ext/io/event/interrupt.h
60
+ - ext/io/event/selector/array.h
115
61
  - ext/io/event/selector/epoll.c
116
62
  - ext/io/event/selector/epoll.h
117
63
  - ext/io/event/selector/kqueue.c
118
64
  - ext/io/event/selector/kqueue.h
65
+ - ext/io/event/selector/list.h
119
66
  - ext/io/event/selector/pidfd.c
120
67
  - ext/io/event/selector/selector.c
121
68
  - ext/io/event/selector/selector.h
@@ -150,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
97
  - !ruby/object:Gem::Version
151
98
  version: '0'
152
99
  requirements: []
153
- rubygems_version: 3.4.7
100
+ rubygems_version: 3.4.10
154
101
  signing_key:
155
102
  specification_version: 4
156
103
  summary: An event loop.
metadata.gz.sig CHANGED
Binary file