@@ -182,6 +182,8 @@ def __init__(
182182 self ._node .add_waitable (self )
183183 self ._logger = self ._node .get_logger ().get_child ('action_client' )
184184
185+ self ._lock = threading .Lock ()
186+
185187 def _generate_random_uuid (self ):
186188 return UUID (uuid = list (uuid .uuid4 ().bytes ))
187189
@@ -241,39 +243,44 @@ def take_data(self):
241243 """Take stuff from lower level so the wait set doesn't immediately wake again."""
242244 data = {}
243245 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
246+ with self ._lock :
247+ taken_data = self ._client_handle .take_goal_response (
248+ self ._action_type .Impl .SendGoalService .Response )
249+ # If take fails, then we get (None, None)
250+ if all (taken_data ):
251+ data ['goal' ] = taken_data
249252
250253 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
254+ with self ._lock :
255+ taken_data = self ._client_handle .take_cancel_response (
256+ self ._action_type .Impl .CancelGoalService .Response )
257+ # If take fails, then we get (None, None)
258+ if all (taken_data ):
259+ data ['cancel' ] = taken_data
256260
257261 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
262+ with self ._lock :
263+ taken_data = self ._client_handle .take_result_response (
264+ self ._action_type .Impl .GetResultService .Response )
265+ # If take fails, then we get (None, None)
266+ if all (taken_data ):
267+ data ['result' ] = taken_data
263268
264269 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
270+ with self ._lock :
271+ taken_data = self ._client_handle .take_feedback (
272+ self ._action_type .Impl .FeedbackMessage )
273+ # If take fails, then we get None
274+ if taken_data is not None :
275+ data ['feedback' ] = taken_data
270276
271277 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
278+ with self ._lock :
279+ taken_data = self ._client_handle .take_status (
280+ self ._action_type .Impl .GoalStatusMessage )
281+ # If take fails, then we get None
282+ if taken_data is not None :
283+ data ['status' ] = taken_data
277284
278285 return data
279286
@@ -354,12 +361,14 @@ async def execute(self, taken_data):
354361
355362 def get_num_entities (self ):
356363 """Return number of each type of entity used in the wait set."""
357- num_entities = self ._client_handle .get_num_entities ()
364+ with self ._lock :
365+ num_entities = self ._client_handle .get_num_entities ()
358366 return NumberOfEntities (* num_entities )
359367
360368 def add_to_wait_set (self , wait_set ):
361369 """Add entities to wait set."""
362- self ._client_handle .add_to_waitset (wait_set )
370+ with self ._lock :
371+ self ._client_handle .add_to_waitset (wait_set )
363372
364373 def __enter__ (self ):
365374 return self ._client_handle .__enter__ ()
@@ -437,10 +446,17 @@ def send_goal_async(self, goal, feedback_callback=None, goal_uuid=None):
437446 request = self ._action_type .Impl .SendGoalService .Request ()
438447 request .goal_id = self ._generate_random_uuid () if goal_uuid is None else goal_uuid
439448 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 ))
449+ future = Future ()
450+ with self ._lock :
451+ sequence_number = self ._client_handle .send_goal_request (request )
452+ if sequence_number in self ._pending_goal_requests :
453+ raise RuntimeError (
454+ 'Sequence ({}) conflicts with pending goal request' .format (sequence_number ))
455+ self ._pending_goal_requests [sequence_number ] = future
456+ self ._goal_sequence_number_to_goal_id [sequence_number ] = request .goal_id
457+ future .add_done_callback (self ._remove_pending_goal_request )
458+ # Add future so executor is aware
459+ self .add_future (future )
444460
445461 if feedback_callback is not None :
446462 # TODO(jacobperron): Move conversion function to a general-use package
@@ -495,16 +511,17 @@ def _cancel_goal_async(self, goal_handle):
495511
496512 cancel_request = CancelGoal .Request ()
497513 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 ))
514+ future : Future [CancelGoal .Response ] = Future ()
515+ with self ._lock :
516+ sequence_number = self ._client_handle .send_cancel_request (cancel_request )
517+ if sequence_number in self ._pending_cancel_requests :
518+ raise RuntimeError (
519+ 'Sequence ({}) conflicts with pending cancel request' .format (sequence_number ))
502520
503- 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 )
521+ self ._pending_cancel_requests [sequence_number ] = future
522+ future .add_done_callback (self ._remove_pending_cancel_request )
523+ # Add future so executor is aware
524+ self .add_future (future )
508525
509526 return future
510527
@@ -547,17 +564,18 @@ def _get_result_async(self, goal_handle):
547564
548565 result_request = self ._action_type .Impl .GetResultService .Request ()
549566 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- 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 )
567+ future : Future [GetResultServiceResponse [ResultT ]] = Future ()
568+ with self ._lock :
569+ sequence_number = self ._client_handle .send_result_request (result_request )
570+ if sequence_number in self ._pending_result_requests :
571+ raise RuntimeError (
572+ 'Sequence ({}) conflicts with pending result request' .format (sequence_number ))
573+
574+ self ._pending_result_requests [sequence_number ] = future
575+ self ._result_sequence_number_to_goal_id [sequence_number ] = result_request .goal_id
576+ future .add_done_callback (self ._remove_pending_result_request )
577+ # Add future so executor is aware
578+ self .add_future (future )
561579
562580 return future
563581
0 commit comments