From 3bda19aa69df2422ee193b2f7e75d3cc4e6c6ac9 Mon Sep 17 00:00:00 2001 From: Casey Dahlin Date: Wed, 8 Oct 2008 18:40:19 -0400 Subject: Introduce hold-differentiated states States can now receive parameters from the holds placed on them out of a specific set of parameters marked for this kind of retrieval, /and/ multiple states can be differentiated based on this mechanism. --- state.rb | 49 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) (limited to 'state.rb') diff --git a/state.rb b/state.rb index d3782f9..2a52b19 100644 --- a/state.rb +++ b/state.rb @@ -21,6 +21,10 @@ module UpState # Occurs when the state machine becomes inconsistent class ConsistencyFault < StandardError; end +# Occurs when something tries to bring up a state without filling +# out its parameters +class AmbiguousRequest < StandardError; end + ENABLE_TRACE = false def trace(*args) @@ -37,15 +41,17 @@ class State attr :holds # Holds that are had on this state attr :status # Are we up? Down? Getting there? class << self - attr :depends # What states does this state depend on? - attr :caused_by # What events can cause this state? - attr :rising_edge # What do we do as we're coming up? - attr :falling_edge # What do we do as we're going down? + attr :depends # What states does this state depend on? + attr :caused_by # What events can cause this state? + attr :rising_edge # What do we do as we're coming up? + attr :falling_edge # What do we do as we're going down? + attr :hold_provides # What params should come from this state's holder? protected attr_writer :depends attr_writer :caused_by attr_writer :rising_edge attr_writer :falling_edge + attr_writer :hold_provides end @@states = Set.new # Set of activatable states @@ -58,6 +64,7 @@ class State @holds = Set.new @deps = deps.to_set @status = :down + @hold_params = {} return if @@states.include? self @@states.add self @@ -121,12 +128,36 @@ class State hold = Hold::User.new(params) if hold == :user hold = Hold::System.new(params) if hold == :system raise TypeError unless hold.is_a? Hold + if @status == :down and self.class.hold_provides.size > 0 + return self.fork(hold) + end trace "#{self} being held with #{hold}" @holds.add hold - rise if @holds.size == 1 + rise if @status == :down self end + # Duplicate this class and bring the duplicate up with the given hold attached + def fork(hold) + new_one = self.clone + new_one.instance_eval do + @holds = Set.new [hold] + @hold_params = {} + self.class.hold_provides.each do |x| + hold.params[x] or raise AmbiguousRequest, "#{x} undefined" + @hold_params[x] = hold.params[x] + end + end + @@states.each do |state| + next unless state == new_one + state.hold(hold) + return state + end + @@states.add new_one + new_one.send :rise + new_one + end + # Release a hold on this state. Arguments are the same as for +hold+. def release(hold, params = {}) hold = Hold::Dep.new(hold) if hold.is_a? State @@ -146,8 +177,7 @@ class State # Parameters to this state def params - @deps.map{ |x| x.params }.inject({}){ |x,y| x.merge y }.merge \ - @holds.map{ |x| x.params }.inject({}){ |x,y| x.merge y } + @deps.map{ |x| x.params }.inject({}){ |x,y| x.merge y }.merge @hold_params end # Set this state to untrue without running any falling edge code @@ -162,6 +192,7 @@ class State trace "#{self} becomes defunct" end @deps.each{ |x| x.state.release(self) } + @hold_params = {} @status = :down State.gc State.depsolve_all @@ -198,7 +229,7 @@ class State # Create a new type of state. +name+ is capitalized and postfixed with "State" # to create the new state class name. +depends+ is a series of Dependency # Objects. This method is not defined in subclasses of State. - def State.new_type(name, caused_by, depends) + def State.new_type(name, caused_by, depends, hold_provides = []) name = name.capitalize + "State" raise NameError, "State already exists" if self.const_defined? name newtype = const_set(name, Class.new(State)) @@ -206,6 +237,7 @@ class State newtype.caused_by = caused_by newtype.rising_edge = Proc.new{} newtype.falling_edge = Proc.new{} + newtype.hold_provides = hold_provides newtype.instance_eval do undef :new_type, :process_event, :gc, :depsolve_all end @@ -230,6 +262,7 @@ class State # Remove inactive states whose deps are no longer satisfied. This method is # not defined in subclasses of State. def State.gc + @@states = @@states.to_a.to_set # Mutability + identity = pain. Semper λ @@states.each{ |x| x.check_deps } nil end -- cgit