Socket.st
changeset 781 4d61e7588ff3
parent 776 de39b88575e3
child 787 07e91b74589e
--- a/Socket.st	Fri Jul 23 01:21:04 1999 +0200
+++ b/Socket.st	Fri Jul 23 11:03:59 1999 +0200
@@ -1687,20 +1687,29 @@
     "
 ! !
 
-!Socket methodsFor:'ST-80 mimicri'!
+!Socket methodsFor:'ST-80 compatibility'!
 
 errorReporter
+    "ST-80 mimicry."
+
     ^ self
 !
 
 notReadySignal
-    "for now - this is not yet raised"
+    "ST-80 mimicry.
+     for now - this is not yet raised"
 
     ^ Signal new
 !
 
 readAppendStream
-    "ST-80 compatibility"
+    "ST-80 mimicry.
+     In ST-80, socket is not a stream, but referes to one.
+     ST-80 code therefore uses 'Socket readWriteStream' to access
+     the actual stream. 
+     In ST/X, sockets inherit from stream, so
+     this method returns the receiver, for transparency"
+
     ^ self
 !
 
@@ -1708,7 +1717,9 @@
     "ST-80 mimicry.
      In ST-80, socket is not a stream, but referes to one.
      ST-80 code therefore uses 'Socket readStream' to access
-     the actual stream. This method returns the receiver, for transparency"
+     the actual stream. 
+     In ST/X, sockets inherit from stream, so
+     this method returns the receiver, for transparency"
 
     ^ self
 
@@ -1725,7 +1736,9 @@
     "ST-80 mimicry.
      In ST-80, socket is not a stream, but referes to one.
      ST-80 code therefore uses 'Socket writeStream' to access
-     the actual stream. This method returns the receiver, for transparency"
+     the actual stream. 
+     In ST/X, sockets inherit from stream, so
+     this method returns the receiver, for transparency"
 
     ^ self
 
@@ -2132,39 +2145,6 @@
 
 !Socket methodsFor:'low level'!
 
-accept
-    "create a new TCP socket from accepting on the receiver.
-     This method will suspend the current process if no connection is waiting.
-     For ST-80 compatibility"
-
-    |newSock|
-
-    newSock := self class new.
-    (newSock acceptOn:self) ifFalse:[^ nil].
-    ^ newSock
-
-    "
-     |sock newSock|
-
-     sock := Socket provide:8004.
-     sock listenFor:5.
-     newSock := sock accept.
-    "
-!
-
-acceptOn:aSocket
-    "accept a connection on a server port (created with:'Socket>>onIPPort:')
-     usage is: (Socket basicNew acceptOn:(Socket onIPPort:9999)).
-     This method will suspend the current process if no connection is waiting.
-     Return the true if ok; false if not."
-
-    aSocket readWriteWait.
-    ^ self blockingAcceptOn:aSocket
-
-    "Modified: / 11.3.1996 / 14:21:31 / stefan"
-    "Modified: / 1.8.1998 / 23:39:10 / cg"
-!
-
 bindAnonymously
     ^ self
 	bindTo:0
@@ -2647,6 +2627,134 @@
     "
 !
 
