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

SOCKET changes (CSocket RAII etc.) #4348

Closed
wants to merge 2 commits into from
Closed

SOCKET changes (CSocket RAII etc.) #4348

wants to merge 2 commits into from

Conversation

Diapolo
Copy link

@Diapolo Diapolo commented Jun 16, 2014

  1. commit: cleanup use of CloseSocket()
    -- call CloseSocket() before printing any text (ensure consistent
    usage in the code), exception is when using NetworkErrorString()
  2. commit: add CSocket (RAII class that provides access to a SOCKET)

fDisconnect = true;
if (hSocket != INVALID_SOCKET)
Copy link
Member

Choose a reason for hiding this comment

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

Don't remove this check! hSocket can be INVALID_SOCKET if there is currently no connection open, in which case no closing needs to be done either.

Copy link
Author

Choose a reason for hiding this comment

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

That check could then perhaps be rewritten to?

if (closesocket(hSocket) != WSAENOTSOCK)
    LogPrint("net", "disconnecting node %s\n", addrName);

Copy link
Member

Choose a reason for hiding this comment

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

That would work. But I like the previous, explicit check better. It's just more clear "close the socket when we're connected" versus "try closing and log a message if it fails".

@laanwj
Copy link
Member

laanwj commented Jun 17, 2014

IMHO we should get rid of myclosesocket and the crazy construction in compat.h and make it a pure compatibility wrapper (only defined on unix), without extra functionality and checks.
Redefining an existing API function with unexpected behavior in the guise of a compatibility wrapper is just too crazy.

@Diapolo
Copy link
Author

Diapolo commented Jun 17, 2014

@laanwj I reworked our closesocket() to only be active on Non-Windows OSes, Windows now uses the pure API call.

inline int myclosesocket(SOCKET& hSocket)
#ifndef WIN32
// Emulate WIN32 closesocket() behaviour for other OSes
inline int closesocket(SOCKET& hSocket)
Copy link
Member

Choose a reason for hiding this comment

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

this can be just SOCKET hSocket now like a normal parameter, no need for it to be a reference anymore :)

Copy link
Author

Choose a reason for hiding this comment

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

Isn't a reference better because we then use no extra copy?

Copy link
Member

Choose a reason for hiding this comment

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

For large types such as structures and objects that can be true (but then we'd use a const reference). For small types like integers and handles it's only overhead. On 64-bit architectures a pointer to an integer is larger than the integer itself.

Copy link
Author

Choose a reason for hiding this comment

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

Understood and will be changed :).

@laanwj
Copy link
Member

laanwj commented Jun 18, 2014

The hard part now will be checking all existing closesocket() calls and determining if they should be followed by hSocket = INVALID_SOCKET.

@Diapolo
Copy link
Author

Diapolo commented Jun 18, 2014

If you mind looking at #3461 and we can get that merged, it would remove quite a few of them ;). Also the closesocket() during network init for listen sockets should be easy, which is also true for socks5(). Not sure if there are that many left then...

@laanwj
Copy link
Member

laanwj commented Jun 21, 2014

Hmm, thinking further about this, another way would be to define a CSocket class with move semantics (and a method Close(), at least, to force closing), and use that instead of passing raw file handles around.
This could avoid socket leaks completely by using RAII.
It's somewhat more complex and involved, but as we have to change every call site of closesocket() anyway it may be something to think about.

@sipa
Copy link
Member

sipa commented Jun 21, 2014

I like the idea of a RAII wrapper for sockets.

@Diapolo
Copy link
Author

Diapolo commented Jun 22, 2014

@sipa @laanwj I started implementing CSocket, can you give me some feedback and help to see if I'm on the right track please :). See: https://github.com/Diapolo/bitcoin/commit/1c5873e0ba4cc466c11e5a0dbc779d78b5662075

Edit: The new class is currently only used for listening sockets to show the current implementation state.
Edit 2: Also it seems that code is currently not working ^^... seems to be too late... a look from some experienced dev would be great anyway.

@Diapolo
Copy link
Author

Diapolo commented Jun 22, 2014

