roby 0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. data/.gitignore +29 -0
  2. data/History.txt +4 -0
  3. data/License-fr.txt +519 -0
  4. data/License.txt +515 -0
  5. data/Manifest.txt +245 -0
  6. data/NOTES +4 -0
  7. data/README.txt +163 -0
  8. data/Rakefile +161 -0
  9. data/TODO.txt +146 -0
  10. data/app/README.txt +24 -0
  11. data/app/Rakefile +8 -0
  12. data/app/config/ROBOT.rb +5 -0
  13. data/app/config/app.yml +91 -0
  14. data/app/config/init.rb +7 -0
  15. data/app/config/roby.yml +3 -0
  16. data/app/controllers/.gitattributes +0 -0
  17. data/app/controllers/ROBOT.rb +2 -0
  18. data/app/data/.gitattributes +0 -0
  19. data/app/planners/ROBOT/main.rb +6 -0
  20. data/app/planners/main.rb +5 -0
  21. data/app/scripts/distributed +3 -0
  22. data/app/scripts/generate/bookmarks +3 -0
  23. data/app/scripts/replay +3 -0
  24. data/app/scripts/results +3 -0
  25. data/app/scripts/run +3 -0
  26. data/app/scripts/server +3 -0
  27. data/app/scripts/shell +3 -0
  28. data/app/scripts/test +3 -0
  29. data/app/tasks/.gitattributes +0 -0
  30. data/app/tasks/ROBOT/.gitattributes +0 -0
  31. data/bin/roby +210 -0
  32. data/bin/roby-log +168 -0
  33. data/bin/roby-shell +25 -0
  34. data/doc/images/event_generalization.png +0 -0
  35. data/doc/images/exception_propagation_1.png +0 -0
  36. data/doc/images/exception_propagation_2.png +0 -0
  37. data/doc/images/exception_propagation_3.png +0 -0
  38. data/doc/images/exception_propagation_4.png +0 -0
  39. data/doc/images/exception_propagation_5.png +0 -0
  40. data/doc/images/replay_handler_error.png +0 -0
  41. data/doc/images/replay_handler_error_0.png +0 -0
  42. data/doc/images/replay_handler_error_1.png +0 -0
  43. data/doc/images/roby_cycle_overview.png +0 -0
  44. data/doc/images/roby_replay_02.png +0 -0
  45. data/doc/images/roby_replay_03.png +0 -0
  46. data/doc/images/roby_replay_04.png +0 -0
  47. data/doc/images/roby_replay_event_representation.png +0 -0
  48. data/doc/images/roby_replay_first_state.png +0 -0
  49. data/doc/images/roby_replay_relations.png +0 -0
  50. data/doc/images/roby_replay_startup.png +0 -0
  51. data/doc/images/task_event_generalization.png +0 -0
  52. data/doc/papers.rdoc +11 -0
  53. data/doc/styles/allison.css +314 -0
  54. data/doc/styles/allison.js +316 -0
  55. data/doc/styles/allison.rb +276 -0
  56. data/doc/styles/jamis.rb +593 -0
  57. data/doc/tutorials/01-GettingStarted.rdoc +86 -0
  58. data/doc/tutorials/02-GoForward.rdoc +220 -0
  59. data/doc/tutorials/03-PlannedPath.rdoc +268 -0
  60. data/doc/tutorials/04-EventPropagation.rdoc +236 -0
  61. data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
  62. data/doc/tutorials/06-Overview.rdoc +40 -0
  63. data/doc/videos.rdoc +69 -0
  64. data/ext/droby/dump.cc +175 -0
  65. data/ext/droby/extconf.rb +3 -0
  66. data/ext/graph/algorithm.cc +746 -0
  67. data/ext/graph/extconf.rb +7 -0
  68. data/ext/graph/graph.cc +529 -0
  69. data/ext/graph/graph.hh +183 -0
  70. data/ext/graph/iterator_sequence.hh +102 -0
  71. data/ext/graph/undirected_dfs.hh +226 -0
  72. data/ext/graph/undirected_graph.hh +421 -0
  73. data/lib/roby.rb +41 -0
  74. data/lib/roby/app.rb +870 -0
  75. data/lib/roby/app/rake.rb +56 -0
  76. data/lib/roby/app/run.rb +14 -0
  77. data/lib/roby/app/scripts/distributed.rb +13 -0
  78. data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
  79. data/lib/roby/app/scripts/replay.rb +31 -0
  80. data/lib/roby/app/scripts/results.rb +15 -0
  81. data/lib/roby/app/scripts/run.rb +26 -0
  82. data/lib/roby/app/scripts/server.rb +18 -0
  83. data/lib/roby/app/scripts/shell.rb +88 -0
  84. data/lib/roby/app/scripts/test.rb +40 -0
  85. data/lib/roby/basic_object.rb +151 -0
  86. data/lib/roby/config.rb +5 -0
  87. data/lib/roby/control.rb +747 -0
  88. data/lib/roby/decision_control.rb +17 -0
  89. data/lib/roby/distributed.rb +32 -0
  90. data/lib/roby/distributed/base.rb +440 -0
  91. data/lib/roby/distributed/communication.rb +871 -0
  92. data/lib/roby/distributed/connection_space.rb +592 -0
  93. data/lib/roby/distributed/distributed_object.rb +206 -0
  94. data/lib/roby/distributed/drb.rb +62 -0
  95. data/lib/roby/distributed/notifications.rb +539 -0
  96. data/lib/roby/distributed/peer.rb +550 -0
  97. data/lib/roby/distributed/protocol.rb +529 -0
  98. data/lib/roby/distributed/proxy.rb +343 -0
  99. data/lib/roby/distributed/subscription.rb +311 -0
  100. data/lib/roby/distributed/transaction.rb +498 -0
  101. data/lib/roby/event.rb +897 -0
  102. data/lib/roby/exceptions.rb +234 -0
  103. data/lib/roby/executives/simple.rb +30 -0
  104. data/lib/roby/graph.rb +166 -0
  105. data/lib/roby/interface.rb +390 -0
  106. data/lib/roby/log.rb +3 -0
  107. data/lib/roby/log/chronicle.rb +303 -0
  108. data/lib/roby/log/console.rb +72 -0
  109. data/lib/roby/log/data_stream.rb +197 -0
  110. data/lib/roby/log/dot.rb +279 -0
  111. data/lib/roby/log/event_stream.rb +151 -0
  112. data/lib/roby/log/file.rb +340 -0
  113. data/lib/roby/log/gui/basic_display.ui +83 -0
  114. data/lib/roby/log/gui/chronicle.rb +26 -0
  115. data/lib/roby/log/gui/chronicle_view.rb +40 -0
  116. data/lib/roby/log/gui/chronicle_view.ui +70 -0
  117. data/lib/roby/log/gui/data_displays.rb +172 -0
  118. data/lib/roby/log/gui/data_displays.ui +155 -0
  119. data/lib/roby/log/gui/notifications.rb +26 -0
  120. data/lib/roby/log/gui/relations.rb +248 -0
  121. data/lib/roby/log/gui/relations.ui +123 -0
  122. data/lib/roby/log/gui/relations_view.rb +185 -0
  123. data/lib/roby/log/gui/relations_view.ui +149 -0
  124. data/lib/roby/log/gui/replay.rb +327 -0
  125. data/lib/roby/log/gui/replay_controls.rb +200 -0
  126. data/lib/roby/log/gui/replay_controls.ui +259 -0
  127. data/lib/roby/log/gui/runtime.rb +130 -0
  128. data/lib/roby/log/hooks.rb +185 -0
  129. data/lib/roby/log/logger.rb +202 -0
  130. data/lib/roby/log/notifications.rb +244 -0
  131. data/lib/roby/log/plan_rebuilder.rb +470 -0
  132. data/lib/roby/log/relations.rb +1056 -0
  133. data/lib/roby/log/server.rb +550 -0
  134. data/lib/roby/log/sqlite.rb +47 -0
  135. data/lib/roby/log/timings.rb +164 -0
  136. data/lib/roby/plan-object.rb +247 -0
  137. data/lib/roby/plan.rb +762 -0
  138. data/lib/roby/planning.rb +13 -0
  139. data/lib/roby/planning/loops.rb +302 -0
  140. data/lib/roby/planning/model.rb +906 -0
  141. data/lib/roby/planning/task.rb +151 -0
  142. data/lib/roby/propagation.rb +562 -0
  143. data/lib/roby/query.rb +619 -0
  144. data/lib/roby/relations.rb +583 -0
  145. data/lib/roby/relations/conflicts.rb +70 -0
  146. data/lib/roby/relations/ensured.rb +20 -0
  147. data/lib/roby/relations/error_handling.rb +23 -0
  148. data/lib/roby/relations/events.rb +9 -0
  149. data/lib/roby/relations/executed_by.rb +193 -0
  150. data/lib/roby/relations/hierarchy.rb +239 -0
  151. data/lib/roby/relations/influence.rb +10 -0
  152. data/lib/roby/relations/planned_by.rb +63 -0
  153. data/lib/roby/robot.rb +7 -0
  154. data/lib/roby/standard_errors.rb +218 -0
  155. data/lib/roby/state.rb +5 -0
  156. data/lib/roby/state/events.rb +221 -0
  157. data/lib/roby/state/information.rb +55 -0
  158. data/lib/roby/state/pos.rb +110 -0
  159. data/lib/roby/state/shapes.rb +32 -0
  160. data/lib/roby/state/state.rb +353 -0
  161. data/lib/roby/support.rb +92 -0
  162. data/lib/roby/task-operations.rb +182 -0
  163. data/lib/roby/task.rb +1618 -0
  164. data/lib/roby/test/common.rb +399 -0
  165. data/lib/roby/test/distributed.rb +214 -0
  166. data/lib/roby/test/tasks/empty_task.rb +9 -0
  167. data/lib/roby/test/tasks/goto.rb +36 -0
  168. data/lib/roby/test/tasks/simple_task.rb +23 -0
  169. data/lib/roby/test/testcase.rb +519 -0
  170. data/lib/roby/test/tools.rb +160 -0
  171. data/lib/roby/thread_task.rb +87 -0
  172. data/lib/roby/transactions.rb +462 -0
  173. data/lib/roby/transactions/proxy.rb +292 -0
  174. data/lib/roby/transactions/updates.rb +139 -0
  175. data/plugins/fault_injection/History.txt +4 -0
  176. data/plugins/fault_injection/README.txt +37 -0
  177. data/plugins/fault_injection/Rakefile +18 -0
  178. data/plugins/fault_injection/TODO.txt +0 -0
  179. data/plugins/fault_injection/app.rb +52 -0
  180. data/plugins/fault_injection/fault_injection.rb +89 -0
  181. data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
  182. data/plugins/subsystems/README.txt +40 -0
  183. data/plugins/subsystems/Rakefile +18 -0
  184. data/plugins/subsystems/app.rb +171 -0
  185. data/plugins/subsystems/test/app/README +24 -0
  186. data/plugins/subsystems/test/app/Rakefile +8 -0
  187. data/plugins/subsystems/test/app/config/app.yml +71 -0
  188. data/plugins/subsystems/test/app/config/init.rb +9 -0
  189. data/plugins/subsystems/test/app/config/roby.yml +3 -0
  190. data/plugins/subsystems/test/app/planners/main.rb +20 -0
  191. data/plugins/subsystems/test/app/scripts/distributed +3 -0
  192. data/plugins/subsystems/test/app/scripts/replay +3 -0
  193. data/plugins/subsystems/test/app/scripts/results +3 -0
  194. data/plugins/subsystems/test/app/scripts/run +3 -0
  195. data/plugins/subsystems/test/app/scripts/server +3 -0
  196. data/plugins/subsystems/test/app/scripts/shell +3 -0
  197. data/plugins/subsystems/test/app/scripts/test +3 -0
  198. data/plugins/subsystems/test/app/tasks/services.rb +15 -0
  199. data/plugins/subsystems/test/test_subsystems.rb +71 -0
  200. data/test/distributed/test_communication.rb +178 -0
  201. data/test/distributed/test_connection.rb +282 -0
  202. data/test/distributed/test_execution.rb +373 -0
  203. data/test/distributed/test_mixed_plan.rb +341 -0
  204. data/test/distributed/test_plan_notifications.rb +238 -0
  205. data/test/distributed/test_protocol.rb +516 -0
  206. data/test/distributed/test_query.rb +102 -0
  207. data/test/distributed/test_remote_plan.rb +491 -0
  208. data/test/distributed/test_transaction.rb +463 -0
  209. data/test/mockups/tasks.rb +27 -0
  210. data/test/planning/test_loops.rb +380 -0
  211. data/test/planning/test_model.rb +427 -0
  212. data/test/planning/test_task.rb +106 -0
  213. data/test/relations/test_conflicts.rb +42 -0
  214. data/test/relations/test_ensured.rb +38 -0
  215. data/test/relations/test_executed_by.rb +149 -0
  216. data/test/relations/test_hierarchy.rb +158 -0
  217. data/test/relations/test_planned_by.rb +54 -0
  218. data/test/suite_core.rb +24 -0
  219. data/test/suite_distributed.rb +9 -0
  220. data/test/suite_planning.rb +3 -0
  221. data/test/suite_relations.rb +8 -0
  222. data/test/test_bgl.rb +508 -0
  223. data/test/test_control.rb +399 -0
  224. data/test/test_event.rb +894 -0
  225. data/test/test_exceptions.rb +592 -0
  226. data/test/test_interface.rb +37 -0
  227. data/test/test_log.rb +114 -0
  228. data/test/test_log_server.rb +132 -0
  229. data/test/test_plan.rb +584 -0
  230. data/test/test_propagation.rb +210 -0
  231. data/test/test_query.rb +266 -0
  232. data/test/test_relations.rb +180 -0
  233. data/test/test_state.rb +414 -0
  234. data/test/test_support.rb +16 -0
  235. data/test/test_task.rb +938 -0
  236. data/test/test_testcase.rb +122 -0
  237. data/test/test_thread_task.rb +73 -0
  238. data/test/test_transactions.rb +569 -0
  239. data/test/test_transactions_proxy.rb +198 -0
  240. metadata +570 -0
