The simplest solution is to make the function that you're spawning return the socket. Then you can join the thread to get it back. Otherwise, you can use a mutex.
I'm not familiar with tungstenite, but I would expect its sockets to close automatically on drop, which in your case would happen when the thread closure completes (due either to an error or to success).
Oh yes, it's weird that you want to move the socket to another thread just to call close on it. Why can't you close it in the spawned thread? How are you notifying the main thread that the socket should be closed?
Are you saying that you want to be able to close the socket "across threads" while it's in the middle of a read? If so, option 1 would be to use a non-blocking version of read() that returns immediately if there's no data available â if such a method exists on the socket â and then call this in a loop that checks a flag on every iteration, and then another thread can set this flag to true when you want to close the socket. Option 2 would be to go full async.
Looking at the API I see that both read and close require &mut WebSocket. And there is no read with a timeout as you said. That means it can be impossible to close the socket! That's a terrible API. Hopefully we're missing something but there isn't a lot to it.
There is no non-blocking read. That's just the problem. Closure takes ownership, then blocks on read.
I've avoided async throughout the app because I'm not a big fan of it. The app using threads and channels for concurrency - and all of it works great.
In practice, calling close() may not be necessary because read() will return an error when the server ends the connection. But I'd rather not build the system around that.
When the remote endpoint decides to close the connection this will return the close message with an optional close frame.
You should continue calling read, write or flush to drive the reply to the close frame until Error::ConnectionClosed is returned. Once that happens it is safe to drop the underlying connection.
I think the intention of tungstenite is that you will give it a reader that never blocks, but instead throws WouldBlock. This will be passed up through WebSocket::read and you can handle it there. If you don't want to deal with that stuff, then tokio-tungstenite is probably better.
Alternatively it looks all variants of the MaybeTlsStream<TcpStream> you get out of connect[1] offer a route (e.g. .get_ref()) to get a &TcpStream you can call set_nonblocking on.
I was going to suggest that also, but if you have to do the handshake yourself, you've lost the simplicity of it. I would just use an async websocket crate at that point.