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

Windows (mingw): Unix.select and non-blocking sockets #4894

Closed
vicuna opened this Issue Oct 16, 2009 · 9 comments

Comments

Projects
None yet
1 participant
@vicuna
Copy link
Collaborator

vicuna commented Oct 16, 2009

Original bug ID: 4894
Reporter: gerd
Assigned to: gildor
Status: closed (set by @xavierleroy on 2012-03-24T14:01:02Z)
Resolution: fixed
Priority: normal
Severity: major
Version: 3.11.1
Fixed in version: 3.12.0+dev
Category: ~DO NOT USE (was: OCaml general)
Monitored by: mfp @ygrek

Bug description

After calling Unix.select on Ocaml 3.11.1 on a non-blocking socket, a following Unix.recv may nevertheless block.

This server code reproduces the problem:

let s = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0;;
Unix.bind s (Unix.ADDR_INET(Unix.addr_inet_any,9999));;
Unix.listen s 10;;
let t,_ = Unix.accept s;;

Now a client connects to this port, and tries to read (!) from the connection.
The server goes on like:

Unix.set_nonblock t;;
let ,,_ = Unix.select [t] [t] (-1.0);;

This select() returns immediately. However, now the socket has forgotten that it is non-blocking:

let p = String.make 1 'x';;
Unix.recv ts p 0 1 [];;

The latter hangs forever.

The OS is Win XP, newest patch level.

Additional information

If I replace the Unix.select with a call to the old (Ocaml 3.10) select wrapper, everything works as expected.

It is a bit unintuitive that a Unix.select is followed by a recv when the select said the socket is only writable, but actually this happens in some networking code. When the socket is non-blocking, the recv should be harmless, and usually fails with EWOULDBLOCK.

@vicuna

This comment has been minimized.

Copy link
Collaborator Author

vicuna commented Apr 18, 2010

Comment author: @xavierleroy

Sylvain, if you can spare the time, could you please have a look at this one? Thanks.

@vicuna

This comment has been minimized.

Copy link
Collaborator Author

vicuna commented May 10, 2010

Comment author: gildor

Gerd,

Thanks for the information. This is enough to reproduce the bug.

@vicuna

This comment has been minimized.

Copy link
Collaborator Author

vicuna commented May 10, 2010

Comment author: gildor

Test case created. I can find the bug on Windows Vista 64bits Profesionnal, I cannot find the bug on Linux (which is normal).

I browse the source code and the bug is at line 556 in otherlibs/win32unix/select.c.

I work on a patch for this.

@vicuna

This comment has been minimized.

Copy link
Collaborator Author

vicuna commented May 19, 2010

Comment author: gildor

The main problem here is to be able to know if the socket is non-blocking.

I don't see any winsock call that retrieves this information. (if you have an idea, let me know).

So basically there are two solutions:

  1. add a field in the Windows filedescr structure which contains this data

  2. go back to Winsock select call, in a separate thread. If the console exit before, cancel the running Winsock select thread...

  3. break compatibility, 2) can generate side effect...

What's your opinion?

@vicuna

This comment has been minimized.

Copy link
Collaborator Author

vicuna commented May 20, 2010

Comment author: gildor

Thread cancellation is not an option. It will make select exit too early and will probably leak resources. So running select in a separate thread is not a good idea.

I think the best option at hand is to add a "flag" field at the end of filedescr structure to store extra data. On of this data could be a FLAG_SOCKET_NONBLOCKING. We could also store more precise information about HANDLE, like combined result of GetFileType and GetConsoleMode...

Xavier and Gerd, do you have an opinion on this matter?

@vicuna

This comment has been minimized.

Copy link
Collaborator Author

vicuna commented May 20, 2010

Comment author: @xavierleroy

To determine whether a socket is non-blocking, you should be able to do
[EDIT: non-working solution, deleted]

As to recording non-blocking status in struct filedescr, I see no problem with that, compatibility or otherwise. While third-party libraries might (perhaps) use this struct, they should always go through the functions win_alloc_handle and win_alloc_socket to allocate them.

@vicuna

This comment has been minimized.

Copy link
Collaborator Author

vicuna commented May 20, 2010

Comment author: gildor

Xavier, I reread the MSDN webpage about ioctlsocket and try it, I don't think ioctlsocket get the previous mode...
http://msdn.microsoft.com/en-us/library/ms738573%28VS.85%29.aspx

Where did you find this information? I probably did something wrong in my code.

However the good news, is that I was forcing the non-blocking mode with this code (select.c):
iMode = 0;
check_error(lpSelectData,
WSAEventSelect((SOCKET)(iterQuery->hFileDescr), aEvents[i], 0) != 0 ||
ioctlsocket((SOCKET)(iterQuery->hFileDescr), FIONBIO, &iMode) != 0)

Just removing the ioctlsocket call seems to solve the problem. I think the MSDN statement about WSAEventSelect modifying the non-blocking state is not true, though I am not sure it works on all version of Windows.

I have tried with Windows Vista and it seems ok.

@vicuna

This comment has been minimized.

Copy link
Collaborator Author

vicuna commented May 21, 2010

Comment author: @xavierleroy

You're right, the "solution" I outlined probably doesn't work, I misread the MSDN doc for ioctlsocket. I edited it out of my previous comment so that no one else is mislead :-)

I think the MSDN statement about WSAEventSelect modifying the non-blocking state is not true, though I am not sure it works on all version of Windows.

Be careful. The MSDN page is very specific about what needs to be done:

"The WSAEventSelect function automatically sets socket s to nonblocking mode, regardless of the value of lNetworkEvents. To set socket s back to blocking mode, it is first necessary to clear the event record associated with socket s via a call to WSAEventSelect with lNetworkEvents set to zero and the hEventObject parameter set to NULL. You can then call ioctlsocket or WSAIoctl to set the socket back to blocking mode."

Again, recording blocking/nonblocking status in struct filedescr is easy and unproblematic, if it can help fixing this particular issue.

@vicuna

This comment has been minimized.

Copy link
Collaborator Author

vicuna commented May 25, 2010

Comment author: gildor

Add a filedescr.flags_fd in win32unix/unixsupport.h. It contains the non-blocking status of the associated filedescr and helps to restore this status after a select.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.