forked from careo/neverblock
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from rightscale/CF-2601_neverblock_and_threads
CF-2601 Have Neverblock + threads play nice
- Loading branch information
Showing
6 changed files
with
255 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
require 'net/http' | ||
|
||
# Author:: Mohammad A. Ali (mailto:[email protected]) | ||
# Copyright:: Copyright (c) 2008 eSpace, Inc. | ||
# License:: Distributes under the same terms as Ruby | ||
|
@@ -14,16 +16,16 @@ def self.logger | |
|
||
# Checks if we should be working in a non-blocking mode | ||
def self.neverblocking? | ||
NB::Fiber.respond_to?(:current) && NB::Fiber.current.respond_to?('[]') && NB::Fiber.current[:neverblock] && NB.reactor.reactor_running? | ||
NB::Fiber.current.respond_to?(:neverblock) && NB::Fiber.current.neverblock && NB.reactor.reactor_running? | ||
end | ||
|
||
# The given block will run its queries either in blocking or non-blocking | ||
# mode based on the first parameter | ||
def self.neverblock(nb = true, &block) | ||
status = NB::Fiber.current[:neverblock] | ||
NB::Fiber.current[:neverblock] = nb | ||
status = NB::Fiber.current.neverblock | ||
NB::Fiber.current.neverblock = !!nb | ||
block.call | ||
NB::Fiber.current[:neverblock] = status | ||
NB::Fiber.current.neverblock = status | ||
end | ||
|
||
# Exception to be thrown for all neverblock internal errors | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ require 'rake' | |
|
||
Gem::Specification.new do |s| | ||
s.name = "neverblock" | ||
s.version = "2.1" | ||
s.version = "2.2" | ||
s.date = "2014-01-31" | ||
s.summary = "Utilities for non-blocking stack components" | ||
s.email = "[email protected]" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
require_relative "../spec_helper" | ||
|
||
describe NeverBlock::Fiber do | ||
context "with mutex" do | ||
before(:each) { @mutex = Mutex.new } | ||
it "turns off neverblock in synchronize section" do | ||
results = [] | ||
EM.run do | ||
fiber_pool = NB::FiberPool.new(1) | ||
fiber_pool.spawn do | ||
results << NB.neverblocking? | ||
@mutex.synchronize do | ||
results << NB.neverblocking? | ||
end | ||
results << NB.neverblocking? | ||
end | ||
fiber_pool.spawn { EM.stop } | ||
end | ||
results.should == [true, false, true] | ||
end | ||
|
||
it "handles multiple synchronizes" do | ||
results = [] | ||
@mutex2 = Mutex.new | ||
EM.run do | ||
fiber_pool = NB::FiberPool.new(1) | ||
fiber_pool.spawn do | ||
results << NB.neverblocking? | ||
@mutex.synchronize do | ||
results << NB.neverblocking? | ||
@mutex2.synchronize do | ||
results << NB.neverblocking? | ||
end | ||
results << NB.neverblocking? | ||
end | ||
results << NB.neverblocking? | ||
end | ||
fiber_pool.spawn { EM.stop } | ||
end | ||
results.should == [true, false, false, false, true] | ||
end | ||
|
||
it "handles interwoven locks" do | ||
results = [] | ||
@mutex2 = Mutex.new | ||
@mutex3 = Mutex.new | ||
EM.run do | ||
fiber_pool = NB::FiberPool.new(1) | ||
fiber_pool.spawn do | ||
# first result is true -- all the rest are false till everything unlocks at the end | ||
results << NB.neverblocking? | ||
@mutex.lock | ||
results << NB.neverblocking? | ||
@mutex2.lock | ||
results << NB.neverblocking? | ||
@mutex3.synchronize { results << NB.neverblocking? } | ||
@mutex.unlock | ||
results << NB.neverblocking? | ||
@mutex3.synchronize { results << NB.neverblocking? } | ||
@mutex2.unlock | ||
results << NB.neverblocking? # expects true | ||
end | ||
fiber_pool.spawn { EM.stop } | ||
end | ||
results.should == [true, false, false, false, false, false, true] | ||
end | ||
|
||
it "recovers from errors in synchronize" do | ||
results = [] | ||
EM.run do | ||
fiber_pool = NB::FiberPool.new(5) | ||
fiber_pool.spawn do | ||
results << NB.neverblocking? | ||
begin | ||
@mutex.synchronize do | ||
results << NB.neverblocking? | ||
raise "ERROR" | ||
end | ||
rescue | ||
end | ||
results << NB.neverblocking? | ||
end | ||
fiber_pool.spawn { EM.stop } | ||
end | ||
results.should == [true, false, true] | ||
end | ||
|
||
# For something like a logger may be background threads calling the logger's | ||
# mutex to synchronize log calls. Those other threads shouldn't affect | ||
# the main event loop thread. | ||
it "turns off neverblock when mutex is locked" do | ||
results = [] | ||
results_by_thread = {} | ||
EM.run do | ||
fiber_pool = NB::FiberPool.new(5) | ||
fiber_pool.spawn do | ||
results << NB.neverblocking? # expect true | ||
@mutex.lock | ||
threads = [] | ||
5.times do | ||
threads << Thread.new do | ||
@mutex.lock | ||
results_by_thread[Thread.current.object_id] = NB.neverblocking? | ||
@mutex.unlock | ||
end | ||
end | ||
results << NB.neverblocking? # expect false | ||
@mutex.unlock | ||
threads.each(&:join) | ||
|
||
results << NB.neverblocking? # expect true | ||
end | ||
fiber_pool.spawn { EM.stop } | ||
end | ||
results.should == [true, false, true] | ||
results_by_thread.values.each do |results| | ||
results.should == false | ||
end | ||
end | ||
|
||
# For something like a logger may be background threads calling the logger's | ||
# mutex to synchronize log calls. Those other threads shouldn't affect | ||
# the main event loop thread. | ||
it "handles conditional variables (which use mutex.sleep)" do | ||
results = [] | ||
@cv = ConditionVariable.new | ||
EM.run do | ||
fiber_pool = NB::FiberPool.new(5) | ||
fiber_pool.spawn do | ||
results << NB.neverblocking? # expect true | ||
@mutex.lock | ||
results << NB.neverblocking? # expect false | ||
t = Thread.new do | ||
@mutex.synchronize do | ||
results << NB.neverblocking? # expect false | ||
@cv.signal | ||
end | ||
end | ||
@cv.wait(@mutex) # Calls @mutex.sleep and waits for thread to call signal | ||
results << NB.neverblocking? # expect false | ||
@mutex.unlock | ||
results << NB.neverblocking? # expect true | ||
end | ||
fiber_pool.spawn { EM.stop } | ||
end | ||
results.should == [true, false, false, false, true] | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters