Class: Roby::PlanObject
- Inherits:
-
DistributedObject
- Object
- DistributedObject
- Roby::PlanObject
- Extended by:
- Models::PlanObject
- Includes:
- DRoby::Identifiable, GUI::GraphvizPlanObject, GUI::RelationsCanvasPlanObject, Relations::DirectedRelationSupport
- Defined in:
- lib/roby/plan_object.rb,
lib/roby.rb,
lib/roby/droby/enable.rb
Overview
Base class for all objects which are included in a plan.
Direct Known Subclasses
Defined Under Namespace
Classes: InstanceHandler
Instance Attribute Summary collapse
-
#addition_time ⇒ Object
The time at which this plan object has been added into its first plan.
-
#executable ⇒ Object
writeonly
- A three-state flag with the following values: nil
- the object is executable if its plan is true
- the object is executable false
-
the object is not executable.
-
#execution_engine ⇒ Object
readonly
The underlying execution engine if #plan is executable.
-
#finalization_handlers ⇒ Array<InstanceHandler>
readonly
Set of finalization handlers defined on this task instance.
-
#finalization_time ⇒ Object
The time at which this plan object has been finalized (i.e. removed from plan), or nil if it has not been (yet).
-
#model ⇒ Object
readonly
This object’s model.
-
#plan ⇒ Object
The plan this object belongs to.
-
#promise_executor ⇒ Object
readonly
A thread pool that ensures that any work queued using #promise is serialized.
-
#removed_at ⇒ Object
The place where this object has been removed from its plan.
Attributes included from Transaction::Proxying::Cache
#transaction_forwarder_module, #transaction_proxy_module
Attributes included from Relations::DirectedRelationSupport
Attributes inherited from DistributedObject
Instance Method Summary collapse
-
#add_child_object(child, type, info = nil) ⇒ Object
Synchronizes the plan of this object from the one of its peer.
-
#apply_relation_changes(object, changes) ⇒ Object
Transfers a set of relations from this plan object to
object. -
#as_plan ⇒ Object
Used in plan management as a way to extract a plan object from any object.
-
#can_finalize? ⇒ Boolean
Whether this task can be finalized.
-
#commit_transaction ⇒ Object
Method called to apply modifications needed to commit this object into the underlying plan.
-
#concrete_model ⇒ Object
The non-specialized model for self.
- #connection_space ⇒ Object
-
#each_finalization_handler {|block| ... } ⇒ void
Enumerates the finalization handlers that should be applied in finalized!.
- #each_in_neighbour_merged(relation, intrusive: nil, &block) ⇒ Object
- #each_out_neighbour_merged(relation, intrusive: nil, &block) ⇒ Object
-
#each_plan_child ⇒ Object
Iterates on all the children of this root object.
-
#engine ⇒ Object
deprecated
Deprecated.
use #execution_engine instead
-
#executable? ⇒ Boolean
If this object is executable.
-
#finalized!(timestamp = nil) ⇒ void
Called when a particular object has been removed from its plan.
-
#finalized? ⇒ Boolean
True if this object has been included in a plan, but has been removed from it since.
-
#forget_peer(peer) ⇒ Object
Called when all links to
peershould be removed. -
#fullfills?(models) ⇒ Boolean
True if this object provides all the given models.
-
#garbage! ⇒ Object
Mark this task as garbage.
-
#garbage? ⇒ Object
Whether this task has been marked as garbage by the garbage collection process.
-
#initialize(plan: TemplatePlan.new) ⇒ PlanObject
constructor
A new instance of PlanObject.
- #initialize_copy(other) ⇒ Object
-
#initialize_replacement(object) ⇒ Object
Called by #replace_by and #replace_subplan_by to do object-specific initialization of
objectwhenobjectis used to replaceselfin a plan. -
#merged_relations(enumerator, intrusive) ⇒ Object
call-seq: merged_relation(enumeration_method, false[, arg1, arg2]) do |self_t, related_t| end merged_relation(enumeration_method, true[, arg1, arg2]) do |related_t| end.
-
#promise(description: "#{self}.promise", executor: promise_executor, &block) ⇒ Promise
Create a promise that is serialized with all promises created for this object.
-
#read_write? ⇒ Boolean
True if this object can be modified by the local plan manager.
-
#real_object ⇒ Object
If
selfis a transaction proxy, returns the underlying plan object, regardless of how many transactions there is on the stack. -
#remotely_useful? ⇒ Boolean
True if this object is useful for one of our peers.
-
#replace_by(object) ⇒ Object
Replaces
selfbyobjectin all graphsselfis part of. -
#replace_subplan_by(object) ⇒ Object
Replaces, in the plan, the subplan generated by this plan object by the one generated by
object. -
#root_object ⇒ Object
Return the root plan object for this object.
-
#root_object? ⇒ Boolean
True if this object is a root object in the plan.
-
#subscribed? ⇒ Boolean
True if we are explicitely subscribed to this object.
-
#transaction_proxy? ⇒ Boolean
True if this object is a transaction proxy, false otherwise.
-
#transaction_stack ⇒ Object
Returns the stack of transactions/plans this object is part of, starting with self.plan.
-
#update_on?(peer) ⇒ Boolean
True if we should send updates about this object to
peer. -
#updated_by?(peer) ⇒ Boolean
True if we receive updates for this object from
peer. -
#when_finalized(options = Hash.new) {|task| ... } ⇒ void
Called when the task gets finalized, i.e.
Methods included from Models::PlanObject
child_plan_object, finalization_handler, match
Methods included from DRoby::Identifiable
Methods included from GUI::RelationsCanvasPlanObject
#display, #display_create, #display_events, #display_name, #display_parent
Methods included from GUI::GraphvizPlanObject
#apply_layout, #dot_label, #to_dot
Methods included from Relations::DirectedRelationSupport
#[], #[]=, #add_parent_object, #child_object?, #child_objects, #clear_vertex, #each_child_object, #each_in_neighbour, #each_out_neighbour, #each_parent_object, #each_relation, #each_relation_graph, #each_relation_sorted, #each_root_relation_graph, #enum_child_objects, #enum_parent_objects, #enum_relations, #leaf?, #parent_object?, #parent_objects, #related_object?, #related_objects, #relation_graph_for, #relations, #remove_child_object, #remove_children, #remove_parent_object, #remove_parents, #remove_relations, #root?, #sorted_relations
Methods inherited from DistributedObject
#add_owner, #clear_owners, #owned_by?, #remove_owner
Constructor Details
#initialize(plan: TemplatePlan.new) ⇒ PlanObject
Returns a new instance of PlanObject.
118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/roby/plan_object.rb', line 118 def initialize(plan: TemplatePlan.new) super() @plan = plan self.plan = @plan @removed_at = nil @executable = nil @garbage = false @finalization_handlers = Array.new @model = self.class end |
Instance Attribute Details
#addition_time ⇒ Object
The time at which this plan object has been added into its first plan
597 598 599 |
# File 'lib/roby/plan_object.rb', line 597 def addition_time @addition_time end |
#executable=(value) ⇒ Object (writeonly)
A three-state flag with the following values:
- nil
-
the object is executable if its plan is
- true
-
the object is executable
- false
-
the object is not executable
338 339 340 |
# File 'lib/roby/plan_object.rb', line 338 def executable=(value) @executable = value end |
#execution_engine ⇒ Object (readonly)
The underlying execution engine if #plan is executable
19 20 21 |
# File 'lib/roby/plan_object.rb', line 19 def execution_engine @execution_engine end |
#finalization_handlers ⇒ Array<InstanceHandler> (readonly)
Returns set of finalization handlers defined on this task instance.
530 531 532 |
# File 'lib/roby/plan_object.rb', line 530 def finalization_handlers @finalization_handlers end |
#finalization_time ⇒ Object
The time at which this plan object has been finalized (i.e. removed from plan), or nil if it has not been (yet)
601 602 603 |
# File 'lib/roby/plan_object.rb', line 601 def finalization_time @finalization_time end |
#model ⇒ Object (readonly)
This object’s model
This is usually self.class, unless #specialize has been called in which case it is this object’s singleton class
11 12 13 |
# File 'lib/roby/plan_object.rb', line 11 def model @model end |
#plan ⇒ Object
The plan this object belongs to
141 142 143 |
# File 'lib/roby/plan_object.rb', line 141 def plan @plan end |
#promise_executor ⇒ Object (readonly)
A thread pool that ensures that any work queued using #promise is serialized
23 24 25 |
# File 'lib/roby/plan_object.rb', line 23 def promise_executor @promise_executor end |
#removed_at ⇒ Object
The place where this object has been removed from its plan. Once an object is removed from its plan, it cannot be added back again.
154 155 156 |
# File 'lib/roby/plan_object.rb', line 154 def removed_at @removed_at end |
Instance Method Details
#add_child_object(child, type, info = nil) ⇒ Object
Synchronizes the plan of this object from the one of its peer
436 437 438 439 440 441 442 443 444 445 446 447 |
# File 'lib/roby/plan_object.rb', line 436 def add_child_object(child, type, info = nil) # :nodoc: if child.plan != plan root_object.synchronize_plan(child.root_object) if !type.kind_of?(Class) # If given a graph, we need to re-resolve it as #plan might # have changed type = relation_graphs.fetch(type.class) end end super end |
#apply_relation_changes(object, changes) ⇒ Object
Transfers a set of relations from this plan object to object. changes is formatted as a sequence of relation, parents, children slices, where parents and children are sets of objects.
For each of these slices, the method removes the parent->self and self->child edges in the given relation, and then adds the corresponding parent->object and object->child edges.
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 |
# File 'lib/roby/plan_object.rb', line 465 def apply_relation_changes(object, changes) # The operation is done in two parts to avoid problems with # creating cycles in the graph: first we remove the old edges, then # we add the new ones. changes.each_slice(3) do |rel, parents, children| next if rel.copy_on_replace? parents.each_slice(2) do |parent, info| parent.remove_child_object(self, rel) end children.each_slice(2) do |child, info| remove_child_object(child, rel) end end changes.each_slice(3) do |rel, parents, children| parents.each_slice(2) do |parent, info| parent.add_child_object(object, rel, info) end children.each_slice(2) do |child, info| object.add_child_object(child, rel, info) end end end |
#as_plan ⇒ Object
Used in plan management as a way to extract a plan object from any object
197 |
# File 'lib/roby/plan_object.rb', line 197 def as_plan; self end |
#can_finalize? ⇒ Boolean
Whether this task can be finalized
Unlike plan-level structure, a task that is marked as keepalive will be processed by garbage collection (e.g. stopping it). However, it will not be finalized (removed from the plan). The garbage collection will clear its relations instead of finalizing it
The default returns true
39 40 41 |
# File 'lib/roby/plan_object.rb', line 39 def can_finalize? true end |
#commit_transaction ⇒ Object
Method called to apply modifications needed to commit this object into the underlying plan
For instance, Task will make sure that argument objects that are transaction proxies are unwrapped
Note that the method should only deal with modifications that are internal to the task itself. Modifications of the relation graphs are handled by Transaction itself in Transaction#apply_modifications_to_plan
The default implementation does nothing
388 389 |
# File 'lib/roby/plan_object.rb', line 388 def commit_transaction end |
#concrete_model ⇒ Object
The non-specialized model for self
It is always self.class
16 |
# File 'lib/roby/plan_object.rb', line 16 def concrete_model; self.class end |
#connection_space ⇒ Object
25 26 27 28 29 |
# File 'lib/roby/plan_object.rb', line 25 def connection_space if plan plan.connection_space end end |
#each_finalization_handler {|block| ... } ⇒ void
This method returns an undefined value.
Enumerates the finalization handlers that should be applied in finalized!
537 538 539 540 541 542 543 544 |
# File 'lib/roby/plan_object.rb', line 537 def each_finalization_handler(&block) finalization_handlers.each do |handler| yield(handler.block) end self.class.each_finalization_handler do |model_handler| model_handler.bind(self).call(&block) end end |
#each_in_neighbour_merged(relation, intrusive: nil, &block) ⇒ Object
222 223 224 225 226 227 228 229 |
# File 'lib/roby/plan_object.rb', line 222 def each_in_neighbour_merged(relation, intrusive: nil, &block) if intrusive.nil? raise ArgumentError, "you must give a true or false to the intrusive flag" end merged_relations( proc { |o, &b| o.each_in_neighbour(relation, &b) }, intrusive, &block) end |
#each_out_neighbour_merged(relation, intrusive: nil, &block) ⇒ Object
231 232 233 234 235 236 237 238 |
# File 'lib/roby/plan_object.rb', line 231 def each_out_neighbour_merged(relation, intrusive: nil, &block) if intrusive.nil? raise ArgumentError, "you must give a true or false to the intrusive flag" end merged_relations( proc { |o, &b| o.each_out_neighbour(relation, &b) }, intrusive, &block) end |
#each_plan_child ⇒ Object
Iterates on all the children of this root object
454 |
# File 'lib/roby/plan_object.rb', line 454 def each_plan_child; self end |
#engine ⇒ Object
use #execution_engine instead
144 145 146 147 |
# File 'lib/roby/plan_object.rb', line 144 def engine Roby.warn_deprecated "PlanObject#engine is deprecated, use #execution_engine instead" execution_engine end |
#executable? ⇒ Boolean
If this object is executable
341 342 343 |
# File 'lib/roby/plan_object.rb', line 341 def executable? @executable || (@executable.nil? && !garbage? && plan && plan.executable?) end |
#finalized!(timestamp = nil) ⇒ void
This method returns an undefined value.
Called when a particular object has been removed from its plan
If PlanObject.debug_finalization_place? is true (set with PlanObject.debug_finalization_place=, the backtrace in this call is stored in #removed_at. It is false by default, as it is pretty expensive.
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 |
# File 'lib/roby/plan_object.rb', line 556 def finalized!( = nil) if self.plan.executable? # call finalization handlers each_finalization_handler do |handler| handler.call(self) end end if root_object? self.plan = nil if PlanObject.debug_finalization_place? self.removed_at = caller else self.removed_at = [] end self.finalization_time = || Time.now self.finalized = true end end |
#finalized? ⇒ Boolean
True if this object has been included in a plan, but has been removed from it since
158 |
# File 'lib/roby/plan_object.rb', line 158 def finalized?; !!removed_at end |
#forget_peer(peer) ⇒ Object
Called when all links to peer should be removed.
424 425 426 427 428 429 430 431 432 433 |
# File 'lib/roby/plan_object.rb', line 424 def forget_peer(peer) if !root_object? raise ArgumentError, "#{self} is not root" end each_plan_child do |child| child.forget_peer(peer) end super end |
#fullfills?(models) ⇒ Boolean
Returns true if this object provides all the given models.
604 605 606 607 608 |
# File 'lib/roby/plan_object.rb', line 604 def fullfills?(models) Array(models).all? do |m| self.model <= m end end |
#garbage! ⇒ Object
Mark this task as garbage
Garbage tasks cannot be involved in new relations, and cannot be executed
Once set, this flag cannot be unset
361 362 363 |
# File 'lib/roby/plan_object.rb', line 361 def garbage! @garbage = true end |
#garbage? ⇒ Object
Whether this task has been marked as garbage by the garbage collection process
351 |
# File 'lib/roby/plan_object.rb', line 351 attr_predicate :garbage? |
#initialize_copy(other) ⇒ Object
131 132 133 134 135 136 137 138 |
# File 'lib/roby/plan_object.rb', line 131 def initialize_copy(other) # Consider whether subclasses initialized the plan before calling us # ... in which case we handle it specially and propagate it super @plan = nil @relation_graphs = nil @finalization_handlers = other.finalization_handlers.dup end |
#initialize_replacement(object) ⇒ Object
Called by #replace_by and #replace_subplan_by to do object-specific initialization of object when object is used to replace self in a plan
The default implementation does nothing
509 510 511 512 513 514 515 516 |
# File 'lib/roby/plan_object.rb', line 509 def initialize_replacement(object) finalization_handlers.each do |handler| if handler.copy_on_replace? object ||= yield object.when_finalized(handler., &handler.block) end end end |
#merged_relations(enumerator, intrusive) ⇒ Object
call-seq:
merged_relation(enumeration_method, false[, arg1, arg2]) do |self_t, |
end
merged_relation(enumeration_method, true[, arg1, arg2]) do ||
end
It is assumed that enumeration_method is the name of a method on self that will yield an object related to self.
This method computes the same set of related objects, but does so while merging all the changes that underlying transactions may have applied. I.e. it is equivalent to calling enumeration_method on the plan that would be the result of the application of the whole transaction stack
If instrusive is false, the edges are yielded at the level they appear. I.e. both self and the related object are given, and
- self_t, related_t
-
may be part of a parent plan of self.plan. I.e.
self_t is either self itself, or the task that self represents in a parent plan / transaction.
If instrusive is true, the related objects are recursively added to all transactions in the transaction stack, and are given at the end. I.e. only the related object is yield, and it is guaranteed to be included in self.plan.
For instance,
merged_relations(:each_child, false) do |parent, child|
...
end
yields the children of self according to the modifications that the transactions apply, but may do so in the transaction’s parent plans.
merged_relations(:each_child, true) do |child|
...
end
Will yield the same set of tasks, but included in self.plan.
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/roby/plan_object.rb', line 280 def merged_relations(enumerator, intrusive) return enum_for(__method__, enumerator, intrusive) if !block_given? plan_chain = self.transaction_stack object = self.real_object enumerator = enumerator.to_proc pending = Array.new while plan_chain.size > 1 plan = plan_chain.pop next_plan = plan_chain.last # Objects that are in +plan+ but not in +next_plan+ are # automatically added, as +next_plan+ is not able to change # them. Those that are included in +next_plan+ are handled # later. new_objects = Array.new enumerator.call(object) do || next if next_plan[, create: false] if !intrusive yield(object, ) else new_objects << end end # Here, pending contains objects from the previous plan (i.e. in # plan.plan). Proxy them in +plan+. # # It is important to do that *after* we enumerated the relations # that exist in +plan+ (above), as it reduces the number of # relations at each level. pending.map! { |t| plan[t] } # And add the new objects that we just discovered pending.concat(new_objects) if next_plan object = next_plan[object] end end if intrusive enumerator.call(self, &Proc.new) for in pending yield(self.plan[]) end else enumerator.call(self) do || yield(self, ) end end end |
#promise(description: "#{self}.promise", executor: promise_executor, &block) ⇒ Promise
Create a promise that is serialized with all promises created for this object
191 192 193 |
# File 'lib/roby/plan_object.rb', line 191 def promise(description: "#{self}.promise", executor: promise_executor, &block) execution_engine.promise(description: description, executor: executor, &block) end |
#read_write? ⇒ Boolean
True if this object can be modified by the local plan manager
519 520 521 522 523 524 525 |
# File 'lib/roby/plan_object.rb', line 519 def read_write? if self_owned? true elsif plan.self_owned? owners.all? { |p| plan.owned_by?(p) } end end |
#real_object ⇒ Object
If self is a transaction proxy, returns the underlying plan object, regardless of how many transactions there is on the stack. Otherwise, return self.
202 203 204 205 206 207 208 |
# File 'lib/roby/plan_object.rb', line 202 def real_object result = self while result.respond_to?(:__getobj__) result = result.__getobj__ end result end |
#remotely_useful? ⇒ Boolean
True if this object is useful for one of our peers
398 |
# File 'lib/roby/plan_object.rb', line 398 def remotely_useful?; (plan && plan.remotely_useful?) || super end |
#replace_by(object) ⇒ Object
Replaces self by object in all graphs self is part of.
500 501 502 |
# File 'lib/roby/plan_object.rb', line 500 def replace_by(object) raise NotImplementedError, "#{self.class} did not reimplement #replace_by" end |
#replace_subplan_by(object) ⇒ Object
Replaces, in the plan, the subplan generated by this plan object by the one generated by object. In practice, it means that we transfer all parent edges whose target is self from the receiver to object. It calls the various add/remove hooks defined in Relations::DirectedRelationSupport.
495 496 497 |
# File 'lib/roby/plan_object.rb', line 495 def replace_subplan_by(object) raise NotImplementedError, "#{self.class} did not reimplement #replace_subplan_by" end |
#root_object ⇒ Object
Return the root plan object for this object.
450 |
# File 'lib/roby/plan_object.rb', line 450 def root_object; self end |
#root_object? ⇒ Boolean
True if this object is a root object in the plan.
452 |
# File 'lib/roby/plan_object.rb', line 452 def root_object?; root_object == self end |
#subscribed? ⇒ Boolean
True if we are explicitely subscribed to this object
366 367 368 369 370 371 372 373 374 |
# File 'lib/roby/plan_object.rb', line 366 def subscribed? if root_object? (plan && plan.subscribed?) || (!self_owned? && owners.any? { |peer| peer.subscribed_plan? }) || super else root_object.subscribed? end end |
#transaction_proxy? ⇒ Boolean
True if this object is a transaction proxy, false otherwise
150 |
# File 'lib/roby/plan_object.rb', line 150 def transaction_proxy?; false end |
#transaction_stack ⇒ Object
Returns the stack of transactions/plans this object is part of, starting with self.plan.
212 213 214 215 216 217 218 219 220 |
# File 'lib/roby/plan_object.rb', line 212 def transaction_stack result = [plan] obj = self while obj.respond_to?(:__getobj__) obj = obj.__getobj__ result << obj.plan end result end |
#update_on?(peer) ⇒ Boolean
True if we should send updates about this object to peer
394 |
# File 'lib/roby/plan_object.rb', line 394 def update_on?(peer); (plan && plan.update_on?(peer)) || super end |
#updated_by?(peer) ⇒ Boolean
True if we receive updates for this object from peer
396 |
# File 'lib/roby/plan_object.rb', line 396 def updated_by?(peer); (plan && plan.updated_by?(peer)) || super end |
#when_finalized(options = Hash.new) {|task| ... } ⇒ void
This method returns an undefined value.
Called when the task gets finalized, i.e. removed from the main plan
586 587 588 589 590 |
# File 'lib/roby/plan_object.rb', line 586 def when_finalized( = Hash.new, &block) = InstanceHandler. check_arity(block, 1) finalization_handlers << InstanceHandler.new(block, ([:on_replace] == :copy)) end |