#TUNING by stefan
authorStefan Vogel <sv@exept.de>
Tue, 06 Dec 2016 18:46:54 +0100
changeset 21098 7b8226c89764
parent 21097 12cf2700134c
child 21099 284f7688b860
#TUNING by stefan class: Win32OperatingSystem changed: #selectOnAnyReadable:writable:exception:readableInto:writableInto:exceptionInto:withTimeOut: Speed up select on sockets
Win32OperatingSystem.st
--- a/Win32OperatingSystem.st	Tue Dec 06 14:26:48 2016 +0100
+++ b/Win32OperatingSystem.st	Tue Dec 06 18:46:54 2016 +0100
@@ -978,7 +978,6 @@
     "Modified: 7.1.1997 / 19:36:11 / stefan"
 ! !
 
-
 !Win32OperatingSystem class methodsFor:'OS signal constants'!
 
 sigABRT
@@ -12022,10 +12021,7 @@
 //#define SELECTDEBUGWIN32
 //#define SELECT3DEBUGWIN32
 #define MAXHANDLE 128
-    int i, idx;
-    INT t;
-    int numHandles;
-    DWORD res;
+    int i;
     HANDLE hArray[MAXHANDLE+1];
     int retArray[MAXHANDLE];
     int readCount, writeCount, exceptCount;
@@ -12035,315 +12031,324 @@
     fd_set readFds;
     fd_set writeFds;
     fd_set exceptFds;
-    int hasSockets;
-    int hasPipes;
+    int numHandles, numSockets, numPipes;
     int pass = 1;       // perform up to 2 passes
 
     if (readableResultFdArray != nil) {
-	if (! __isArrayLike(readableResultFdArray)) {
-	    goto fail;
-	}
-	resultSizeReadable = __arraySize(readableResultFdArray);
+        if (! __isArrayLike(readableResultFdArray)) {
+            goto fail;
+        }
+        resultSizeReadable = __arraySize(readableResultFdArray);
     }
     if (writableResultFdArray != nil) {
-	if (! __isArrayLike(writableResultFdArray)) {
-	    goto fail;
-	}
-	resultSizeWritable = __arraySize(writableResultFdArray);
-	if (readableResultFdArray == writableResultFdArray) {
-	    // allow common result set for read/write/except
-	    pcntW = &cntR;
-	}
+        if (! __isArrayLike(writableResultFdArray)) {
+            goto fail;
+        }
+        resultSizeWritable = __arraySize(writableResultFdArray);
+        if (readableResultFdArray == writableResultFdArray) {
+            // allow common result set for read/write/except
+            pcntW = &cntR;
+        }
     }
     if (exceptionResultFdArray != nil) {
-	if (! __isArrayLike(exceptionResultFdArray)) {
-	    goto fail;
-	}
-	resultSizeException = __arraySize(exceptionResultFdArray);
-	if (exceptionResultFdArray == readableResultFdArray) {
-	    // allow common result set for read/write/except
-	    pcntE = &cntR;
-	} else if (exceptionResultFdArray == writableResultFdArray) {
-	    pcntE = &cntW;
-	}
+        if (! __isArrayLike(exceptionResultFdArray)) {
+            goto fail;
+        }
+        resultSizeException = __arraySize(exceptionResultFdArray);
+        if (exceptionResultFdArray == readableResultFdArray) {
+            // allow common result set for read/write/except
+            pcntE = &cntR;
+        } else if (exceptionResultFdArray == writableResultFdArray) {
+            pcntE = &cntW;
+        }
     }
 
     if (__isNonNilObject(readFdArray)) {
-	if (! __isArrayLike(readFdArray)) goto fail;
-	readCount = __arraySize(readFdArray);
+        if (! __isArrayLike(readFdArray)) goto fail;
+        readCount = __arraySize(readFdArray);
     } else {
-	readCount = 0;
+        readCount = 0;
     }
 
     if (__isNonNilObject(writeFdArray)) {
-	if (! __isArrayLike(writeFdArray)) goto fail;
-	writeCount = __arraySize(writeFdArray);
+        if (! __isArrayLike(writeFdArray)) goto fail;
+        writeCount = __arraySize(writeFdArray);
     } else {
-	writeCount = 0;
+        writeCount = 0;
     }
 
     if (__isNonNilObject(exceptFdArray)) {
-	if (! __isArrayLike(exceptFdArray)) goto fail;
-	exceptCount = __arraySize(exceptFdArray);
+        if (! __isArrayLike(exceptFdArray)) goto fail;
+        exceptCount = __arraySize(exceptFdArray);
     } else {
-	exceptCount = 0;
+        exceptCount = 0;
     }
 
 pollAgain:
     FD_ZERO(&readFds);
     FD_ZERO(&writeFds);
     FD_ZERO(&exceptFds);
-    numHandles = hasSockets = hasPipes = 0;
+    numHandles = numSockets = numPipes = 0;
 
     for (i = 0; (i < readCount) && (numHandles < MAXHANDLE); i++) {
-	OBJ fd = __arrayVal(readFdArray)[i];
-
-	if (fd != nil) {
-	    if (__Class(fd) == @global(Win32SocketHandle)) {
-		FD_SET (_HANDLEVal(fd), &readFds);
-		hasSockets++;
-	    } else if (__isSmallInteger(fd)) {
-		DWORD canRead;
-		if (PeekNamedPipe(_get_osfhandle(__intVal(fd)), 0, 0, 0, &canRead, 0)) {
-		    if (canRead > 0) {
-			if (*pcntR < resultSizeReadable) {
-			    __arrayVal(readableResultFdArray)[*pcntR] = fd;
-			}
-			(*pcntR)++; cntAll++;
-		    }
-		} else {
-		    @global(LastErrorNumber) = __mkSmallInteger(EBADF);
-		    RETURN (__mkSmallInteger(-1));
-		}
-		hasPipes++;
-	    } else {
-		hArray  [numHandles] = _HANDLEVal(fd);
-		retArray[numHandles] = i;
-		++numHandles;
-	    }
-	}
+        OBJ fd = __arrayVal(readFdArray)[i];
+
+        if (fd != nil) {
+            if (__Class(fd) == @global(Win32SocketHandle)) {
+                FD_SET (_HANDLEVal(fd), &readFds);
+                numSockets++;
+            } else if (__isSmallInteger(fd)) {
+                DWORD canRead;
+                if (PeekNamedPipe(_get_osfhandle(__intVal(fd)), 0, 0, 0, &canRead, 0)) {
+                    if (canRead > 0) {
+                        if (*pcntR < resultSizeReadable) {
+                            __arrayVal(readableResultFdArray)[*pcntR] = fd;
+                        }
+                        (*pcntR)++; cntAll++;
+                    }
+                } else {
+                    @global(LastErrorNumber) = __mkSmallInteger(EBADF);
+                    RETURN (__mkSmallInteger(-1));
+                }
+                numPipes++;
+            } else {
+                hArray  [numHandles] = _HANDLEVal(fd);
+                retArray[numHandles] = i;
+                ++numHandles;
+            }
+        }
     }
 
     for (i = 0; (i < writeCount) && (numHandles < MAXHANDLE); i++) {
-	OBJ fd = __arrayVal(writeFdArray)[i];
-
-	if (fd != nil) {
-	    if (__Class(fd) == @global(Win32SocketHandle)) {
-		FD_SET (_HANDLEVal(fd), &writeFds);
-		hasSockets++;
-	    } else if (__isSmallInteger(fd)) {
-		// kludge: assume that pipes can alway be written
-	       if (*pcntW < resultSizeWritable) {
-		    __arrayVal(writableResultFdArray)[*pcntW] = fd;
-		}
-		(*pcntW)++; cntAll++;
-		// there is no pipe to check
-	    } else {
-		hArray  [numHandles] = _HANDLEVal(fd);
-		retArray[numHandles] = i + 10000;
-		++numHandles;
-	    }
-	}
+        OBJ fd = __arrayVal(writeFdArray)[i];
+
+        if (fd != nil) {
+            if (__Class(fd) == @global(Win32SocketHandle)) {
+                FD_SET (_HANDLEVal(fd), &writeFds);
+                numSockets++;
+            } else if (__isSmallInteger(fd)) {
+                // kludge: assume that pipes can alway be written
+               if (*pcntW < resultSizeWritable) {
+                    __arrayVal(writableResultFdArray)[*pcntW] = fd;
+                }
+                (*pcntW)++; cntAll++;
+                // there is no pipe to check
+            } else {
+                hArray  [numHandles] = _HANDLEVal(fd);
+                retArray[numHandles] = i + 10000;
+                ++numHandles;
+            }
+        }
     }
 
     for (i = 0; (i < exceptCount) && (numHandles < MAXHANDLE); i++) {
-	OBJ fdOrPid = __arrayVal(exceptFdArray)[i];
-
-	if (fdOrPid != nil) {
-	    if (__Class(fdOrPid) == @global(Win32SocketHandle)) {
-		FD_SET (_HANDLEVal(fdOrPid), &exceptFds);
-		hasSockets++;
-	    } else if (__isExternalAddressLike(fdOrPid)) {
-		// a PID
-		hArray  [numHandles] = _HANDLEVal(fdOrPid);
-		retArray[numHandles] = i + 20000;
-		++numHandles;
-	    }
-	}
-    }
-
-    if (hasSockets) {
-	struct timeval tv = {0, 0};
-	int nReady;
-
-#ifdef SELECT3DEBUGWIN32
-	console_printf("select hasSockets = %d\n", hasSockets);
-#endif
-	nReady = select(1 , &readFds, &writeFds, &exceptFds, &tv);  // first parameter to select is ignored in windows
-	if (nReady < 0) {
-#ifdef SELECTDEBUGWIN32
-	    console_printf("error in select %d %d\n", nReady, GetLastError());
-#endif
-	    @global(LastErrorNumber) = __mkSmallInteger(EBADF);
-	    RETURN (__mkSmallInteger(-1));
-	}
-	if (nReady > 0) {
-#ifdef SELECT3DEBUGWIN32
-	    console_printf("select nReady %d of %d\n", nReady, hasSockets);
-#endif
-	    for (i = 0; i < readCount; i++) {
-		OBJ fd = __arrayVal(readFdArray)[i];
-		if ((__Class(fd) == @global(Win32SocketHandle)) && FD_ISSET(_HANDLEVal(fd), &readFds)) {
-		    if (*pcntR < resultSizeReadable) {
-			__arrayVal(readableResultFdArray)[*pcntR] = fd;
-			__STORE(readableResultFdArray, fd);
-		    }
-		    (*pcntR)++; cntAll++;
-		}
-	    }
-	    for (i = 0; i < writeCount; i++) {
-		OBJ fd = __arrayVal(writeFdArray)[i];
-		if ((__Class(fd) == @global(Win32SocketHandle)) && FD_ISSET(_HANDLEVal(fd), &writeFds)) {
-		    if (*pcntW < resultSizeWritable) {
-			__arrayVal(writableResultFdArray)[*pcntW] = fd;
-			__STORE(writableResultFdArray, fd);
-		    }
-		    (*pcntW)++; cntAll++;
-		}
-	    }
-	    for (i = 0; i < exceptCount; i++) {
-		OBJ fd = __arrayVal(exceptFdArray)[i];
-		if ((__Class(fd) == @global(Win32SocketHandle)) && FD_ISSET(_HANDLEVal(fd), &exceptFds)) {
-		    if (*pcntE < resultSizeException) {
-			__arrayVal(exceptionResultFdArray)[*pcntE] = fd;
-			__STORE(exceptionResultFdArray, fd);
-		    }
-		    (*pcntE)++; cntAll++;
-		}
-	    }
-
-	}
-    }
-    if (pass > 1)       // perform maximum 2 passes
-	goto done;
-
-    if (cntAll) {
-	// check for other handles and return immediately, no timeout
-	t = 0;
-    } else {
-	if (__isSmallInteger(millis)) {
-	    t = __intVal(millis);
-
-	    if (t <= 0 && numHandles == 0) {
-		RETURN (__mkSmallInteger(0));
-	    }
-	} else {
-	    t = INFINITE;
-	}
-    }
-
-    if (numHandles == 0 && t == 0) {
-	// nothing to do and no wait
-	goto done;
-    }
+        OBJ fdOrPid = __arrayVal(exceptFdArray)[i];
+
+        if (fdOrPid != nil) {
+            if (__Class(fdOrPid) == @global(Win32SocketHandle)) {
+                FD_SET (_HANDLEVal(fdOrPid), &exceptFds);
+                numSockets++;
+            } else if (__isExternalAddressLike(fdOrPid)) {
+                // a PID
+                hArray  [numHandles] = _HANDLEVal(fdOrPid);
+                retArray[numHandles] = i + 20000;
+                ++numHandles;
+            }
+        }
+    }
+
+    // +++++ checking for Windows Handles +++++++++++++++++++++++++++++++++++++++++
+    if (numHandles != 0) {
+        DWORD res;          
+        int idx;
+        INT t;
+
+        if (numSockets || pass > 1) {
+            // do not wait - wait when checking for sockets
+            t = 0;
+        } else if (__isSmallInteger(millis)) {
+            t = __intVal(millis);
+        } else {
+            t = INFINITE;
+        }
 
 #ifdef SELECT3DEBUGWIN32
-    console_printf("wait numhandles = %d timeout = %d\n", numHandles, t);
-#endif
-
-    res = __vmWait(numHandles, hArray, MAXHANDLE, t);
-
-    if (res == WAIT_TIMEOUT) {
+        console_printf("wait numhandles = %d timeout = %d\n", numHandles, t);
+#endif
+
+        res = __vmWait(numHandles, hArray, MAXHANDLE, (int)t);
+
+        if (res == WAIT_TIMEOUT) {
 #ifdef SELECT3DEBUGWIN32
-	console_printf("- timeOut; ret nil\n" );
-#endif
-	if (t != 0 && (hasSockets || hasPipes)) {
-	    // if not a single handle is ready, poll sockets an pipes again
-	    pass = 2;
-	    goto pollAgain;
-	}
-	goto done;
-    }
-
-    if (res == WAIT_FAILED) {
+            console_printf("- timeOut" );
+#endif
+            goto checkSockets;
+        }
+        if (res == __WAIT_INTERRUPTED) {
+#ifdef SELECT3DEBUGWIN32
+            console_printf("- interrupted\n" );
+#endif
+            goto done;
+        }
+
+        if (res == WAIT_FAILED) {
 #ifdef SELECT2DEBUGWIN32
-	console_printf("- error %d (last %d); ret -1\n", __threadErrno, GetLastError());
-#endif
-	if (__threadErrno == EINTR) {
-	    @global(LastErrorNumber) = nil;
-	    RETURN (__mkSmallInteger(0));
-	} else {
-	    if (@global(InfoPrinting) == true) {
-//                console_fprintf(stderr, "Win32OS [info]: select errno = %d (last %d)\n", __threadErrno, GetLastError());
-		console_printf("Win32OS [info]: select errno = %d (last %d)\n", __threadErrno, GetLastError());
-	    }
-	    @global(LastErrorNumber) = __mkSmallInteger(EBADF);
-	    RETURN (__mkSmallInteger(-1));
-	}
-    }
-
-    if (numHandles) {
-	if (res == numHandles) {
-	    // vmwait() added an IRQ event to the handles, and this one has been triggered
-	    if (1 /* @global(InfoPrinting) == true */) {
-		console_fprintf(stderr, "Win32OS [info]: plugIn event has been handled\n");
-	    }
-	    goto done;
-	}
-	if ((res < 0) || (res >= numHandles)) {
-	    console_printf("- res=%d error1 %d\n", res, GetLastError());
-	    goto done;
-	}
-
-	idx = retArray[res];
-	cntAll++;
+            console_printf("- error %d (last %d); ret -1\n", __threadErrno, GetLastError());
+#endif
+            if (__threadErrno == EINTR) {
+                @global(LastErrorNumber) = nil;
+                RETURN (__mkSmallInteger(0));
+            } else {
+                if (@global(InfoPrinting) == true) {
+//                    console_fprintf(stderr, "Win32OS [info]: select errno = %d (last %d)\n", __threadErrno, GetLastError());
+                    console_printf("Win32OS [info]: select errno = %d (last %d)\n", __threadErrno, GetLastError());
+                }
+                @global(LastErrorNumber) = __mkSmallInteger(EBADF);
+                RETURN (__mkSmallInteger(-1));
+            }
+        }
+
+        if (res == numHandles) {
+            // vmwait() added an IRQ event to the handles, and this one has been triggered
+            if (1 /* @global(InfoPrinting) == true */) {
+                console_fprintf(stderr, "Win32OS [info]: plugIn event has been handled\n");
+            }
+            goto done;
+        }
+        if ((res < 0) || (res >= numHandles)) {
+            console_printf("- res=%d error1 %d\n", res, GetLastError());
+            goto done;
+        }
+
+        idx = retArray[res];
+        cntAll++;
 
 #ifdef SELECTDEBUGWIN32
-	console_printf("wait Handles res %d idx %d numHandles %d --- ", res, idx, numHandles);
-#endif
-	if (idx < 10000) {
-	    if (*pcntR < resultSizeReadable) {
-		OBJ temp = __arrayVal(readFdArray)[idx];
-		__arrayVal(readableResultFdArray)[*pcntR] = temp;
-		__STORE(readableResultFdArray, temp);
+        console_printf("wait Handles res %d idx %d numHandles %d --- ", res, idx, numHandles);
+#endif
+        if (idx < 10000) {
+            if (*pcntR < resultSizeReadable) {
+                OBJ temp = __arrayVal(readFdArray)[idx];
+                __arrayVal(readableResultFdArray)[*pcntR] = temp;
+                __STORE(readableResultFdArray, temp);
 #ifdef SELECTDEBUGWIN32
-		console_printf("read ready: %x\n", __externalAddressVal(temp));
-#endif
-		(*pcntR)++;
-	    }
-	} else if (idx < 20000) {
-	    if (*pcntW < resultSizeWritable) {
-		OBJ temp = __arrayVal(writeFdArray)[idx-10000];
-		__arrayVal(writableResultFdArray)[*pcntW] = temp;
-		__STORE(writableResultFdArray, temp);
+                console_printf("read ready: %x\n", __externalAddressVal(temp));
+#endif
+                (*pcntR)++;
+            }
+        } else if (idx < 20000) {
+            if (*pcntW < resultSizeWritable) {
+                OBJ temp = __arrayVal(writeFdArray)[idx-10000];
+                __arrayVal(writableResultFdArray)[*pcntW] = temp;
+                __STORE(writableResultFdArray, temp);
+#ifdef SELECTDEBUGWIN32
+                console_printf("write ready: %x\n", temp);
+#endif
+                (*pcntW)++;
+            }
+        } else {
+            if (*pcntE < resultSizeException) {
+                OBJ temp = __arrayVal(exceptFdArray)[idx-20000];
+                __arrayVal(exceptionResultFdArray)[*pcntE] = temp;
+                __STORE(exceptionResultFdArray, temp);
+#ifdef SELECTDEBUGWIN32
+                console_printf("except ready: %x\n", temp);
+#endif
+                (*pcntE)++;
+            }
 #ifdef SELECTDEBUGWIN32
-		console_printf("write ready: %x\n", temp);
-#endif
-		(*pcntW)++;
-	    }
-	} else {
-	    if (*pcntE < resultSizeException) {
-		OBJ temp = __arrayVal(exceptFdArray)[idx-20000];
-		__arrayVal(exceptionResultFdArray)[*pcntE] = temp;
-		__STORE(exceptionResultFdArray, temp);
+            else
+                console_printf("cntE: %d, resultSizeException: %d\n", *pcntE, resultSizeException);
+#endif
+        }
+    }
+
+
+// ++++++++++ Check Sockets +++++++++++++++++++++++++++++++++++
+checkSockets:
+    if (pass > 1)       // perform maximum 2 passes
+        goto done;
+
+    if (numSockets) {
+        struct timeval tv = {0, 0};
+        struct timeval *tvp = &tv;
+        int nReady;
+
+        // do not wait, if there are threads that can be resumed
+        if (!__vmTestIfAnyThreadMustBeResumed() && cntAll == 0) {
+            // no ready handles found yet - do wait
+            if (__isSmallInteger(millis)) {
+                tv.tv_usec = __intVal(millis) * 1000;
+            } else {
+                // no timeout
+                tvp = 0;
+            }
+        }
+
+#ifdef SELECT3DEBUGWIN32
+        console_printf("select numSockets = %d\n", numSockets);
+#endif
+        nReady = select(1 , &readFds, &writeFds, &exceptFds, tvp);  // first parameter to select is ignored in windows
+        if (nReady < 0) {
 #ifdef SELECTDEBUGWIN32
-		console_printf("except ready: %x\n", temp);
-#endif
-		(*pcntE)++;
-	    }
-#ifdef SELECTDEBUGWIN32
-	    else
-		console_printf("cntE: %d, resultSizeException: %d\n", *pcntE, resultSizeException);
-#endif
-	}
-    }
-    if (t != 0 && (hasSockets || hasPipes)) {
-	// back after timeout, maybe some sockets or pipes did wake up
-	// in the meantime?
-	pass = 2;
-	goto pollAgain;
+            console_printf("error in select %d %d\n", nReady, GetLastError());
+#endif
+            @global(LastErrorNumber) = __mkSmallInteger(EBADF);
+            RETURN (__mkSmallInteger(-1));
+        }
+        if (nReady > 0) {
+#ifdef SELECT3DEBUGWIN32
+            console_printf("select nReady %d of %d\n", nReady, numSockets);
+#endif
+            for (i = 0; i < readCount; i++) {
+                OBJ fd = __arrayVal(readFdArray)[i];
+                if ((__Class(fd) == @global(Win32SocketHandle)) && FD_ISSET(_HANDLEVal(fd), &readFds)) {
+                    if (*pcntR < resultSizeReadable) {
+                        __arrayVal(readableResultFdArray)[*pcntR] = fd;
+                        __STORE(readableResultFdArray, fd);
+                    }
+                    (*pcntR)++; cntAll++;
+                }
+            }
+            for (i = 0; i < writeCount; i++) {
+                OBJ fd = __arrayVal(writeFdArray)[i];
+                if ((__Class(fd) == @global(Win32SocketHandle)) && FD_ISSET(_HANDLEVal(fd), &writeFds)) {
+                    if (*pcntW < resultSizeWritable) {
+                        __arrayVal(writableResultFdArray)[*pcntW] = fd;
+                        __STORE(writableResultFdArray, fd);
+                    }
+                    (*pcntW)++; cntAll++;
+                }
+            }
+            for (i = 0; i < exceptCount; i++) {
+                OBJ fd = __arrayVal(exceptFdArray)[i];
+                if ((__Class(fd) == @global(Win32SocketHandle)) && FD_ISSET(_HANDLEVal(fd), &exceptFds)) {
+                    if (*pcntE < resultSizeException) {
+                        __arrayVal(exceptionResultFdArray)[*pcntE] = fd;
+                        __STORE(exceptionResultFdArray, fd);
+                    }
+                    (*pcntE)++; cntAll++;
+                }
+            }
+        }
+        if (tvp && tv.tv_usec != 0 && (numHandles != 0 || numPipes != 0)) {
+            // back after timeout, maybe some handles or pipes did wake up
+            // in the meantime?
+            pass = 2;
+            goto pollAgain;
+        }
     }
 
 done:
     /* add a delimiter */
     if (*pcntR < resultSizeReadable) {
-	__arrayVal(readableResultFdArray)[*pcntR] = nil;
+        __arrayVal(readableResultFdArray)[*pcntR] = nil;
     }
     if (*pcntW < resultSizeWritable) {
-	__arrayVal(writableResultFdArray)[*pcntW] = nil;
+        __arrayVal(writableResultFdArray)[*pcntW] = nil;
     }
     if (*pcntE < resultSizeException) {
-	__arrayVal(exceptionResultFdArray)[*pcntE] = nil;
+        __arrayVal(exceptionResultFdArray)[*pcntE] = nil;
     }
 
     @global(LastErrorNumber) = nil;