1
1
use std:: {
2
- boxed:: Box ,
3
2
ffi:: CString ,
4
3
sync:: { Arc , Mutex , MutexGuard } ,
4
+ collections:: HashMap ,
5
5
} ;
6
6
7
- use futures:: channel:: mpsc:: { UnboundedSender , unbounded} ;
8
-
9
7
use rosidl_runtime_rs:: Message ;
10
8
11
9
use crate :: {
12
10
error:: ToResult ,
13
11
rcl_bindings:: * ,
14
- MessageCow , Node , RclrsError , Promise , ENTITY_LIFECYCLE_MUTEX ,
15
- ExecutorCommands , Executable , QoSProfile , Waitable , WaitableLifecycle ,
12
+ MessageCow , Node , RclrsError , RclReturnCode , Promise , ENTITY_LIFECYCLE_MUTEX ,
13
+ Executable , QoSProfile , Waitable , WaitableLifecycle ,
16
14
ExecutableHandle , ExecutableKind , ServiceInfo ,
17
15
} ;
18
16
@@ -25,9 +23,6 @@ pub use client_callback::*;
25
23
mod client_output;
26
24
pub use client_output:: * ;
27
25
28
- mod client_task;
29
- use client_task:: * ;
30
-
31
26
/// Main class responsible for sending requests to a ROS service.
32
27
///
33
28
/// The only available way to instantiate clients is via [`Node::create_client`][1], this is to
40
35
T : rosidl_runtime_rs:: Service ,
41
36
{
42
37
handle : Arc < ClientHandle > ,
43
- action : UnboundedSender < ClientAction < T > > ,
38
+ board : Arc < Mutex < ClientRequestBoard < T > > > ,
44
39
#[ allow( unused) ]
45
40
lifecycle : WaitableLifecycle ,
46
41
}
86
81
}
87
82
. ok ( ) ?;
88
83
84
+ println ! ( "vvvvvvvvv Sent client request {sequence_number} vvvvvvvvvvvv" ) ;
89
85
// TODO(@mxgrey): Log errors here when logging becomes available.
90
- self . action . unbounded_send (
91
- ClientAction :: NewRequest { sequence_number, sender }
92
- ) . ok ( ) ;
86
+ self . board . lock ( ) . unwrap ( ) . new_request ( sequence_number, sender) ;
93
87
94
88
Ok ( promise)
95
89
}
@@ -231,39 +225,39 @@ where
231
225
} ) ;
232
226
233
227
let commands = node. commands ( ) ;
234
-
235
- let ( action, receiver) = unbounded ( ) ;
236
- let _ = commands. run ( client_task ( receiver, Arc :: clone ( & handle) ) ) ;
228
+ let board = Arc :: new ( Mutex :: new ( ClientRequestBoard :: new ( ) ) ) ;
237
229
238
230
let ( waitable, lifecycle) = Waitable :: new (
239
231
Box :: new ( ClientExecutable {
240
232
handle : Arc :: clone ( & handle) ,
241
- action : action . clone ( ) ,
233
+ board : Arc :: clone ( & board ) ,
242
234
} ) ,
243
235
Some ( Arc :: clone ( & commands. get_guard_condition ( ) ) ) ,
244
236
) ;
245
237
commands. add_to_wait_set ( waitable) ;
246
238
247
239
Ok ( Arc :: new ( Self {
248
240
handle,
249
- action ,
241
+ board ,
250
242
lifecycle,
251
243
} ) )
252
244
}
253
245
}
254
246
255
- struct ClientExecutable < T : rosidl_runtime_rs:: Service > {
247
+ struct ClientExecutable < T >
248
+ where
249
+ T : rosidl_runtime_rs:: Service ,
250
+ {
256
251
handle : Arc < ClientHandle > ,
257
- action : UnboundedSender < ClientAction < T > >
252
+ board : Arc < Mutex < ClientRequestBoard < T > > >
258
253
}
259
254
260
255
impl < T > Executable for ClientExecutable < T >
261
256
where
262
257
T : rosidl_runtime_rs:: Service ,
263
258
{
264
259
fn execute ( & mut self ) -> Result < ( ) , RclrsError > {
265
- self . action . unbounded_send ( ClientAction :: TakeResponse ) . ok ( ) ;
266
- Ok ( ( ) )
260
+ self . board . lock ( ) . unwrap ( ) . execute ( & self . handle )
267
261
}
268
262
269
263
fn handle ( & self ) -> ExecutableHandle {
@@ -275,6 +269,107 @@ where
275
269
}
276
270
}
277
271
272
+ type SequenceNumber = i64 ;
273
+
274
+ /// This is used internally to monitor the state of active requests, as well as
275
+ /// responses that have arrived without a known request.
276
+ struct ClientRequestBoard < T >
277
+ where
278
+ T : rosidl_runtime_rs:: Service ,
279
+ {
280
+ // This stores all active requests that have not received a response yet
281
+ active_requests : HashMap < SequenceNumber , AnyClientOutputSender < T :: Response > > ,
282
+ // This holds responses that came in when no active request matched the
283
+ // sequence number. This could happen if take_response is triggered before
284
+ // the new_request for the same sequence number. That is extremely unlikely
285
+ // to ever happen but is theoretically possible on systems that may exhibit
286
+ // very strange CPU scheduling patterns, so we should account for it.
287
+ loose_responses : HashMap < SequenceNumber , ( T :: Response , rmw_service_info_t ) > ,
288
+ }
289
+
290
+ impl < T > ClientRequestBoard < T >
291
+ where
292
+ T : rosidl_runtime_rs:: Service ,
293
+ {
294
+ fn new ( ) -> Self {
295
+ Self {
296
+ active_requests : Default :: default ( ) ,
297
+ loose_responses : Default :: default ( ) ,
298
+ }
299
+ }
300
+
301
+ fn new_request (
302
+ & mut self ,
303
+ sequence_number : SequenceNumber ,
304
+ sender : AnyClientOutputSender < T :: Response > ,
305
+ ) {
306
+ if let Some ( ( response, info) ) = self . loose_responses . remove ( & sequence_number) {
307
+ // Weirdly the response for this request already arrived, so we'll
308
+ // send it off immediately.
309
+ sender. send_response ( response, info) ;
310
+ } else {
311
+ self . active_requests . insert ( sequence_number, sender) ;
312
+ }
313
+ }
314
+
315
+ fn execute ( & mut self , handle : & Arc < ClientHandle > ) -> Result < ( ) , RclrsError > {
316
+ match self . take_response ( handle) {
317
+ Ok ( ( response, info) ) => {
318
+ let seq = info. request_id . sequence_number ;
319
+ if let Some ( sender) = self . active_requests . remove ( & seq) {
320
+ dbg ! ( ) ;
321
+ println ! ( "Received response for {info:?}" ) ;
322
+ // The active request is available, so send this response off
323
+ sender. send_response ( response, info) ;
324
+ } else {
325
+ dbg ! ( ) ;
326
+ println ! ( "Received loose response for {info:?}" ) ;
327
+ // Weirdly there isn't an active request for this, so save
328
+ // it in the loose responses map.
329
+ self . loose_responses . insert ( seq, ( response, info) ) ;
330
+ }
331
+ }
332
+ Err ( err) => {
333
+ match err {
334
+ RclrsError :: RclError { code : RclReturnCode :: ClientTakeFailed , .. } => {
335
+ // This is okay, it means a spurious wakeup happened
336
+ dbg ! ( ) ;
337
+ println ! ( "Spurious wakeup for client" ) ;
338
+ }
339
+ err => {
340
+ dbg ! ( ) ;
341
+ // TODO(@mxgrey): Log the error here once logging is available
342
+ eprintln ! ( "Error while taking a response for a client: {err}" ) ;
343
+ }
344
+ }
345
+ }
346
+ }
347
+ Ok ( ( ) )
348
+ }
349
+
350
+ fn take_response (
351
+ & self ,
352
+ handle : & Arc < ClientHandle > ,
353
+ ) -> Result < ( T :: Response , rmw_service_info_t ) , RclrsError > {
354
+ let mut service_info_out = ServiceInfo :: zero_initialized_rmw ( ) ;
355
+ let mut response_out = <T :: Response as Message >:: RmwMsg :: default ( ) ;
356
+ let handle = & * handle. lock ( ) ;
357
+ unsafe {
358
+ // SAFETY: The three pointers are all kept valid by the handle
359
+ rcl_take_response_with_info (
360
+ handle,
361
+ & mut service_info_out,
362
+ & mut response_out as * mut <T :: Response as Message >:: RmwMsg as * mut _ ,
363
+ )
364
+ }
365
+ . ok ( )
366
+ . map ( |_| (
367
+ T :: Response :: from_rmw_message ( response_out) ,
368
+ service_info_out,
369
+ ) )
370
+ }
371
+ }
372
+
278
373
/// Manage the lifecycle of an `rcl_client_t`, including managing its dependencies
279
374
/// on `rcl_node_t` and `rcl_context_t` by ensuring that these dependencies are
280
375
/// [dropped after][1] the `rcl_client_t`.
0 commit comments