Skip to content

Instantly share code, notes, and snippets.

@mloughran
Created May 30, 2012 15:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mloughran/2837064 to your computer and use it in GitHub Desktop.
Save mloughran/2837064 to your computer and use it in GitHub Desktop.
Two state deferrable synchronisation primitive, in need of a name
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