Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce deadline queue #sonar #238

Merged
merged 7 commits into from
Dec 5, 2024
Merged

introduce deadline queue #sonar #238

merged 7 commits into from
Dec 5, 2024

Conversation

serges147
Copy link
Contributor

No description provided.

@serges147 serges147 self-assigned this Dec 3, 2024
@@ -326,18 +332,39 @@ CANARD_PRIVATE struct CanardTxQueueItem* txAllocateQueueItem(struct CanardTxQueu
return out;
}

#define CONTAINER_OF(type, ptr, member) ((type*) (((ptr) == NULL) ? NULL : ((char*) (ptr) - offsetof(type, member))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be near the top. Also, there is risk of this expression accidentally casting away a const qualifier on the pointer. Maybe we should have const and mutable editions of this macro, where the const one would use const type* and const char*?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should have const and mutable editions of this macro, where the const one would use const type* and const char*?

Good point - I will move it to the top, and make two versions: CONTAINER_OF & CONST_CONTAINER_OF

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually we assume immutability by default, like with the payload containers, for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean to have CONTAINER_OF (const version) and MUTABLE_CONTAINER_OF (mutable version) ?

/// Holds the statistics of a transmission queue.
struct CanardTxQueueStats
{
/// Holds number of dropped TX frames (due to timeout when `now > deadline`).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not just timeout. If the media fails to accept a frame, we discard it, right? Along with every other frame in the transfer chain.

Copy link
Contributor Author

@serges147 serges147 Dec 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such business logic was out of lizard scope. Yes, at libcyphal I did it as you described. At flux_grip and telega I'm not 100% sure, but it seems that there is no notion of error there - just boolean either accepted or not accepted ifc->push. If it was not accepted then it stays in the queue - for retry purposes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but if we are designing a template method ("template" in the sense of design patterns, not type-generic programming), then it makes sense to support fallible media, right?

@serges147 serges147 marked this pull request as ready for review December 4, 2024 14:14
Copy link

sonarcloud bot commented Dec 4, 2024

Quality Gate Failed Quality Gate failed

Failed conditions
90.7% Coverage on New Code (required ≥ 99%)

See analysis details on SonarQube Cloud

Copy link
Member

@pavel-kirienko pavel-kirienko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The migration guide requires updating.

@@ -502,7 +519,9 @@ struct CanardTxQueue canardTxInit(const size_t capacity,
/// frames (so all frames will have the same timestamp value). This feature is intended to facilitate transmission
/// deadline tracking, i.e., aborting frames that could not be transmitted before the specified deadline.
/// Therefore, normally, the timestamp value should be in the future.
/// The library itself, however, does not use or check this value in any way, so it can be zero if not needed.
/// The library uses `now > deadline` comparison to determine which frames timed out, and so could
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we could use a clarification that we're talking about frames that were enqueued earlier, not the ones that are added by this call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

already fixed in the next pr branch (sshirokov/v4_tx_poll)

@@ -306,6 +306,13 @@ struct CanardMemoryResource
CanardMemoryAllocate allocate; ///< Shall be a valid pointer.
};

/// Holds the statistics of a transmission queue.
struct CanardTxQueueStats
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we name it just CanardStats, with the possibility of future extension with other metrics? Then dropped_frames becomes tx_dropped_frames.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, if you have redundant media, you can see such stats per TX queue. If we move CanardTxQueueStats out of CanardTxQueue (to the CanaradInstance I assume as more generic CanardStats is proposed) then we will loose such distinction.
Also, under Udpard there is no such thing as "UdpardInstance" as you know of course - my point is that under Udpard we have to keep it per TX queue (I assume that we want similar improvements at Udpard as well, namely: two trees, flush on push, udpardTxPoll).

So, I propose to keep it as I did. The only thing what maybe could be valueable is to split dropped_frames to two separate counters:

  • dropped_expired_frames - incremented by the canardTxPush and canardTxPoll for each expired frame.
  • dropped_failed_frames - incremented by the new canardTxPoll when frame handler returns negative result.
    Currently both cases increment the very same dropped_frames.

@@ -1114,7 +1204,8 @@ struct CanardTxQueueItem* canardTxPeek(const struct CanardTxQueue* const que)
{
// Paragraph 6.7.2.1.15 of the C standard says:
// A pointer to a structure object, suitably converted, points to its initial member, and vice versa.
out = (struct CanardTxQueueItem*) (void*) cavlFindExtremum(que->root, false);
struct CanardTreeNode* const priority_node = cavlFindExtremum(que->priority_root, false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment above (citing the standard) is no longer applicable so it should be removed.

const CanardMicrosecond now_usec)
{
struct CanardTxQueueItem* tx_item = NULL;
while (NULL != (tx_item = MUTABLE_CONTAINER_OF( //
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assignment in an expression violates some MISRA C rule whose number I forgot (ChatGPT can tell you which one); please split this into the longer form.

break;
}

// All frames of the transfer are released at once b/c they all have the same deadline.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this special case here? All frames in a transfer have the same timeout (IIRC it is mentioned in the docs, too), so either all or none will be kept even if you don't include this special case. On the other hand, one might argue that the current solution is more robust because it doesn't make assumptions about timeout equality. So maybe we do need this special case, after all.

There is also a related question: do we really need to locate the extremum at every iteration? This looks like an avoidable Shliemel the painter problem. What we have right now is good enough but perhaps an explanatory comment is warranted, saying that it should be possible to avoid extremum call at every iteration by simply fetching the neighboring node from the current one before deleting it. Obviously this will only work if we rely on the assumption that the timeout is the same for all frames in the transfer (otherwise the loop below will invalidate the tree linkage).

Copy link
Contributor Author

@serges147 serges147 Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that such flushing is more like for robustness and "self healing" for a special corner case, namely when media stopped to work for some time (f.e. stopped to answer/callback at all), whole TX capacity exhausted, then media recovered but it won't be engaged/triggered anymore b/c we cannot push anything new due to full capacity. Flushing by timeout will resolve such deadlock.

really need to locate the extremum

I think it is okey, especially if you think about nominal use case in which there will be only one call to cavlFindExtremum, it will return NULL (b/c normally everything properly transmitted before deadline), and that is it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the current solution is acceptable, I must point out that in real-time programming, one should focus not only on the general case but also on the worst case.

@serges147
Copy link
Contributor Author

The migration guide requires updating.

Yes, it will be done in the next pr when new canardTxPoll appears.

@serges147 serges147 merged commit 5220b05 into v4 Dec 5, 2024
16 of 17 checks passed
@serges147 serges147 deleted the sshirokov/v4_deadline branch December 5, 2024 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants