summaryrefslogtreecommitdiff
path: root/test/fiber/scheduler.rb
diff options
context:
space:
mode:
authorBenoit Daloze <eregontp@gmail.com>2020-09-05 16:26:24 +1200
committerSamuel Williams <samuel.williams@oriontransfer.co.nz>2020-09-14 16:44:09 +1200
commit178c1b0922dc727897d81d7cfe9c97d5ffa97fd9 (patch)
tree113600e7e6a196b779bcac7529535597858f78a7 /test/fiber/scheduler.rb
parent9e0a48c7a31ecd39be0596d0517b9d521ae75282 (diff)
Make Mutex per-Fiber instead of per-Thread
* Enables Mutex to be used as synchronization between multiple Fibers of the same Thread. * With a Fiber scheduler we can yield to another Fiber on contended Mutex#lock instead of blocking the entire thread. * This also makes the behavior of Mutex consistent across CRuby, JRuby and TruffleRuby. * [Feature #16792]
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/3434
Diffstat (limited to 'test/fiber/scheduler.rb')
-rw-r--r--test/fiber/scheduler.rb46
1 files changed, 44 insertions, 2 deletions
diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb
index 1f690b4c08..fa05daf886 100644
--- a/test/fiber/scheduler.rb
+++ b/test/fiber/scheduler.rb
@@ -14,6 +14,12 @@ class Scheduler
@readable = {}
@writable = {}
@waiting = {}
+
+ @urgent = nil
+
+ @lock = Mutex.new
+ @locking = 0
+ @ready = []
end
attr :readable
@@ -35,9 +41,11 @@ class Scheduler
end
def run
- while @readable.any? or @writable.any? or @waiting.any?
+ @urgent = IO.pipe
+
+ while @readable.any? or @writable.any? or @waiting.any? or @locking.positive?
# Can only handle file descriptors up to 1024...
- readable, writable = IO.select(@readable.keys, @writable.keys, [], next_timeout)
+ readable, writable = IO.select(@readable.keys + [@urgent.first], @writable.keys, [], next_timeout)
# puts "readable: #{readable}" if readable&.any?
# puts "writable: #{writable}" if writable&.any?
@@ -63,7 +71,24 @@ class Scheduler
end
end
end
+
+ if @ready.any?
+ # Clear out the urgent notification pipe.
+ @urgent.first.read_nonblock(1024)
+
+ ready = nil
+
+ @lock.synchronize do
+ ready, @ready = @ready, Array.new
+ end
+
+ ready.each do |fiber|
+ fiber.resume
+ end
+ end
end
+ ensure
+ @urgent.each(&:close)
end
def current_time
@@ -95,6 +120,23 @@ class Scheduler
return true
end
+ def mutex_lock(mutex)
+ @locking += 1
+ Fiber.yield
+ ensure
+ @locking -= 1
+ end
+
+ def mutex_unlock(mutex, fiber)
+ @lock.synchronize do
+ @ready << fiber
+
+ if @urgent
+ @urgent.last.write('.')
+ end
+ end
+ end
+
def fiber(&block)
fiber = Fiber.new(blocking: false, &block)