+closeFile
+    "low level close"
+
+%{  /* NOCONTEXT */
+#ifndef NO_SOCKET
+
+    OBJ t;
+
+    t = __INST(filePointer);
+    if (t != nil) {
+	FILE *fp;
+
+	fp = __FILEVal(t);
+	fflush(fp);
+	shutdown(fileno(fp), 2);
+	fclose(fp);
+	__INST(filePointer) = nil;
+    }
+#endif
+%}
+!
+
+listenFor:aNumber
+    "same as listenWithBacklog: - for ST-80 compatibility"
+
+    ^ self listenWithBacklog:aNumber
+!
+
+listenWithBacklog:aNumber
+    "start listening; return true if ok, false on error"
+
+    filePointer isNil ifTrue:[
+	^ self error:'not a valid socket'
+    ].
+%{
+#ifndef NO_SOCKET
+    OBJ fp = __INST(filePointer);
+    int sock;
+    int ret;
+
+    if (! __isSmallInteger(aNumber)) {
+	DBGPRINTF(("SOCKET: invalid arg\n"));
+	RETURN (false);
+    }
+
+    sock = fileno(__FILEVal(fp));
+
+    __BEGIN_INTERRUPTABLE__
+    do {
+	ret = listen(sock, _intVal(aNumber));
+    } while ((ret < 0) && (errno == EINTR));
+    __END_INTERRUPTABLE__
+
+    if (ret < 0) {
+	DBGPRINTF(("SOCKET: listen call failed errno=%d\n", errno));
+	__INST(lastErrorNumber) = __MKSMALLINT(errno);
+	RETURN (false);
+    }
+#else
+    RETURN (false);
+#endif
+%}.
+    ^ true
+!
+
+shutDown 
+    "shutDown without flushing "
+
+    filePointer isNil ifTrue:[^ self].
+    Lobby unregister:self.
+
+%{
+#ifndef NO_SOCKET
+
+    OBJ fp;
+
+    fp = __INST(filePointer);
+    if (fp != nil) {
+	FILE *f;
+	int fd;
+
+	__INST(filePointer) = nil;
+	f = __FILEVal(fp);
+	fd = fileno(f);
+	__BEGIN_INTERRUPTABLE__
+	shutdown(fd, 2);
+	close(fd);
+	__END_INTERRUPTABLE__
+    }
+#endif
+%}
+! !
+
+!Socket methodsFor:'low level-accepting'!
+
+accept
+    "create a new TCP socket from accepting on the receiver.
+     This method will suspend the current process if no connection is waiting.
+     For ST-80 compatibility"
+
+    |newSock|
+
+    newSock := self class new.
+    (newSock acceptOn:self) ifFalse:[^ nil].
+    ^ newSock
+
+    "
+     |sock newSock|
+
+     sock := Socket provide:8004.
+     sock listenFor:5.
+     newSock := sock accept.
+    "
+!
+
+acceptOn:aSocket
+    "accept a connection on a server port (created with:'Socket>>onIPPort:')
+     usage is: (Socket basicNew acceptOn:(Socket onIPPort:9999)).
+     This method will suspend the current process if no connection is waiting.
+     Return the true if ok; false if not."
+
+    aSocket readWriteWait.
+    ^ self blockingAcceptOn:aSocket
+
+    "Modified: / 11.3.1996 / 14:21:31 / stefan"
+    "Modified: / 1.8.1998 / 23:39:10 / cg"
+!
+
 blockingAcceptOn:aSocket
     "accept a connection on a server port (created with:'Socket>>onIPPort:')
      usage is: (Socket basicNew acceptOn:(Socket onIPPort:9999)).
@@ -2941,27 +3049,81 @@
     ^ true
 !
 
-closeFile
-    "low level close"
-
-%{  /* NOCONTEXT */
-#ifndef NO_SOCKET
-
-    OBJ t;
-
-    t = __INST(filePointer);
-    if (t != nil) {
-	FILE *fp;
-
-	fp = __FILEVal(t);
-	fflush(fp);
-	shutdown(fileno(fp), 2);
-	fclose(fp);
-	__INST(filePointer) = nil;
-    }
-#endif
-%}
-!
+waitForNewConnectionOrDataOnAny:otherConnections timeout:timeoutSeconds
+    "suspend the current process, until either a new connection comes
+     in at the receiver, or data arrives on any of the otherConnections.
+     For a new connection, an accept is performed and the new socket is returned.
+     For an old connection, that socket is returned.
+     In any case, the caller gets a socket to operate on as return value,
+     or nil, if a timeout occured.
+     This method implements the inner wait-primitive of a multi-connection 
+     server application."
+
+    "/ first, a quick check if data is already available
+
+    |connection wasBlocked sema|
+
+    self canReadWithoutBlocking ifTrue:[
+        ^ self accept.
+    ].
+    otherConnections do:[:aConnection |
+        aConnection canReadWithoutBlocking ifTrue:[
+            ^ aConnection
+        ]
+    ].
+
+    [
+        "/ check again - prevent incoming interrupts from disturbing our setup
+        wasBlocked := OperatingSystem blockInterrupts.
+
+        self canReadWithoutBlocking ifTrue:[
+            wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+            ^ self accept.
+        ].
+        otherConnections do:[:aConnection |
+            aConnection canReadWithoutBlocking ifTrue:[
+                wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+                ^ aConnection
+            ]
+        ].
+
+        "/ nope - must wait.
+
+        sema := Semaphore new name:'multiReadWait'.
+        otherConnections do:[:aConnection |
+            Processor signal:sema onInput:(aConnection fileDescriptor).
+        ].
+        Processor signal:sema onInput:(self fileDescriptor).
+        timeoutSeconds notNil ifTrue:[
+            Processor signal:sema afterSeconds:timeoutSeconds 
+        ].
+        Processor activeProcess state:#ioWait.
+        sema wait.
+        Processor disableSemaphore:sema.
+
+    ] valueOnUnwindDo:[
+        Processor disableSemaphore:sema.
+        wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+    ].
+
+    "/ see who it was ...
+
+    self canReadWithoutBlocking ifTrue:[
+        ^ self accept.
+    ].
+    otherConnections do:[:aConnection |
+        aConnection canReadWithoutBlocking ifTrue:[
+            ^ aConnection
+        ]
+    ].
+
+    "/ none - a timeout
+    ^ nil
+
+
+! !
+
+!Socket methodsFor:'low level-connecting'!
 
 connectTo:hostOrPathName port:portNrOrName
     "low level connect; connect to port, portNrOrName on host, hostName.
@@ -3397,77 +3559,6 @@
      If a non-nil timeout is given, stop trying after that time and return false as well."
 
     ^ self connectTo:hostOrPathName port:portNrOrName blocking:false withTimeout:millis
-!
-
-listenFor:aNumber
-    "same as listenWithBacklog: - for ST-80 compatibility"
-
-    ^ self listenWithBacklog:aNumber
-!
-
-listenWithBacklog:aNumber
-    "start listening; return true if ok, false on error"
-
-    filePointer isNil ifTrue:[
-	^ self error:'not a valid socket'
-    ].
-%{
-#ifndef NO_SOCKET
-    OBJ fp = __INST(filePointer);
-    int sock;
-    int ret;
-
-    if (! __isSmallInteger(aNumber)) {
-	DBGPRINTF(("SOCKET: invalid arg\n"));
-	RETURN (false);
-    }
-
-    sock = fileno(__FILEVal(fp));
-
-    __BEGIN_INTERRUPTABLE__
-    do {
-	ret = listen(sock, _intVal(aNumber));
-    } while ((ret < 0) && (errno == EINTR));
-    __END_INTERRUPTABLE__
-
-    if (ret < 0) {
-	DBGPRINTF(("SOCKET: listen call failed errno=%d\n", errno));
-	__INST(lastErrorNumber) = __MKSMALLINT(errno);
-	RETURN (false);
-    }
-#else
-    RETURN (false);
-#endif
-%}.
-    ^ true
-!
-
-shutDown 
-    "shutDown without flushing "
-
-    filePointer isNil ifTrue:[^ self].
-    Lobby unregister:self.
-
-%{
-#ifndef NO_SOCKET
-
-    OBJ fp;
-
-    fp = __INST(filePointer);
-    if (fp != nil) {
-	FILE *f;
-	int fd;
-
-	__INST(filePointer) = nil;
-	f = __FILEVal(fp);
-	fd = fileno(f);
-	__BEGIN_INTERRUPTABLE__
-	shutdown(fd, 2);
-	close(fd);
-	__END_INTERRUPTABLE__
-    }
-#endif
-%}
 ! !
 
 !Socket methodsFor:'queries'!
@@ -4091,5 +4182,5 @@
 !Socket class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic2/Socket.st,v 1.120 1999-07-19 08:04:56 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic2/Socket.st,v 1.121 1999-07-23 09:03:59 cg Exp $'
 ! !