Home My Page Projects Code Snippets Project Openings SML/NJ Bugs
Summary Activity Tracker Lists

[#59] Non-blocking socket functions broken

Date:
2010-08-31 00:32
Priority:
3
State:
Closed
Submitted by:
Bug Submitter (webuser)
Assigned to:
John Reppy (jhr)
Machine Architecture:
None
Operating System:
Generic Unix
Component:
Basis Library
Resolution:
None
Severity:
Critical
OS Version:
Debian lenny
SML/NJ Version:
110.72
Keywords:
IO
URL:
Transcript (of reproduction):
I run strace sml test.sml. After startup messages, I get: socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 4 setsockopt(4, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 bind(4, {sa_family=AF_INET, sin_port=htons(1234), sin_addr=inet_addr(
Source (for reproduction):
fun test () = let val listener = INetSock.TCP.socket () val _ = Socket.Ctl.setREUSEADDR (listener, true) val _ = Socket.bind (listener, INetSock.any 1234) val _ = Socket.listen (listener, 50) fun getConnection () = case Socket.acceptNB listener of NONE = getConnection () | SOME (conn, addr) = conn val conn = getConnection () in Socket.recvVecNB (conn, 1) end val _ = test ()
Summary:
Non-blocking socket functions broken

Detailed description
Non-blocking receive and send functions will block when called
on sockets that have been created by calls to acceptNB. This makes
it impossible to implement a server that uses nonblocking network
I/O functions.
Additional comments:
The problem is found in system/Basis/Implementation/Sockets/socket.sml.

The acceptNB function calls accept(), and once a socket is returned,
it invokes sockNB to wrap it in a Socket.SOCK with {nb = true}. This
is not correct. When accept() is called on a nonblocking listen socket,
the new connection socket it returns does not inherit the nonblockingness
of the listen socket. This causes future calls to recvVecNB and so on
to think that the socket is already nonblocking, when it actually isn .

Fix:
Here is a patch, to which I release all copyright interest:

Index: socket.sml
===================================================================
--- socket.sml (revision 3552)
+++ socket.sml (working copy)
@@ -163,9 +163,6 @@
val wrapNB_o = OpsysDetails.wrapNB_o
val wrapNB_b = OpsysDetails.wrapNB_b

- fun sockB fd = SOCK { fd = fd, nb = ref false }
- fun sockNB fd = SOCK { fd = fd, nb = ref true }
-
(* socket address operations *)
fun sameAddr (ADDR a1, ADDR a2) = (a1 = a2)
local
@@ -187,13 +184,13 @@
(** Should do some range checking on backLog *)
fun listen (SOCK { fd, ... }, backLog) = listen (fd, backLog)

- fun accept0 (sock, getfd) s = let
+ fun accept0 getfd s = let
val (newFD, addr) = accept (getfd s)
in
- (sock newFD, ADDR addr)
+ (SOCK { fd = newFD, nb = ref false }, ADDR addr)
end
- fun accept s = accept0 (sockB, fdB) s
- fun acceptNB s = wrapNB_o (accept0 (sockNB, fdNB)) s
+ fun accept s = accept0 fdB s
+ fun acceptNB s = wrapNB_o (accept0 fdNB) s

fun connect0 getfd (s, ADDR addr) = connect (getfd s, addr)
fun connect arg = connect0 fdB arg


Submitted via web form by Jacob Potter jdpotter@andrew.cmu.edu

Comments:

Message  ↓
Date: 2011-04-08 16:36
Sender: John Reppy

The bug was the way that Socket.acceptNB created new sockets. They
were marked as non-blocking, when they are not, which meant that
subsequent attempts to use them in non-blocking I/O would block.

Fixed for 110.73

Attached Files:

Changes

Field Old Value Date By
status_idOpen2011-04-08 16:36jhr
close_date2011-04-08 16:362011-04-08 16:36jhr
assigned_tonone2011-03-22 13:34jhr