roby 0.7

Sign up to get free protection for your applications and to get access to all the features.
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