@@ -182,6 +182,8 @@ def __init__(
182
182
self ._node .add_waitable (self )
183
183
self ._logger = self ._node .get_logger ().get_child ('action_client' )
184
184
185
+ self ._lock = threading .Lock ()
186
+
185
187
def _generate_random_uuid (self ):
186
188
return UUID (uuid = list (uuid .uuid4 ().bytes ))
187
189
@@ -229,51 +231,57 @@ def _remove_pending_result_request(self, future):
229
231
# Start Waitable API
230
232
def is_ready (self , wait_set ):
231
233
"""Return True if one or more entities are ready in the wait set."""
232
- ready_entities = self ._client_handle .is_ready (wait_set )
233
- self ._is_feedback_ready = ready_entities [0 ]
234
- self ._is_status_ready = ready_entities [1 ]
235
- self ._is_goal_response_ready = ready_entities [2 ]
236
- self ._is_cancel_response_ready = ready_entities [3 ]
237
- self ._is_result_response_ready = ready_entities [4 ]
234
+ with self ._lock :
235
+ ready_entities = self ._client_handle .is_ready (wait_set )
236
+ self ._is_feedback_ready = ready_entities [0 ]
237
+ self ._is_status_ready = ready_entities [1 ]
238
+ self ._is_goal_response_ready = ready_entities [2 ]
239
+ self ._is_cancel_response_ready = ready_entities [3 ]
240
+ self ._is_result_response_ready = ready_entities [4 ]
238
241
return any (ready_entities )
239
242
240
243
def take_data (self ):
241
244
"""Take stuff from lower level so the wait set doesn't immediately wake again."""
242
245
data = {}
243
246
if self ._is_goal_response_ready :
244
- taken_data = self ._client_handle .take_goal_response (
245
- self ._action_type .Impl .SendGoalService .Response )
246
- # If take fails, then we get (None, None)
247
- if all (taken_data ):
248
- data ['goal' ] = taken_data
247
+ with self ._lock :
248
+ taken_data = self ._client_handle .take_goal_response (
249
+ self ._action_type .Impl .SendGoalService .Response )
250
+ # If take fails, then we get (None, None)
251
+ if all (taken_data ):
252
+ data ['goal' ] = taken_data
249
253
250
254
if self ._is_cancel_response_ready :
251
- taken_data = self ._client_handle .take_cancel_response (
252
- self ._action_type .Impl .CancelGoalService .Response )
253
- # If take fails, then we get (None, None)
254
- if all (taken_data ):
255
- data ['cancel' ] = taken_data
255
+ with self ._lock :
256
+ taken_data = self ._client_handle .take_cancel_response (
257
+ self ._action_type .Impl .CancelGoalService .Response )
258
+ # If take fails, then we get (None, None)
259
+ if all (taken_data ):
260
+ data ['cancel' ] = taken_data
256
261
257
262
if self ._is_result_response_ready :
258
- taken_data = self ._client_handle .take_result_response (
259
- self ._action_type .Impl .GetResultService .Response )
260
- # If take fails, then we get (None, None)
261
- if all (taken_data ):
262
- data ['result' ] = taken_data
263
+ with self ._lock :
264
+ taken_data = self ._client_handle .take_result_response (
265
+ self ._action_type .Impl .GetResultService .Response )
266
+ # If take fails, then we get (None, None)
267
+ if all (taken_data ):
268
+ data ['result' ] = taken_data
263
269
264
270
if self ._is_feedback_ready :
265
- taken_data = self ._client_handle .take_feedback (
266
- self ._action_type .Impl .FeedbackMessage )
267
- # If take fails, then we get None
268
- if taken_data is not None :
269
- data ['feedback' ] = taken_data
271
+ with self ._lock :
272
+ taken_data = self ._client_handle .take_feedback (
273
+ self ._action_type .Impl .FeedbackMessage )
274
+ # If take fails, then we get None
275
+ if taken_data is not None :
276
+ data ['feedback' ] = taken_data
270
277
271
278
if self ._is_status_ready :
272
- taken_data = self ._client_handle .take_status (
273
- self ._action_type .Impl .GoalStatusMessage )
274
- # If take fails, then we get None
275
- if taken_data is not None :
276
- data ['status' ] = taken_data
279
+ with self ._lock :
280
+ taken_data = self ._client_handle .take_status (
281
+ self ._action_type .Impl .GoalStatusMessage )
282
+ # If take fails, then we get None
283
+ if taken_data is not None :
284
+ data ['status' ] = taken_data
277
285
278
286
return data
279
287
@@ -354,12 +362,14 @@ async def execute(self, taken_data):
354
362
355
363
def get_num_entities (self ):
356
364
"""Return number of each type of entity used in the wait set."""
357
- num_entities = self ._client_handle .get_num_entities ()
365
+ with self ._lock :
366
+ num_entities = self ._client_handle .get_num_entities ()
358
367
return NumberOfEntities (* num_entities )
359
368
360
369
def add_to_wait_set (self , wait_set ):
361
370
"""Add entities to wait set."""
362
- self ._client_handle .add_to_waitset (wait_set )
371
+ with self ._lock :
372
+ self ._client_handle .add_to_waitset (wait_set )
363
373
364
374
def __enter__ (self ):
365
375
return self ._client_handle .__enter__ ()
@@ -437,23 +447,23 @@ def send_goal_async(self, goal, feedback_callback=None, goal_uuid=None):
437
447
request = self ._action_type .Impl .SendGoalService .Request ()
438
448
request .goal_id = self ._generate_random_uuid () if goal_uuid is None else goal_uuid
439
449
request .goal = goal
440
- sequence_number = self ._client_handle .send_goal_request (request )
441
- if sequence_number in self ._pending_goal_requests :
442
- raise RuntimeError (
443
- 'Sequence ({}) conflicts with pending goal request' .format (sequence_number ))
450
+ future = Future ()
451
+ with self ._lock :
452
+ sequence_number = self ._client_handle .send_goal_request (request )
453
+ if sequence_number in self ._pending_goal_requests :
454
+ raise RuntimeError (
455
+ 'Sequence ({}) conflicts with pending goal request' .format (sequence_number ))
456
+ self ._pending_goal_requests [sequence_number ] = future
457
+ self ._goal_sequence_number_to_goal_id [sequence_number ] = request .goal_id
458
+ future .add_done_callback (self ._remove_pending_goal_request )
459
+ # Add future so executor is aware
460
+ self .add_future (future )
444
461
445
462
if feedback_callback is not None :
446
463
# TODO(jacobperron): Move conversion function to a general-use package
447
464
goal_uuid = bytes (request .goal_id .uuid )
448
465
self ._feedback_callbacks [goal_uuid ] = feedback_callback
449
466
450
- future = Future ()
451
- self ._pending_goal_requests [sequence_number ] = future
452
- self ._goal_sequence_number_to_goal_id [sequence_number ] = request .goal_id
453
- future .add_done_callback (self ._remove_pending_goal_request )
454
- # Add future so executor is aware
455
- self .add_future (future )
456
-
457
467
return future
458
468
459
469
def _cancel_goal (self , goal_handle ):
@@ -495,16 +505,17 @@ def _cancel_goal_async(self, goal_handle):
495
505
496
506
cancel_request = CancelGoal .Request ()
497
507
cancel_request .goal_info .goal_id = goal_handle .goal_id
498
- sequence_number = self ._client_handle .send_cancel_request (cancel_request )
499
- if sequence_number in self ._pending_cancel_requests :
500
- raise RuntimeError (
501
- 'Sequence ({}) conflicts with pending cancel request' .format (sequence_number ))
502
-
503
508
future = Future ()
504
- self ._pending_cancel_requests [sequence_number ] = future
505
- future .add_done_callback (self ._remove_pending_cancel_request )
506
- # Add future so executor is aware
507
- self .add_future (future )
509
+ with self ._lock :
510
+ sequence_number = self ._client_handle .send_cancel_request (cancel_request )
511
+ if sequence_number in self ._pending_cancel_requests :
512
+ raise RuntimeError (
513
+ 'Sequence ({}) conflicts with pending cancel request' .format (sequence_number ))
514
+
515
+ self ._pending_cancel_requests [sequence_number ] = future
516
+ future .add_done_callback (self ._remove_pending_cancel_request )
517
+ # Add future so executor is aware
518
+ self .add_future (future )
508
519
509
520
return future
510
521
@@ -547,17 +558,18 @@ def _get_result_async(self, goal_handle):
547
558
548
559
result_request = self ._action_type .Impl .GetResultService .Request ()
549
560
result_request .goal_id = goal_handle .goal_id
550
- sequence_number = self ._client_handle .send_result_request (result_request )
551
- if sequence_number in self ._pending_result_requests :
552
- raise RuntimeError (
553
- 'Sequence ({}) conflicts with pending result request' .format (sequence_number ))
554
-
555
561
future = Future ()
556
- self ._pending_result_requests [sequence_number ] = future
557
- self ._result_sequence_number_to_goal_id [sequence_number ] = result_request .goal_id
558
- future .add_done_callback (self ._remove_pending_result_request )
559
- # Add future so executor is aware
560
- self .add_future (future )
562
+ with self ._lock :
563
+ sequence_number = self ._client_handle .send_result_request (result_request )
564
+ if sequence_number in self ._pending_result_requests :
565
+ raise RuntimeError (
566
+ 'Sequence ({}) conflicts with pending result request' .format (sequence_number ))
567
+
568
+ self ._pending_result_requests [sequence_number ] = future
569
+ self ._result_sequence_number_to_goal_id [sequence_number ] = result_request .goal_id
570
+ future .add_done_callback (self ._remove_pending_result_request )
571
+ # Add future so executor is aware
572
+ self .add_future (future )
561
573
562
574
return future
563
575
0 commit comments