@laanwj @sipa Alright, I was able to fix the problems from last night. During BOOST_FOREACH processing existing sockets were copied/used and got closed by the destructor after the loop ^^. This is now fixed by a new flag fCloseOnDestruct in the CSocket class. I need some initial feedback now :).

Commit is at: https://github.com/Diapolo/bitcoin/commit/079dab40b8e2d88786d2fd0bef9025afa5719b9d

@Diapolo
Copy link
Author

Diapolo commented Jun 22, 2014

I'm going for some incremental pulls that are included here, see #4388 for the first one.

@Diapolo
Copy link
Author

Diapolo commented Jun 24, 2014

Did anyone take a look at the RAII class yet? I don't want to continue, if it's bugged or it's implementation is not correct.

@jgarzik
Copy link
Contributor

jgarzik commented Jun 26, 2014

If we are going to have an RAII CSocket, we might as well add classes Connection, SocketServer, IOEngine.

Then we can ditch boost::asio for the RPC server, and use that instead.

@laanwj
Copy link
Member

laanwj commented Jun 26, 2014

@Diapolo No, haven't got around to it.

@jgarzik I disagree we should be spending time writing and maintaining our own asynchronous I/O library. It's not like we're innovating anything here. If we want to ditch boost::asio we should switch to something which is well-known to be robust and high-performance like libevent (or derivatives).

@jgarzik
Copy link
Contributor

jgarzik commented Jun 26, 2014

If we don't want our own async I/O lib, then adding RAII CSocket is moving in the wrong direction. boost::asio already has portable socket classes and much much more.

@laanwj
Copy link
Member

laanwj commented Jun 26, 2014

Well - in principle I agree that using the same network library everywhere would be a good thing. But I know at least @gmaxwell doesn't like boost::asio and doesn't want to switch the P2P code to it.

So at some point we have to make a decision about what async networking library to use. Not necessarily now.

The point here was to get rid of the strange compat.h behavior - a compatibility wrapper should provide compatibility not add functionality. I then thought, if we're changing socket closing behavior we might as well make it RAII. But if that's a stap too far, fair enough...

@Diapolo
Copy link
Author

Diapolo commented Jul 1, 2014

@laanwj Thinking about always call closesocket() before printing any text (ensure consistent usage in the code)... closesocket() can fail, which would likely interact with our LogPrint()... so I guess it's a good idea to first log and close the socket afterwards... but then everywhere in our code.

@laanwj
Copy link
Member

laanwj commented Jul 1, 2014

@Diapolo Well in cases where the purpose of the Logprintf is to log the error it should definitely be logged before the close, otherwise it will log the status of the close(). In other cases it doesn't matter what you do first.

@Diapolo
Copy link
Author

Diapolo commented Jul 1, 2014

@laanwj Right, it's only relevant, when a call to NetworkErrorString() is used for the logging.

@Diapolo
Copy link
Author

Diapolo commented Jul 15, 2014

Did anyone look at the RAII wrapper yet or is this considered unhelpful in the end?
Edit: I know this needs a rebase now...

@laanwj
Copy link
Member

laanwj commented Jul 16, 2014

@Diapolo I've looked at it and it looks okay, but as @jgarzik says it may be too much if we intend to move to some other async networking library for P2P. Although it is clearly the way forward - from another async networking library we'd expect nothing less - right now it may brings subtle new bugs that have to be debugged. So I prefer the more trivial change in #4504 for now.

@Diapolo
Copy link
Author

Diapolo commented Jul 16, 2014

@laanwj Rebased and updated to include changes for node whitelisting. I'm not sure #4504 is an alternative, as it's "just" refactoring.

laanwj added a commit to laanwj/bitcoin that referenced this pull request Jul 17, 2014
Simpler alternative to bitcoin#4348.

The current setup with closesocket() is strange. It poses
as a compatibility wrapper but adds functionality.

Rename it and make it a documented utility function in netbase.

Code movement only, zero effect on the functionality.
- call CloseSocket() before printing any text (ensure consistent
  usage in the code), exception is when using NetworkErrorString()
@Diapolo Diapolo changed the title extend and cleanup use of closesocket() SOCKET changes (CSocket RAII etc.) Jul 21, 2014
@Diapolo
Copy link
Author

Diapolo commented Jul 21, 2014