@@ -0,0 +1,17 @@
1
+ module Roby
2
+ class DecisionControl
3
+ def conflict(starting_task, running_tasks)
4
+ for t in running_tasks
5
+ starting_task.event(:start).postpone t.event(:stop)
6
+ return
7
+ end
8
+ end
9
+ end
10
+
11
+ class << self
12
+ attr_reader :decision_control
13
+ end
14
+
15
+ @decision_control = DecisionControl.new
16
+ end
17
+
@@ -0,0 +1,32 @@
1
+ require 'roby'
2
+ require 'roby/distributed/drb'
3
+ require 'roby/distributed/protocol'
4
+
5
+ require 'roby/distributed/proxy'
6
+ require 'roby/distributed/connection_space'
7
+ require 'roby/distributed/notifications'
8
+ require 'roby/distributed/peer'
9
+ require 'roby/distributed/transaction'
10
+
11
+ # == Communication protocol (and code namespace structure)
12
+ # == Getting remote objects
13
+ # There is actually two ways to get a remote object
14
+ # * the object has been explicitely subscribed to by calling Peer#subscribe
15
+ # * the object has been sent to us because it is linked to an object we own
16
+ # or an object we are subscribed to
17
+ #
18
+ # In the first case, the object must be referenced in the first place. It can
19
+ # have been sent to us as a query result (see Query), or because it has been
20
+ # involved in a distributed transaction. In the second case, it is either us
21
+ # which have added the relation, or the remote peer. If it is us, we should
22
+ # have subscribed to the object, added the relation, and then we may
23
+ # unsubscribe to the object.
24
+ #
25
+ # We forget about a remote object when Plan#garbage_collect removes it.
26
+ #
27
+ # == Subscription management
28
+ # The pDB gets updated about all objects it is subscribed to.
29
+ module Roby::Distributed
30
+ end
31
+
32
+
@@ -0,0 +1,440 @@
1
+ require 'drb'
2
+
3
+ # A thread-safe reference-counting class
4
+ class RefCounting
5
+ def initialize
6
+ @values = Hash.new(0)
7
+ @mutex = Mutex.new
8
+ end
9
+
10
+ # True if +obj+ is referenced
11
+ def ref?(obj); @mutex.synchronize { @values[obj] > 0 } end
12
+ # Dereference +obj+ by one
13
+ def deref(obj)
14
+ @mutex.synchronize do
15
+ if (@values[obj] -= 1) == 0
16
+ @values.delete(obj)
17
+ return true
18
+ end
19
+ end
20
+ false
21
+ end
22
+ # Add +1 to the reference count of +obj+
23
+ def ref(obj)
24
+ @mutex.synchronize do
25
+ @values[obj] += 1
26
+ end
27
+ end
28
+ # Returns the set of referenced objects
29
+ def referenced_objects
30
+ @mutex.synchronize do
31
+ @values.keys
32
+ end
33
+ end
34
+ # Remove +object+ from the set of referenced objects, regardless of its
35
+ # reference count
36
+ def delete(object)
37
+ @mutex.synchronize do
38
+ @values.delete(object)
39
+ end
40
+ end
41
+ end
42
+
43
+ class Object
44
+ def initialize_copy(old) # :nodoc:
45
+ super
46
+ @__droby_remote_id__ = nil
47
+ end
48
+
49
+ # The Roby::Distributed::RemoteID for this object
50
+ def remote_id
51
+ @__droby_remote_id__ ||= Roby::Distributed::RemoteID.from_object(self)
52
+ end
53
+ end
54
+
55
+ class DRbObject
56
+ # We don't want this method to call the remote object.
57
+ def to_s
58
+ inspect
59
+ end
60
+ # Converts this DRbObject into Roby::Distributed::RemoteID
61
+ def remote_id
62
+ @__droby_remote_id__ ||= Roby::Distributed::RemoteID.new(__drburi, __drbref)
63
+ end
64
+ end
65
+
66
+ module Roby
67
+ module Distributed
68
+ DISCOVERY_RING_PORT = 48901
69
+ DEFAULT_DROBY_PORT = 48902
70
+
71
+ class InvalidRemoteOperation < RuntimeError; end
72
+
73
+ class InvalidRemoteTaskOperation < InvalidRemoteOperation
74
+ attr_reader :task
75
+ def initialize(task); @task = task end
76
+ end
77
+
78
+ extend Logger::Hierarchy
79
+ extend Logger::Forward
80
+
81
+ # RemoteID objects are used in dRoby to reference objects on other
82
+ # peers. It uses the same mechanisms that DRbObject but is not
83
+ # converted back into a local object automatically, and does not allow
84
+ # to call remote methods on a remote object.
85
+ class RemoteID
86
+ # The URI of the DRb server
87
+ attr_reader :uri
88
+ # The reference ID of the object on the DRb server
89
+ attr_reader :ref
90
+
91
+ # Creates a new RemoteID with the given URI and ID
92
+ def initialize(uri, ref)
93
+ @uri, @ref = uri, ref.to_int
94
+ @hash = [uri, ref].hash
95
+ end
96
+
97
+ def _dump(lvl) # :nodoc:
98
+ @__droby_marshalled__ ||= Marshal.dump([uri, ref])
99
+ end
100
+ def self._load(str) # :nodoc:
101
+ new(*Marshal.load(str))
102
+ end
103
+
104
+ def ==(other) # :nodoc:
105
+ other.kind_of?(RemoteID) && other.ref == ref && other.uri == uri
106
+ end
107
+ alias :eql? :==
108
+ attr_reader :hash
109
+
110
+ # True if this object references a local object
111
+ def local?; DRb.here?(uri) end
112
+ # If this ID references a local object, returns it. Otherwise, returns self.
113
+ def local_object
114
+ if DRb.here?(uri)
115
+ DRb.to_obj(ref)
116
+ else
117
+ self
118
+ end
119
+ end
120
+
121
+ def to_s(peer = nil)
122
+ if peer
123
+ "0x#{Object.address_from_id(ref).to_s(16)}@#{peer.name}"
124
+ else
125
+ "0x#{Object.address_from_id(ref).to_s(16)}@#{uri}"
126
+ end
127
+ end
128
+ def inspect; to_s end
129
+ def pretty_print(pp); pp.text to_s end
130
+
131
+ def to_local(peer, create)
132
+ object = local_object
133
+ if object.kind_of?(RemoteID)
134
+ if local_proxy = peer.proxies[object]
135
+ return peer.proxy_setup(local_proxy)
136
+ elsif !create
137
+ return
138
+ elsif peer.removing_proxies.has_key?(object)
139
+ marshalled_object = peer.removing_proxies[object].last
140
+ Distributed.debug "reusing marshalled #{marshalled_object} for #{self} from #{peer}"
141
+ marshalled_object.remote_siblings.delete(Distributed.droby_dump)
142
+ marshalled_object.remote_siblings[peer.droby_dump] = self
143
+
144
+ if marshalled_object.respond_to?(:plan) && !marshalled_object.plan
145
+ # Take care of the "proxy is GCed while the peer
146
+ # sends us messages about it" case. In this case,
147
+ # the object has already been removed when it is
148
+ # marshalled (#plan == nil).
149
+ #
150
+ # This cannot happen in transactions: it only happens
151
+ # in plans where one side can remove an object while
152
+ # the other side is doing something on it
153
+ marshalled_object.instance_variable_set(:@plan, Roby.plan)
154
+ end
155
+
156
+ object = peer.local_object(marshalled_object)
157
+
158
+ if object.respond_to?(:plan) && !object.plan
159
+ raise "#{object} has no plan !"
160
+ end
161
+
162
+ return object
163
+ end
164
+ raise ArgumentError, "#{self} has no proxy"
165
+ else
166
+ object
167
+ end
168
+ end
169
+
170
+ private :remote_id
171
+
172
+ # Returns the RemoteID object for +obj+. This is actually
173
+ # equivalent to obj.remote_id
174
+ def self.from_object(obj)
175
+ Roby::Distributed::RemoteID.new(DRb.current_server.uri, DRb.to_id(obj) || 0)
176
+ end
177
+
178
+ # Creates a DRbObject corresponding to the object referenced by this RemoteID
179
+ def to_drb_object
180
+ DRbObject.new_with(uri, (ref == 0 ? nil : ref))
181
+ end
182
+ end
183
+
184
+ @updated_objects = ValueSet.new
185
+ @allowed_remote_access = Array.new
186
+ @keep = RefCounting.new
187
+ @removed_objects = ValueSet.new
188
+ class << self
189
+ # The one and only ConnectionSpace object
190
+ attr_reader :state
191
+
192
+ # Sets the #state attribute for Roby::Distributed
193
+ def state=(new_state)
194
+ if log = logger
195
+ if new_state
196
+ logger.progname = new_state.name
197
+ else
198
+ logger.progname = "Roby"
199
+ end
200
+ end
201
+ @state = new_state
202
+ end
203
+
204
+ # True if this plan manager owns +object+
205
+ def owns?(object); !state || state.owns?(object) end
206
+
207
+ # The set of objects we should temporarily keep because they are used
208
+ # in a callback mechanism (like a remote query or a trigger)
209
+ attr_reader :keep
210
+
211
+ # Compute the subset of +candidates+ that are to be considered as
212
+ # useful because of our peers and returns it.
213
+ #
214
+ # More specifically, an object will be included in the result if:
215
+ # * this plan manager is subscribed to it
216
+ # * the object is directly related to a self-owned object
217
+ # * if +include_subscriptions_relations+ is true, +object+ is
218
+ # directly related to a subscribed object.
219
+ #
220
+ # The method takes into account plan children in its computation:
221
+ # for instance, a task will be included in the result if one of
222
+ # its events meet the requirements described above.
223
+ #
224
+ # If +result+ is non-nil, the method adds the objects to +result+
225
+ # using #<< and returns it.
226
+ def remotely_useful_objects(candidates, include_subscriptions_relations, result = nil)
227
+ return ValueSet.new if candidates.empty?
228
+
229
+ result ||= Distributed.keep.referenced_objects.to_value_set
230
+
231
+ child_set = ValueSet.new
232
+ for obj in candidates
233
+ if result.include?(obj.root_object)
234
+ next
235
+ elsif obj.subscribed?
236
+ result << obj
237
+ next
238
+ end
239
+
240
+ not_found = obj.each_relation do |rel|
241
+ next unless rel.distribute? && rel.root_relation?
242
+
243
+ not_found = obj.each_parent_object(rel) do |parent|
244
+ parent = parent.root_object
245
+ if parent.distribute? &&
246
+ ((include_subscriptions_relations && parent.subscribed?) || parent.self_owned?)
247
+ result << obj.root_object
248
+ break
249
+ end
250
+ end
251
+ break unless not_found
252
+
253
+ not_found = obj.each_child_object(rel) do |child|
254
+ child = child.root_object
255
+ if child.distribute? &&
256
+ ((include_subscriptions_relations && child.subscribed?) || child.self_owned?)
257
+ result << obj.root_object
258
+ break
259
+ end
260
+ end
261
+ break unless not_found
262
+ end
263
+
264
+ if not_found && obj.respond_to?(:each_plan_child)
265
+ obj.each_plan_child { |plan_child| child_set << plan_child }
266
+ end
267
+ end
268
+
269
+ result.merge remotely_useful_objects(child_set, false, result)
270
+ end
271
+
272
+ # The list of objects that are being updated because of remote update
273
+ attr_reader :updated_objects
274
+
275
+ # True if we are updating +object+
276
+ def updating?(object)
277
+ updated_objects.include?(object)
278
+ end
279
+
280
+ # True if we are updating all objects in +objects+
281
+ def updating_all?(objects)
282
+ updated_objects.include_all?(objects.to_value_set)
283
+ end
284
+
285
+ # Call the block with the objects in +objects+ added to the
286
+ # updated_objects set
287
+ def update_all(objects)
288
+ old_updated_objects = @updated_objects
289
+ @updated_objects |= objects.to_value_set
290
+ yield
291
+ ensure
292
+ @updated_objects = old_updated_objects
293
+ end
294
+
295
+ # Call the block with the objects in +objects+ added to the
296
+ # updated_objects set
297
+ def update(object)
298
+ if object.respond_to?(:__getobj__) && !object.kind_of?(Roby::Transactions::Proxy)
299
+ object = object.__getobj__
300
+ end
301
+
302
+ included = unless updated_objects.include?(object)
303
+ @updated_objects << object
304
+ end
305
+
306
+ yield
307
+ ensure
308
+ @updated_objects.delete(object) if included
309
+ end
310
+
311
+ # Yields the relations of +object+ which are to be distributed
312
+ # among peers.
313
+ def each_object_relation(object)
314
+ object.each_relation do |rel|
315
+ yield(rel) if rel.distribute?
316
+ end
317
+ end
318
+
319
+ # The list of known peers. See ConnectionSpace#peers
320
+ def peers
321
+ if state then state.peers
322
+ else {}
323
+ end
324
+ end
325
+
326
+ # The set of objects that have been removed locally, but for which
327
+ # there are still references on our peers
328
+ attr_reader :removed_objects
329
+ end
330
+
331
+ @cycles_rx = Queue.new
332
+ @pending_cycles = Array.new
333
+ @pending_remote_events = Array.new
334
+
335
+ class << self
336
+ # The queue of cycles read by ConnectionSpace#receive and not processed
337
+ attr_reader :cycles_rx
338
+ # The set of cycles that have been read from #pending_cycles but
339
+ # have not been processed yet because the peers have disabled_rx? set
340
+ #
341
+ # This variable must be accessed only in the control thread
342
+ attr_reader :pending_cycles
343
+ end
344
+
345
+ # Extract data received so far from our peers and replays it if
346
+ # possible. Data can be ignored if RX is disabled with this peer
347
+ # (through Peer#disable_rx), or delayed if there is event propagation
348
+ # involved. In that last case, the events will be fired at the
349
+ # beginning of the next execution cycle and the remaining messages at
350
+ # the end of that same cycle.
351
+ def self.process_pending
352
+ delayed_cycles = []
353
+ while !(pending_cycles.empty? && cycles_rx.empty?)
354
+ peer, calls = if pending_cycles.empty?
355
+ cycles_rx.pop
356
+ else pending_cycles.shift
357
+ end
358
+
359
+ if peer.disabled_rx?
360
+ delayed_cycles.push [peer, calls]
361
+ else
362
+ if remaining = process_cycle(peer, calls)
363
+ delayed_cycles.push [peer, remaining]
364
+ end
365
+ end
366
+ end
367
+
368
+ ensure
369
+ @pending_cycles = delayed_cycles
370
+ end
371
+
372
+ # Process once cycle worth of data from the given peer.
373
+ def self.process_cycle(peer, calls)
374
+ from = Time.now
375
+ calls_size = calls.size
376
+
377
+ peer_server = peer.local_server
378
+ peer_server.processing = true
379
+
380
+ if !peer.connected?
381
+ return
382
+ end
383
+
384
+ while call_spec = calls.shift
385
+ return unless call_spec
386
+
387
+ is_callback, method, args, critical, message_id = *call_spec
388
+ Distributed.debug do
389
+ args_s = args.map { |obj| obj ? obj.to_s : 'nil' }
390
+ "processing #{is_callback ? 'callback' : 'method'} [#{message_id}]#{method}(#{args_s.join(", ")})"
391
+ end
392
+
393
+ result = catch(:ignore_this_call) do
394
+ peer_server.queued_completion = false
395
+ peer_server.current_message_id = message_id
396
+ peer_server.processing_callback = !!is_callback
397
+
398
+ result = begin
399
+ peer_server.send(method, *args)
400
+ rescue Exception => e
401
+ if critical
402
+ peer.fatal_error e, method, args
403
+ else
404
+ peer_server.completed!(e, true)
405
+ end
406
+ end
407
+
408
+ if !peer.connected?
409
+ return
410
+ end
411
+ result
412
+ end
413
+
414
+ if method != :completed && method != :completion_group && !peer.disconnecting? && !peer.disconnected?
415
+ if peer_server.queued_completion?
416
+ Distributed.debug "done and already queued the completion message"
417
+ else
418
+ Distributed.debug { "done, returns #{result || 'nil'}" }
419
+ peer.queue_call false, :completed, [result, false, message_id]
420
+ end
421
+ end
422
+
423
+ if peer.disabled_rx?
424
+ return calls
425
+ end
426
+
427
+ end
428
+
429
+ Distributed.debug "successfully served #{calls_size} calls in #{Time.now - from} seconds"
430
+ nil
431
+
432
+ rescue Exception => e
433
+ Distributed.info "error in dRoby processing: #{e.full_message}"
434
+ peer.disconnect
435
+
436
+ ensure
437
+ peer_server.processing = false
438
+ end
439
+ end
440
+ end