-
Notifications
You must be signed in to change notification settings - Fork 564
MinGW: port POSIX socket compatibility layer #2046
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
base: master
Are you sure you want to change the base?
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
We should group A more difficult question is whether introduction of xfoo() warrants conversion from legacy POSIX foo() to modern C++ API: If every foo() caller is a C++ caller and that caller has to be updated anyway, then it could be the right time to start doing proper memory management and error handling (instead of writing or preserving awkward and duplicated code). That requires a lot more/serious work though. It may also require to get rid of the remaining legacy C callers. |
okay, pending @rousskov review issues and final call on whether to use compat/xsocket.*
filenames.
Windows' accept(2) has the same signature as POSIX', but returns a SOCKET (under the hood, a int *). This confuses error handling code in squid, which uses POSIX return values to detect error values. Implement xaccept() to wrap around it, taking the implementation from mswindows.h and using it on MinGW as well. Error highlighting the issue: ``` TcpAcceptor.cc: In member function 'bool Comm::TcpAcceptor::acceptInto(Comm::ConnectionPointer&)': TcpAcceptor.cc:352:17: error: comparison of unsigned expression in '< 0' is always false [-Werror=type-limits] 352 | if (rawSock < 0) { ```
Co-authored-by: Amos Jeffries <[email protected]>
@yadij I think this got to a point where I would feel confident about checkpointing and continuing in a new PR |
Co-authored-by: Alex Rousskov <[email protected]>
@@ -23,18 +23,34 @@ | |||
#define _S_IWRITE 0x0080 | |||
#endif | |||
|
|||
/// true if handle is a socket, false if a file or error |
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.
Where feasible, we only document one of the mutually exclusive boolean result values to avoid ambiguities, conflicts, etc.
/// true if handle is a socket, false if a file or error | |
/// whether a given handle is a valid socket |
isSocket(intptr_t handle) | ||
{ | ||
if (!isValidSocketHandle(handle)) { | ||
// errno is already set by _get_osfhandle() |
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.
If this function guarantees errno setting on errors, then it is missing a SetErrnoFromWsaError() below, right? If it does not guarantee, then this comment is misleading.
// errno is already set by _get_osfhandle() |
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.
If you mean calling SetErrnoFromWsaError();
before the return false;
statement .... then yes that is 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.
If you mean calling
SetErrnoFromWsaError();
before thereturn false;
statement
Yes, I do (and there are two return false
statement below, but it can be argued that only the second one is relevant here). However, the If
parts of my change request are very important -- I do not know whether this function should guarantee/set errno, and the correct way to address my change request depends on whether that guarantee exists.
In the ideal world, isFoo() test functions should not alter global state and, hence, should not promise to set errno. I hope we can agree on (and approach) that ideal and, hence, should not promise that isSocket() sets errno, but that remains to be seen.
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.
From _get_osfhandle()
documentation:
returns INVALID_HANDLE_VALUE (-1). It also sets errno to EBADF, indicating an invalid file handle.
There is no documentation about what a call to WSAGetLastError()
will produce. So IMHO we should not call our mapping function and clobber that EBADF
value with an unknown and possibly unrelated error value.
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.
There is no documentation about what a call to WSAGetLastError() will produce
IMO, existing WSAGetLastError() documentation strongly implies that WSAGetLastError() does not change errno. However, WSAGetLastError() behavior is secondary to what this change request is about.
I suggest to adopt an "isFoo() test functions should not alter global state" principle and adjust isSocket() code accordingly (i.e. apply my suggestion as is, without adding a SetErrnoFromWsaError() call).
Due to factors outside of our control (e.g., globals-changing non-Squid functions that isFoo() has to call), we cannot fully comply with that principle in some cases, but we can usually make our own code compliant.
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 agree on not adding the SetErrnoFromWsaError()
call. At least for the OSF API calls. If for no reason other than they are not WSA API calls.
FWIW, IMO that handling of "is" functions/methods is basic good practice we should not need to have a policy about. That calls into question whether these functions should be "is functions" at all. Maybe they are better as "requireX" instead.
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 agree on not adding the
SetErrnoFromWsaError()
call. At least for the OSF API calls. If for no reason other than they are not WSA API calls.
FWIW, IMO that handling of "is" functions/methods is basic good practice we should not need to have a policy about.
Glad we agree regarding the important bits. The difference between an implicit "basic good practice" (that everybody applies) and an explicit principle (that everybody honors) is a secondary detail. Current PR code does not follow that practice or honors that principle, prompting this change request.
That calls into question whether these functions should be "is functions" at all. Maybe they are better as "requireX" instead.
- If callers need a boolean function that tests whether condition Foo is true, then isFoo() is better than requireFoo().
- If callers need a void function that enforces condition Bar, then requireBar() is better than isBar().
- If callers need a boolean function that helps the caller enforce condition Baz (i.e. a function that contains partial enforcement code in addition to condition testing code), then neither isBaz() nor requireBaz() work well.
AFAICT, all existing isSocket() callers belong to the first category and do not enforce an "x is a socket" condition, so isSocket() name sounds correct to me.
Co-authored-by: Alex Rousskov <[email protected]>
Co-authored-by: Alex Rousskov <[email protected]>
Co-authored-by: Alex Rousskov <[email protected]>
{ WSAEISCONN, EISCONN }, | ||
|
||
{ WSAEWOULDBLOCK, EWOULDBLOCK }, | ||
// Windows does not have a equivalent of EAGAIN |
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.
To match the other two similar comments (and fix grammar):
// Windows does not have a equivalent of EAGAIN | |
// no Windows error code maps to EAGAIN |
// no Windows error code maps to ENFILE | ||
|
||
{ WSAECONNRESET, ECONNRESET } | ||
}; |
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 recommend sorting {WSAX, EY}
entries alphabetically so that it is easier to find a particular error and easier to decide where to place new entries. I would sort by the second/EY field (so that comments can participate in the same sort rather than moved to the beginning or end), but sorting by the first/WSAX field is better than no sorting at all.
I do not insist on this, but the current grouping looks odd. I am aware of this grouping origins.
const auto result = ::getsockopt(handle, level, option_name, static_cast<char *>(option_value), &ol); | ||
if (result == SOCKET_ERROR) | ||
SetErrnoFromWsaError(); | ||
return result; |
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.
Unresolving: xrecvfrom() is still broken.
// errno is already set by _get_osfhandle() | ||
return SOCKET_ERROR; | ||
} | ||
auto fl = static_cast<int>(*fromlen); |
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.
WIndows and Linux documentation says that fromlen may be nil. Please do not dereference without checking.
While fixing this, please rename fromlen
to fromLen
or similar to use camelCase and make this variable more readable.
inline ssize_t | ||
xsendto(int socketFd, const void * buf, size_t len, int flags, const struct sockaddr * to, socklen_t l) | ||
{ | ||
return sendto(socketFd, static_cast<const char *>(buf), static_cast<int>(len), flags, to, l); |
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.
These two problematic casts are Windows-specific and do not belong to this non-WIndows code, right? At least some of the existing callers do not perform these cast. Please check all non-Windows wrappers for similar unwanted casts.
winsock's socket-related functions are very similar but not
identical to the POSIX standard. Make the mswindows
compatibility layer available to MinGW, and modernize it.
Implement portable wrappers around socket-related calls, named
x[function], and use them in all callsites.
Error highlighting the issue: