Refactor Drv::Ip::UdpSocket to clean up state members and remove dependency on dynamic memory#3728
Refactor Drv::Ip::UdpSocket to clean up state members and remove dependency on dynamic memory#3728LeStarch merged 16 commits intonasa:develfrom
Conversation
…argument validation. Adding some better logging.
LeStarch
left a comment
There was a problem hiding this comment.
There is a lot more here than I expected. Also, it seems that many asserts have been changed to if-checks. The general rule is:
- If it is clearly a coding error, assert. (e.g. a char* was passed in as nullptr, or
bindwas called beforeopen) - Use an
ifto check for things that could be a error with the run.
Drv/Ip/UdpSocket.hpp
Outdated
| #ifdef TGT_OS_TYPE_VXWORKS | ||
| #include <socket.h> | ||
| #include <inetLib.h> | ||
| #elif defined TGT_OS_TYPE_LINUX || TGT_OS_TYPE_DARWIN |
There was a problem hiding this comment.
Perhaps we should just have the else clause and VXWORKS. Most patforms will put the headers as UNIX does.
There was a problem hiding this comment.
Can we move the #include for linux/darwin into else?
LeStarch
left a comment
There was a problem hiding this comment.
Few more cleanup items.
Drv/Ip/UdpSocket.hpp
Outdated
| #ifdef TGT_OS_TYPE_VXWORKS | ||
| #include <socket.h> | ||
| #include <inetLib.h> | ||
| #elif defined TGT_OS_TYPE_LINUX || TGT_OS_TYPE_DARWIN |
There was a problem hiding this comment.
Can we move the #include for linux/darwin into else?
…ementation of send and recv to properly handle 0-length datagrams
|
@LeStarch The latest check-ins include a fix for a latent bug in the UdpSocket implementation for the handling of 0-length datagrams that you pointed out. Due to UDP supporting 0-length data and TCP not technically supporting 0-length data it required a little bit more refactoring to properly detangle the send/recv implementation in IpSocket and allow UdpSocket to override these commands. |
Drv/Ip/test/ut/TestUdp.cpp
Outdated
| ASSERT_EQ(receiver.configureRecv("127.0.0.1", port), Drv::SOCK_SUCCESS); | ||
| ASSERT_EQ(receiver.open(recv_fd), Drv::SOCK_SUCCESS); | ||
|
|
||
| // Set a custom, longer receive timeout for this specific test to 500ms |
Drv/Ip/test/ut/SocketTestHelper.cpp
Outdated
|
|
||
| if (status == Drv::SOCK_SUCCESS) { | ||
| received_size += bytes_actually_received; | ||
| } else if (status == Drv::SOCK_NO_DATA_AVAILABLE) { |
There was a problem hiding this comment.
The point of this method is to wait (even if no data is available) until such a time as the whole message received (in that the message might span multiple timeouts).
Thus I am confused why this is being commuted to a failure.
Drv/Ip/IpSocket.cpp
Outdated
| return SOCK_NO_DATA_AVAILABLE; | ||
| } else if (errno == EINTR) { | ||
| // Interrupted system call. Retry if not the last iteration. | ||
| if (i == (SOCKET_MAX_ITERATIONS - 1)) { |
There was a problem hiding this comment.
Why doe we need both this and the check at the end? Can't we just continue again?
| } | ||
|
|
||
| SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) { | ||
| // Note: socketDescriptor.fd can be -1 in some test cases |
There was a problem hiding this comment.
Why? How could this ever be -1?
| return received; | ||
| } | ||
|
|
||
| SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) { |
There was a problem hiding this comment.
Why do we need a special send? Can we not just update the send method on the IpSocket?
It seems odd that:
- We fixed asserts in the send in IpSocket to allow size==0, but also override the method
recvis updated to respect 0 length, in Ip, but send is done in UDP.
| // Nothing to be received | ||
| if ((size == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) { | ||
| // Loop primarily for EINTR. Other conditions should lead to an earlier exit. | ||
| for (U32 i = 0; i < SOCKET_MAX_ITERATIONS; i++) { |
Check warning
Code scanning / CodeQL
Comparison result is always the same Warning
| SocketIpStatus IpSocket::handleZeroReturn() { | ||
| // For TCP (which IpSocket primarily serves as a base for, or when not overridden), | ||
| // a return of 0 from ::recv means the peer has performed an orderly shutdown. | ||
| return SOCK_DISCONNECTED; |
Check warning
Code scanning / CodeQL
Unchecked function argument Warning
| } | ||
|
|
||
| // For non-zero-length data, delegate to the base class implementation | ||
| return IpSocket::send(socketDescriptor, data, size); |
Check warning
Code scanning / CodeQL
Unchecked function argument Warning
| } | ||
|
|
||
| // For non-zero-length data, delegate to the base class implementation | ||
| return IpSocket::send(socketDescriptor, data, size); |
Check warning
Code scanning / CodeQL
Unchecked function argument Warning
LeStarch
left a comment
There was a problem hiding this comment.
I need to understand this comment
//TODO: Uncomment FW_ASSERT for socketDescriptor.fd once we fix TcpClientTester to not pass in uninitialized socketDescriptor
Change Description
This change cleans up Drv::Ip::UdpSocket to remove the members
m_state,m_recv_port, andm_recv_hostnamein favour of two new membersm_addr_sendandm_addr_recv. As part of this change we are also removing the dependency on dynamic memory for storing state.This change also improved error handling, edge case handling, and memory safety.
Rationale
Simplifies code and fixes bug of having duplicate data structures storing the same state.
Testing/Review Recommendations
Existing UTs cover the changes made except for handling 0-length datagrams. A new UT was added specifically to test the behaviour of 0-length datagrams.
Future Work
None