-
Notifications
You must be signed in to change notification settings - Fork 2k
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
sys/posix/socket: implement MSG_PEEK for recvfrom #16850
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accidentally only commented.
I've had a look at this for implementing CoAP-over-TCP (with some thoughts at rust-embedded-community/embedded-nal#59). Why is there a need for an extra buffer? AIU each TCP socket from GNRC already has a buffer, so peeking could just read that buffer without advancing the read cursor. Other stacks may or may not support this, but then the read call fails. |
See #16739 (comment) ff |
I don't understand how the 16739 comment relates. Right now we have
This commit adds on top of this a
Now what comes out of 16739 might smush 1 and 2 into the same thing, but either way, a read that does not advance the read cursor (inside 1 or the combined 1+2 pktbuf chain) would achieve the same without an additional buffer (and, above that, do backpressure better). |
Ahh yes. Sorry, I forgot that yab (yet another buffer) was introduced here and I thought you were referring to the reassembled buffers. |
So using a read cursor sounds like a good idea if we can remove the additional buffer with it. If everything is based on |
The discussions on size and size source for the PEEK_BUF would be moot if this would be done at socket level. I encourage you to look at sock_tcp_read and gnrc_tcp_recv, the FSM_EVENT_CALL_RECV event, and how they could be extended: Running a parallel chain (sock_tcp_peek, gnrc_tcp_recv2?) or running with flags that'll eventually call ringbuffer_peek rather than ringbuffer_get would allow playing all this in the existing buffers. (That is, if @miri64 approves of this direction; she has the better overview of the directions these layers should take.) |
If you use |
I think, since this is originally from #15969, that @HendrikVE is using lwIP, so just looking at GNRC won't help much. |
Then what good does it bring against my problem with posix sockets and lwip implementing a read cursor in gnrc? |
Exactly^^ |
But it might be a good idea to look how in lwIP they implemented |
I was unaware that lwIP was your target platform -- but the large-scale approach would be the same: sock_tcp_read would need a sock_tcp_peak counterpart, and then initially that could be implemented for lwIP (as miri suggested, by looking at how they do MSG_PEEK for their own POSIX layer), and would be initially unavailable for GNRC backed sockets. |
See https://git.savannah.nongnu.org/cgit/lwip.git/tree/src/api/sockets.c?h=STABLE-2_1_x#n932 ff |
d91ceba
to
d10f512
Compare
I finally found the time to come back to this. I did a major rework of this PR. As suggested I integrated the peek behaviour directly into lwIP which yields a few advantages:
The disadvantages are: The advantages clearly outweigh the disadvantages. For tcp I moved |
d10f512
to
b33b221
Compare
sys/include/net/sock.h
Outdated
/** | ||
* @brief Just peek data and keep it for later use | ||
* | ||
* @note Currently only supported for lwIP! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @note Currently only supported for lwIP! | |
* @note Select module `sock_aux_peek` and a compatible network stack | |
* to use this. Currently only supported for lwIP. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also renamed the module to sock_aux_peek
since the aux part was missing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that the aux part is needed: that part just means that there is an extra _aux
in the argument. So a _peek
should not have an _aux
in it as it takes no _aux
argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other aux options sock_aux_local
, sock_aux_rssi
and sock_aux_time.stamp
also have this prefix. Sure, unlike peek, they define what else to request, not how to request. But I think for consistency of the enum it is better to use this prefix here as well, even if SOCK_AUX_PEEK
was smuggled in there just to make use of the aux-function infrastructure.
Pondering how this would best be implemented on GNRC I was briefly wondering whether this would need special considerations for interactions with async -- given that (in the level triggered "something is available" mode of POSIX's select), a failed peek might lead to a deadlock where no new event happens that would wake up the thread that is hoping for a sufficiently long peek. This is not an issue here: async TCP sockets generate a callback on every recv event, so it can be handled just fine. (If that callback sets something level-ish, that's fine as long as the peek clears that level). So I think on the async side it's all good. |
9954e85
to
6b6a5d9
Compare
fe1fcde
to
e85704c
Compare
45d8d4e
to
01de0e1
Compare
pkg/lwip/include/sock_types.h
Outdated
mutex_t mutex; /**< Mutex to protect the sock */ | ||
struct netbuf *last_buf; /**< Last received data */ | ||
bool peek_buf_avail; /**< Whether there is a buffer left from a previous peek operation */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should those be inside an
#ifdef MODULE_SOCK_AUX_PEEK
…
#endif
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would unfortunately entail ifdef
s all over, since IS_USED(MODULE_SOCK_AUX_PEEK)
is not enough for the application to compile (tons of has no member named
). This would clutter the code too much. In my opinion the trade-off that these variables are always included vs cluttering the code with ifdef
s is ok, but I can understand that people here might disagree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it will add 16 bytes to each socket, even when not used.
Btw: Why is the mutex needed now? Can sockets now be shared between multiple threads?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it will add 16 bytes to each socket, even when not used.
I added a fixup commit showing how it would look like when removing the members from the struct instead. If you prefer this I'll merge this into the branch.
Btw: Why is the mutex needed now? Can sockets now be shared between multiple threads?
Now that you mention it... Seems like they are not intended to be thread-safe at all. I removed the mutex entirely.
@@ -127,6 +130,9 @@ struct sock_tcp_queue { | |||
*/ | |||
struct sock_udp { | |||
lwip_sock_base_t base; /**< parent class */ | |||
mutex_t mutex; /**< Mutex to protect the sock */ | |||
struct netbuf *last_buf; /**< Last received data */ | |||
bool peek_buf_avail; /**< Whether there is a buffer left from a previous peek operation */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw is there a connection between last_buf
and peek_buf_avail
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We keep last_buf
always in the sock as is until it is received without the peek flag. peek_buf_avail
is basically used to keep track of whether we already read from last_buf
or not which is important for sock_ip_recv_aux
which is looping over sock_ip_recv_buf_aux
. The first call of sock_ip_recv_buf_aux
will return the peeked data and a result > 0
. When calling sock_ip_recv_buf_aux
a second time (since the result was > 0
) we reset the flag and return 0
to break the loop. Returning 0
instantly would be another option, but that does not seem right.
01de0e1
to
098df85
Compare
98e00b1
to
9dd2aa0
Compare
* This assertion will trigger should the behavior change in the future. | ||
*/ | ||
if (old_ctx) { | ||
assert(res == 0 && ctx == NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
args so @miri64 pointed out that it's actually LWIP that makes use of that and that comment is wrong 😬
Contribution description
These changes make it possible to peek bytes from the socket instead of reading them only once. When in peek mode all read bytes are stored in a ringbuffer for later use.
Testing procedure
You can test this PR e.g. in conjunction with everything else in #15969.
Issues/PRs references
This PR is split out from the chunky #15969.