3
3
/// Author: Pavel Kirienko <[email protected] >
4
4
5
5
#include "canard.h"
6
+ #include <stddef.h>
6
7
#include <string.h>
7
8
8
9
// --------------------------------------------- BUILD CONFIGURATION ---------------------------------------------
70
71
71
72
#define INITIAL_TOGGLE_STATE true
72
73
74
+ #define CONTAINER_OF (type , ptr , member ) \
75
+ ((const type*) (((ptr) == NULL) ? NULL : (const void*) (((const char*) (ptr)) - offsetof(type, member))))
76
+ #define MUTABLE_CONTAINER_OF (type , ptr , member ) \
77
+ ((type*) (((ptr) == NULL) ? NULL : (void*) (((char*) (ptr)) - offsetof(type, member))))
78
+
73
79
/// Used for inserting new items into AVL trees.
74
80
CANARD_PRIVATE struct CanardTreeNode * avlTrivialFactory (void * const user_reference )
75
81
{
@@ -299,10 +305,15 @@ CANARD_PRIVATE struct CanardTxQueueItem* txAllocateQueueItem(struct CanardTxQueu
299
305
struct CanardTxQueueItem * out = ins -> memory .allocate (ins -> memory .user_reference , sizeof (struct CanardTxQueueItem ));
300
306
if (out != NULL )
301
307
{
302
- out -> base .up = NULL ;
303
- out -> base .lr [0 ] = NULL ;
304
- out -> base .lr [1 ] = NULL ;
305
- out -> base .bf = 0 ;
308
+ out -> priority_base .up = NULL ;
309
+ out -> priority_base .lr [0 ] = NULL ;
310
+ out -> priority_base .lr [1 ] = NULL ;
311
+ out -> priority_base .bf = 0 ;
312
+
313
+ out -> deadline_base .up = NULL ;
314
+ out -> deadline_base .lr [0 ] = NULL ;
315
+ out -> deadline_base .lr [1 ] = NULL ;
316
+ out -> deadline_base .bf = 0 ;
306
317
307
318
out -> next_in_transfer = NULL ; // Last by default.
308
319
out -> tx_deadline_usec = deadline_usec ;
@@ -329,15 +340,34 @@ CANARD_PRIVATE struct CanardTxQueueItem* txAllocateQueueItem(struct CanardTxQueu
329
340
/// Frames with identical CAN ID that are added later always compare greater than their counterparts with same CAN ID.
330
341
/// This ensures that CAN frames with the same CAN ID are transmitted in the FIFO order.
331
342
/// Frames that should be transmitted earlier compare smaller (i.e., put on the left side of the tree).
332
- CANARD_PRIVATE int8_t txAVLPredicate (void * const user_reference , // NOSONAR Cavl API requires pointer to non-const.
333
- const struct CanardTreeNode * const node )
343
+ CANARD_PRIVATE int8_t txAVLPriorityPredicate ( //
344
+ void * const user_reference , // NOSONAR Cavl API requires pointer to non-const.
345
+ const struct CanardTreeNode * const node )
334
346
{
335
- const struct CanardTxQueueItem * const target = (const struct CanardTxQueueItem * ) user_reference ;
336
- const struct CanardTxQueueItem * const other = (const struct CanardTxQueueItem * ) (const void * ) node ;
347
+ typedef struct CanardTxQueueItem TxItem ;
348
+
349
+ const TxItem * const target = CONTAINER_OF (TxItem , user_reference , priority_base );
350
+ const TxItem * const other = CONTAINER_OF (TxItem , node , priority_base );
337
351
CANARD_ASSERT ((target != NULL ) && (other != NULL ));
338
352
return (target -> frame .extended_can_id >= other -> frame .extended_can_id ) ? +1 : -1 ;
339
353
}
340
354
355
+ /// Frames with identical deadline
356
+ /// that are added later always compare greater than their counterparts with the same deadline.
357
+ /// This ensures that CAN frames with the same deadline are, when timed out, dropped in the FIFO order.
358
+ /// Frames that should be dropped earlier compare smaller (i.e., put on the left side of the tree).
359
+ CANARD_PRIVATE int8_t txAVLDeadlinePredicate ( //
360
+ void * const user_reference , // NOSONAR Cavl API requires pointer to non-const.
361
+ const struct CanardTreeNode * const node )
362
+ {
363
+ typedef struct CanardTxQueueItem TxItem ;
364
+
365
+ const TxItem * const target = CONTAINER_OF (TxItem , user_reference , deadline_base );
366
+ const TxItem * const other = CONTAINER_OF (TxItem , node , deadline_base );
367
+ CANARD_ASSERT ((target != NULL ) && (other != NULL ));
368
+ return (target -> tx_deadline_usec >= other -> tx_deadline_usec ) ? +1 : -1 ;
369
+ }
370
+
341
371
/// Returns the number of frames enqueued or error (i.e., =1 or <0).
342
372
CANARD_PRIVATE int32_t txPushSingleFrame (struct CanardTxQueue * const que ,
343
373
const struct CanardInstance * const ins ,
@@ -369,11 +399,19 @@ CANARD_PRIVATE int32_t txPushSingleFrame(struct CanardTxQueue* const que,
369
399
uint8_t * const frame_bytes = tqi -> frame .payload .data ;
370
400
(void ) memset (frame_bytes + payload .size , PADDING_BYTE_VALUE , padding_size ); // NOLINT
371
401
* (frame_bytes + (frame_payload_size - 1U )) = txMakeTailByte (true, true, true, transfer_id );
372
- // Insert the newly created TX item into the queue.
373
- const struct CanardTreeNode * const res =
374
- cavlSearch (& que -> root , & tqi -> base , & txAVLPredicate , & avlTrivialFactory );
375
- (void ) res ;
376
- CANARD_ASSERT (res == & tqi -> base );
402
+
403
+ // Insert the newly created TX item into the priority queue.
404
+ const struct CanardTreeNode * const priority_queue_res =
405
+ cavlSearch (& que -> priority_root , & tqi -> priority_base , & txAVLPriorityPredicate , & avlTrivialFactory );
406
+ (void ) priority_queue_res ;
407
+ CANARD_ASSERT (priority_queue_res == & tqi -> priority_base );
408
+
409
+ // Insert the newly created TX item into the deadline queue.
410
+ const struct CanardTreeNode * const deadline_queue_res =
411
+ cavlSearch (& que -> deadline_root , & tqi -> deadline_base , & txAVLDeadlinePredicate , & avlTrivialFactory );
412
+ (void ) deadline_queue_res ;
413
+ CANARD_ASSERT (deadline_queue_res == & tqi -> deadline_base );
414
+
377
415
que -> size ++ ;
378
416
CANARD_ASSERT (que -> size <= que -> capacity );
379
417
out = 1 ; // One frame enqueued.
@@ -514,11 +552,18 @@ CANARD_PRIVATE int32_t txPushMultiFrame(struct CanardTxQueue* const que,
514
552
struct CanardTxQueueItem * next = sq .head ;
515
553
do
516
554
{
517
- const struct CanardTreeNode * const res =
518
- cavlSearch (& que -> root , & next -> base , & txAVLPredicate , & avlTrivialFactory );
519
- (void ) res ;
520
- CANARD_ASSERT (res == & next -> base );
521
- CANARD_ASSERT (que -> root != NULL );
555
+ const struct CanardTreeNode * const priority_queue_res =
556
+ cavlSearch (& que -> priority_root , & next -> priority_base , & txAVLPriorityPredicate , & avlTrivialFactory );
557
+ (void ) priority_queue_res ;
558
+ CANARD_ASSERT (priority_queue_res == & next -> priority_base );
559
+ CANARD_ASSERT (que -> priority_root != NULL );
560
+
561
+ const struct CanardTreeNode * const deadline_queue_res =
562
+ cavlSearch (& que -> deadline_root , & next -> deadline_base , & txAVLDeadlinePredicate , & avlTrivialFactory );
563
+ (void ) deadline_queue_res ;
564
+ CANARD_ASSERT (deadline_queue_res == & next -> deadline_base );
565
+ CANARD_ASSERT (que -> deadline_root != NULL );
566
+
522
567
next = next -> next_in_transfer ;
523
568
} while (next != NULL );
524
569
CANARD_ASSERT (num_frames == sq .size );
@@ -547,6 +592,35 @@ CANARD_PRIVATE int32_t txPushMultiFrame(struct CanardTxQueue* const que,
547
592
return out ;
548
593
}
549
594
595
+ /// Flushes expired transfers by comparing deadline timestamps of the pending transfers with the current time.
596
+ CANARD_PRIVATE void txFlushExpiredTransfers (struct CanardTxQueue * const que ,
597
+ const struct CanardInstance * const ins ,
598
+ const CanardMicrosecond now_usec )
599
+ {
600
+ struct CanardTxQueueItem * tx_item = NULL ;
601
+ while (NULL != (tx_item = MUTABLE_CONTAINER_OF ( //
602
+ struct CanardTxQueueItem ,
603
+ cavlFindExtremum (que -> deadline_root , false),
604
+ deadline_base )))
605
+ {
606
+ if (now_usec <= tx_item -> tx_deadline_usec )
607
+ {
608
+ // The queue is sorted by deadline, so we can stop here.
609
+ break ;
610
+ }
611
+
612
+ // All frames of the transfer are released at once b/c they all have the same deadline.
613
+ struct CanardTxQueueItem * tx_item_to_free = NULL ;
614
+ while (NULL != (tx_item_to_free = canardTxPop (que , tx_item )))
615
+ {
616
+ tx_item = tx_item_to_free -> next_in_transfer ;
617
+ canardTxFree (que , ins , tx_item_to_free );
618
+
619
+ que -> stats .dropped_frames ++ ;
620
+ }
621
+ }
622
+ }
623
+
550
624
// --------------------------------------------- RECEPTION ---------------------------------------------
551
625
552
626
#define RX_SESSIONS_PER_SUBSCRIPTION (CANARD_NODE_ID_MAX + 1U)
@@ -1005,8 +1079,9 @@ CANARD_PRIVATE int8_t
1005
1079
rxSubscriptionPredicateOnPortID (void * const user_reference , // NOSONAR Cavl API requires pointer to non-const.
1006
1080
const struct CanardTreeNode * const node )
1007
1081
{
1082
+ CANARD_ASSERT ((user_reference != NULL ) && (node != NULL ));
1008
1083
const CanardPortID sought = * ((const CanardPortID * ) user_reference );
1009
- const CanardPortID other = (( const struct CanardRxSubscription * ) ( const void * ) node )-> port_id ;
1084
+ const CanardPortID other = CONTAINER_OF ( struct CanardRxSubscription , node , base )-> port_id ;
1010
1085
static const int8_t NegPos [2 ] = {-1 , +1 };
1011
1086
// Clang-Tidy mistakenly identifies a narrowing cast to int8_t here, which is incorrect.
1012
1087
return (sought == other ) ? 0 : NegPos [sought > other ]; // NOLINT no narrowing conversion is taking place here
@@ -1016,7 +1091,9 @@ CANARD_PRIVATE int8_t
1016
1091
rxSubscriptionPredicateOnStruct (void * const user_reference , // NOSONAR Cavl API requires pointer to non-const.
1017
1092
const struct CanardTreeNode * const node )
1018
1093
{
1019
- return rxSubscriptionPredicateOnPortID (& ((struct CanardRxSubscription * ) user_reference )-> port_id , node );
1094
+ return rxSubscriptionPredicateOnPortID ( //
1095
+ & MUTABLE_CONTAINER_OF (struct CanardRxSubscription , user_reference , base )-> port_id ,
1096
+ node );
1020
1097
}
1021
1098
1022
1099
// --------------------------------------------- PUBLIC API ---------------------------------------------
@@ -1056,7 +1133,8 @@ struct CanardTxQueue canardTxInit(const size_t capacity,
1056
1133
.capacity = capacity ,
1057
1134
.mtu_bytes = mtu_bytes ,
1058
1135
.size = 0 ,
1059
- .root = NULL ,
1136
+ .priority_root = NULL ,
1137
+ .deadline_root = NULL ,
1060
1138
.memory = memory ,
1061
1139
.user_reference = NULL ,
1062
1140
};
@@ -1067,8 +1145,20 @@ int32_t canardTxPush(struct CanardTxQueue* const que,
1067
1145
const struct CanardInstance * const ins ,
1068
1146
const CanardMicrosecond tx_deadline_usec ,
1069
1147
const struct CanardTransferMetadata * const metadata ,
1070
- const struct CanardPayload payload )
1148
+ const struct CanardPayload payload ,
1149
+ const CanardMicrosecond now_usec )
1071
1150
{
1151
+ // Before pushing payload (potentially in multiple frames), we need to try to flush any expired transfers.
1152
+ // This is necessary to ensure that we don't exhaust the capacity of the queue by holding outdated frames.
1153
+ // The flushing is done by comparing deadline timestamps of the pending transfers with the current time,
1154
+ // which makes sense only if the current time is known (bigger than zero).
1155
+ if (now_usec > 0 )
1156
+ {
1157
+ txFlushExpiredTransfers (que , ins , now_usec );
1158
+ }
1159
+
1160
+ (void ) now_usec ;
1161
+
1072
1162
int32_t out = - CANARD_ERROR_INVALID_ARGUMENT ;
1073
1163
if ((ins != NULL ) && (que != NULL ) && (metadata != NULL ) && ((payload .data != NULL ) || (0U == payload .size )))
1074
1164
{
@@ -1114,7 +1204,8 @@ struct CanardTxQueueItem* canardTxPeek(const struct CanardTxQueue* const que)
1114
1204
{
1115
1205
// Paragraph 6.7.2.1.15 of the C standard says:
1116
1206
// A pointer to a structure object, suitably converted, points to its initial member, and vice versa.
1117
- out = (struct CanardTxQueueItem * ) (void * ) cavlFindExtremum (que -> root , false);
1207
+ struct CanardTreeNode * const priority_node = cavlFindExtremum (que -> priority_root , false);
1208
+ out = MUTABLE_CONTAINER_OF (struct CanardTxQueueItem , priority_node , priority_base );
1118
1209
}
1119
1210
return out ;
1120
1211
}
@@ -1127,7 +1218,8 @@ struct CanardTxQueueItem* canardTxPop(struct CanardTxQueue* const que, struct Ca
1127
1218
// A pointer to a structure object, suitably converted, points to its initial member, and vice versa.
1128
1219
// Note that the highest-priority frame is always a leaf node in the AVL tree, which means that it is very
1129
1220
// cheap to remove.
1130
- cavlRemove (& que -> root , & item -> base );
1221
+ cavlRemove (& que -> priority_root , & item -> priority_base );
1222
+ cavlRemove (& que -> deadline_root , & item -> deadline_base );
1131
1223
que -> size -- ;
1132
1224
}
1133
1225
return item ;
@@ -1169,11 +1261,13 @@ int8_t canardRxAccept(struct CanardInstance* const ins,
1169
1261
// This is the reason the function has a logarithmic time complexity of the number of subscriptions.
1170
1262
// Note also that this one of the two variable-complexity operations in the RX pipeline; the other one
1171
1263
// is memcpy(). Excepting these two cases, the entire RX pipeline contains neither loops nor recursion.
1172
- struct CanardRxSubscription * const sub = (struct CanardRxSubscription * ) (void * )
1264
+ struct CanardRxSubscription * const sub = MUTABLE_CONTAINER_OF ( //
1265
+ struct CanardRxSubscription ,
1173
1266
cavlSearch (& ins -> rx_subscriptions [(size_t ) model .transfer_kind ],
1174
1267
& model .port_id ,
1175
1268
& rxSubscriptionPredicateOnPortID ,
1176
- NULL );
1269
+ NULL ),
1270
+ base );
1177
1271
if (out_subscription != NULL )
1178
1272
{
1179
1273
* out_subscription = sub ; // Expose selected instance to the caller.
@@ -1230,7 +1324,7 @@ int8_t canardRxSubscribe(struct CanardInstance* const ins,
1230
1324
out_subscription -> sessions [i ] = NULL ;
1231
1325
}
1232
1326
const struct CanardTreeNode * const res = cavlSearch (& ins -> rx_subscriptions [tk ],
1233
- out_subscription ,
1327
+ & out_subscription -> base ,
1234
1328
& rxSubscriptionPredicateOnStruct ,
1235
1329
& avlTrivialFactory );
1236
1330
(void ) res ;
@@ -1249,9 +1343,12 @@ int8_t canardRxUnsubscribe(struct CanardInstance* const ins,
1249
1343
const size_t tk = (size_t ) transfer_kind ;
1250
1344
if ((ins != NULL ) && (tk < CANARD_NUM_TRANSFER_KINDS ))
1251
1345
{
1252
- CanardPortID port_id_mutable = port_id ;
1253
- struct CanardRxSubscription * const sub = (struct CanardRxSubscription * ) (void * )
1254
- cavlSearch (& ins -> rx_subscriptions [tk ], & port_id_mutable , & rxSubscriptionPredicateOnPortID , NULL );
1346
+ CanardPortID port_id_mutable = port_id ;
1347
+
1348
+ struct CanardRxSubscription * const sub = MUTABLE_CONTAINER_OF ( //
1349
+ struct CanardRxSubscription ,
1350
+ cavlSearch (& ins -> rx_subscriptions [tk ], & port_id_mutable , & rxSubscriptionPredicateOnPortID , NULL ),
1351
+ base );
1255
1352
if (sub != NULL )
1256
1353
{
1257
1354
cavlRemove (& ins -> rx_subscriptions [tk ], & sub -> base );
@@ -1287,9 +1384,12 @@ int8_t canardRxGetSubscription(struct CanardInstance* const ins,
1287
1384
const size_t tk = (size_t ) transfer_kind ;
1288
1385
if ((ins != NULL ) && (tk < CANARD_NUM_TRANSFER_KINDS ))
1289
1386
{
1290
- CanardPortID port_id_mutable = port_id ;
1291
- struct CanardRxSubscription * const sub = (struct CanardRxSubscription * ) (void * )
1292
- cavlSearch (& ins -> rx_subscriptions [tk ], & port_id_mutable , & rxSubscriptionPredicateOnPortID , NULL );
1387
+ CanardPortID port_id_mutable = port_id ;
1388
+
1389
+ struct CanardRxSubscription * const sub = MUTABLE_CONTAINER_OF ( //
1390
+ struct CanardRxSubscription ,
1391
+ cavlSearch (& ins -> rx_subscriptions [tk ], & port_id_mutable , & rxSubscriptionPredicateOnPortID , NULL ),
1392
+ base );
1293
1393
if (sub != NULL )
1294
1394
{
1295
1395
CANARD_ASSERT (sub -> port_id == port_id );
0 commit comments