summaryrefslogtreecommitdiffstats
path: root/state.rb
diff options
context:
space:
mode:
authorCasey Dahlin <cdahlin@redhat.com>2008-10-08 18:40:19 -0400
committerCasey Dahlin <cdahlin@redhat.com>2008-10-08 18:40:19 -0400
commit3bda19aa69df2422ee193b2f7e75d3cc4e6c6ac9 (patch)
tree256ae1fb632b8e66b769ea0d4062f05adcabf768 /state.rb
parentd4b57c441dec1bc8b1bce8edfc8f1f3aa186fc27 (diff)
downloadupstate-3bda19aa69df2422ee193b2f7e75d3cc4e6c6ac9.tar.gz
upstate-3bda19aa69df2422ee193b2f7e75d3cc4e6c6ac9.tar.xz
upstate-3bda19aa69df2422ee193b2f7e75d3cc4e6c6ac9.zip
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.
Diffstat (limited to 'state.rb')
-rw-r--r--state.rb49
1 files changed, 41 insertions, 8 deletions
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