@laanwj I reworked and rethought this, there is no need to try to emulate WIN32 closesocket() behaviour for non-WIN32, as close() gives error EBADF - fd isn't a valid open file descriptor., which we typedef as WSAENOTSOCK in compat.h anyway ;). So they behave the same already.

@jgarzik
Copy link
Contributor

jgarzik commented Jul 21, 2014

CSocket seems to add never-used methods and flags.

@Diapolo
Copy link
Author

Diapolo commented Jul 21, 2014

@jgarzik Right, the ones who are unused are void SetCloseOnDestruct(bool fFlag); and void SetWhitelisted(bool fFlag); and I mentioned that in a comment in the source. Also CSocket is currently ONLY used for listen sockets, because I didn't want to change the whole source, before anyone gave some feedback to my implementation.

@BitcoinPullTester
Copy link

Automatic sanity-testing: PASSED, see http://jenkins.bluematt.me/pull-tester/p4348_684bec2f41d0963fe24cf55cd397b962a25529cf/ for binaries and test log.
This test script verifies pulls every time they are updated. It, however, dies sometimes and fails to test properly. If you are waiting on a test, please check timestamps to verify that the test.log is moving at http://jenkins.bluematt.me/pull-tester/current/
Contact BlueMatt on freenode if something looks broken.

@kazcw
Copy link
Contributor

kazcw commented Jul 21, 2014

The move semantics seem important. Right now you can copy or assign a socket and have two sockets using the same handle; then if one goes out of scope or is Close()ed the other will have its underlying socket closed without CloseSocket setting hSocket to INVALID_SOCKET, so it still returns IsValid()==true. With cloning disallowed, fCloseOnDestruct shouldn't be necessary.

Putting the "whitelisted" flag in what would otherwise be a general RAII socket wrapper seems odd. Why not keep the ListenSocket struct, and just change it from using a handle to a CSocket?

Having the Get() method is no different from just making the hSocket a public member. I suggest either adding wrappers for the functions that take sockets (non-member friend functions could do the FD_SET operations), or if CSocket is to be a struct with a destructor, making it look like a struct with a destructor.

@Diapolo
Copy link
Author

Diapolo commented Jul 25, 2014

@kazcw I guess I need some more help to implement what you suggest about disallow cloning. Makes sense that no CSocket should have the same handle. How can this be achieved?

@kazcw
Copy link
Contributor

kazcw commented Jul 25, 2014

To prevent copying, you can declare a private copy constructor with no implementation.

vhListenSocket would need to be a vector of pointers.

@laanwj laanwj added the P2P label Jul 31, 2014
@laanwj
Copy link
Member

laanwj commented Aug 4, 2014

As said before, I deem the RAII changes too risky for what it would bring and am not going to merge it, sorry.

This would make sense as part of a change that wraps all networking behavior in proper classes, but if we make such a high-impact change to networking we should consider switching to an existing high-performance async networking library instead of mucking about with our own.

Something to consider in the future, but right now there are much more pressing things.

@laanwj laanwj closed this Aug 4, 2014
@Diapolo Diapolo mentioned this pull request Aug 5, 2014
@Diapolo
Copy link
Author

Diapolo commented Aug 5, 2014

@laanwj I created #4636 as these changes seem uncontroversial.

@Diapolo Diapolo deleted the net_cleanup_1 branch August 5, 2014 11:43
MathyV pushed a commit to reddcoin-project/reddcoin-3.10 that referenced this pull request Nov 24, 2014
Simpler alternative to bitcoin#4348.

The current setup with closesocket() is strange. It poses
as a compatibility wrapper but adds functionality.

Rename it and make it a documented utility function in netbase.

Code movement only, zero effect on the functionality.
reddink pushed a commit to reddcoin-project/reddcoin-3.10 that referenced this pull request May 27, 2020
Simpler alternative to bitcoin#4348.

The current setup with closesocket() is strange. It poses
as a compatibility wrapper but adds functionality.

Rename it and make it a documented utility function in netbase.

Code movement only, zero effect on the functionality.

(cherry picked from commit 43f510d)

# Conflicts:
#	src/netbase.cpp
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Sep 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants