Created
May 30, 2012 15:39
-
-
Save mloughran/2837064 to your computer and use it in GitHub Desktop.
Two state deferrable synchronisation primitive, in need of a name
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
require 'eventmachine' | |
# Allows calling set/clear at will, and ensures that only a single on_set or | |
# on_clear deferrable is running concurrently. It also ensures that | |
# un-necessary callbacks are not run, i.e. set-unset-set in sequency only | |
# requires the on_set callback to run once | |
# | |
# The blocks/procs supplied for on_set and on_clear must return deferrables | |
# | |
class Thing | |
def initialize(on_set = nil, on_clear = nil) | |
@state = false | |
# The state we'll switch to when the current deferrable completes: | |
@pending_state = false | |
@on_set, @on_clear = on_set, on_clear | |
end | |
def on_set(&block) | |
@on_set = block | |
end | |
def on_clear(&block) | |
@on_clear = block | |
end | |
def set | |
case [@state, @pending_state] | |
when [false, false] | |
run(true, @on_set) | |
when [false, true] | |
@queue = nil | |
when [true, false] | |
@queue = [true, @on_set] | |
# [true, true] is no-op | |
end | |
end | |
def clear | |
case [@state, @pending_state] | |
# [false, false] is no-op | |
when [false, true] | |
@queue = [false, @on_clear] | |
when [true, false] | |
@queue = nil | |
when [true, true] | |
run(false, @on_clear) | |
end | |
end | |
def in_progress? | |
@state != @pending_state | |
end | |
private | |
def run(new_state, block) | |
df = block.call | |
raise "Block must return a deferrable" unless df.kind_of?(EM::Deferrable) | |
@pending_state = new_state | |
df.callback { | |
@state = new_state | |
# Run queue | |
if @queue | |
run(*@queue) | |
@queue = nil | |
end | |
} | |
end | |
end | |
# Example: | |
EM.run { | |
thing = Thing.new | |
thing.on_set { | |
puts "On set!!!" | |
df = EM::DefaultDeferrable.new | |
EM.add_timer(2) { | |
puts "Set done" | |
df.succeed | |
} | |
df | |
} | |
thing.on_clear { | |
puts "On clear!!!" | |
df = EM::DefaultDeferrable.new | |
EM.add_timer(2) { | |
puts "Clear done" | |
df.succeed | |
} | |
df | |
} | |
thing.set | |
EM.add_timer(1) { | |
thing.set | |
thing.clear | |
thing.set | |
thing.clear | |
} | |
EM.add_timer(5) { | |
puts "Clear, set, clear!" | |
thing.clear | |
thing.set | |
thing.clear | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment