# HG changeset patch # User sr # Date 1583402010 -3600 # Node ID 914f6955a10a2800d23d438b7b30525bf634dc63 # Parent 50ae296c97446562941489bd399c07666b3b9300 #REFACTORING by Stefan Reise class: Socket added: #blockingNextPutAllForNonBlockingSocket: #systemBlockingNextPutAll: removed: #nonBlockingNextPutAll: #primNonBlockingNextPutAll: changed: #setNonBlocking diff -r 50ae296c9744 -r 914f6955a10a Socket.st --- a/Socket.st Wed Mar 04 17:07:17 2020 +0100 +++ b/Socket.st Thu Mar 05 10:53:30 2020 +0100 @@ -16,10 +16,10 @@ "{ NameSpace: Smalltalk }" NonPositionableExternalStream subclass:#Socket - instanceVariableNames:'domain socketType protocol port peer peerName listening' - classVariableNames:'' - poolDictionaries:'' - category:'Streams-External' + instanceVariableNames:'domain socketType protocol port peer peerName listening' + classVariableNames:'' + poolDictionaries:'' + category:'Streams-External' ! !Socket primitiveDefinitions! @@ -4375,10 +4375,36 @@ !Socket methodsFor:'support websocket'! -nonBlockingNextPutAll:someBytes - "using #primNonBlockingNextPutAll: - because #nextPutAll: handles WSAEWOULDBLOCK / EWOULDBLOCK incorrect (at least for windows) - only called by WebSocketStream #criticalSocketNextPutAll:" +blockingNextPutAllForNonBlockingSocket:someBytes + "explanation of the method name: + blocking -> this method blocks its STX process until all bytes have been written + ForNonBlockingSocket -> this method only works with non-blocking sockets + + the write is done within the current thread, + but the primitiv #send returns because the socket is non-blocking (in case of would block). + of cause if you write big data at once and the socket never would block, STX will freeze, + so please split big data into small packages" + + "PROBLEM \ BUG: + currently STX will freeze when writing and reading concurrently on the same blocking socket from both ends. + even when all those writers and readers have their own thread (by calling via __NOINT_CALL). + + WORKAROUND (or Other Concept): + using a non-blocking socket + + NEW PROBLEM: + with non-blocking sockets you can not use the default #nextPut: + because #nextPut: ends up in __NOINT_CALL('send') and probably due to until now not supported non-blocking sockets, + it does not set __threadErrno correctly to WOULDBLOCK in case of would block. + so by using default #nextPut: with an non-blocking socket you will miss some bytes or + falsely get an error instead of WOULBLOCK + + NEXT WORKAROUND: + use a your own #nextPut: method (#blockingNextPutAllForNonBlockingSocket:) and handle WOULDBLOCK correctly + + Notes: + - all sockets under windows are created as blocking sockets by default + - there is NO query in windows to ask if a socket is blocking or non-blocking" |bytes countRemainingBytesToWrite result| @@ -4393,7 +4419,7 @@ countRemainingBytesToWrite := bytes size. [ - result := self primNonBlockingNextPutAll:bytes. "/ may just writes a subset or may 0 -> would block + result := self systemBlockingNextPutAll:bytes. "/ may just writes a subset or may 0 -> would block result >= 0 "/ 0 or more bytes has been written and:[result ~= countRemainingBytesToWrite] "/ there are remaining bytes to write ] whileTrue:[ @@ -4406,15 +4432,94 @@ self writeWaitWithTimeoutMs:1000. ]. - "Created: / 30-01-2020 / 16:35:08 / Stefan Reise" - "Modified (format): / 06-02-2020 / 13:18:59 / Stefan Reise" - "Modified (comment): / 03-03-2020 / 15:25:26 / Stefan Vogel" + "Created: / 05-03-2020 / 10:35:54 / Stefan Reise" ! -primNonBlockingNextPutAll:someBytes - "using #primNonBlockingNextPutAll: - because #nextPutAll: handles WSAEWOULDBLOCK / EWOULDBLOCK incorrect (at least for windows) - only called by WebSocketStream #criticalSocketNextPutAll:" +setNonBlocking + "DO NO move this functionality into Win32OperatingSystem #setBlocking:fd:, + because it will not work correctly, caused by the following problems. + and even if the problems has been fixed, it would change the behavior for all sockets (not only websockets)" + + "PROBLEM \ BUG: + currently STX will freeze when writing and reading concurrently on the same blocking socket from both ends. + even when all those writers and readers have their own thread (by calling via __NOINT_CALL). + + WORKAROUND (or Other Concept): + using a non-blocking socket + + NEW PROBLEM: + with non-blocking sockets you can not use the default #nextPut: + because #nextPut: ends up in __NOINT_CALL('send') and probably due to until now not supported non-blocking sockets, + it does not set __threadErrno correctly to WOULDBLOCK in case of would block. + so by using default #nextPut: with an non-blocking socket you will miss some bytes or + falsely get an error instead of WOULBLOCK + + NEXT WORKAROUND: + use a your own #nextPut: method (#blockingNextPutAllForNonBlockingSocket:) and handle WOULDBLOCK correctly + + Notes: + - all sockets under windows are created as blocking sockets by default + - there is NO query in windows to ask if a socket is blocking or non-blocking" + + OperatingSystem isMSWINDOWSlike ifFalse:[ + "/ this method is for windows os only + ^ self + ]. + +%{ +# ifdef __win32__ + OBJ fp = __INST(handle); + + // ALWAYS check for proper arguments, please + if (fp != NULL) { + int result; + u_long nonBlocking = 1; + SOCKET socket = SOCKET_FROM_FILE_OBJECT(fp); + + result = ioctlsocket(socket, FIONBIO, &nonBlocking); + if (result == SOCKET_ERROR) { + console_fprintf(stderr, "Win32OS [info]: ioctlsocket failed with %d\n", WSAGetLastError()); + RETURN(false); + } + + RETURN(true); + } +#endif // __win32__ +%}. + + self primitiveFailed. + + "Modified: / 03-03-2020 / 15:29:26 / Stefan Vogel" + "Modified: / 05-03-2020 / 10:37:12 / Stefan Reise" +! + +systemBlockingNextPutAll:someBytes + "explanation of the method name: + systemBlocking -> this method blocks the entire STX + until all bytes have been written or until the socket would block + + so please split big data into small packages" + + "PROBLEM \ BUG: + currently STX will freeze when writing and reading concurrently on the same blocking socket from both ends. + even when all those writers and readers have their own thread (by calling via __NOINT_CALL). + + WORKAROUND (or Other Concept): + using a non-blocking socket + + NEW PROBLEM: + with non-blocking sockets you can not use the default #nextPut: + because #nextPut: ends up in __NOINT_CALL('send') and probably due to until now not supported non-blocking sockets, + it does not set __threadErrno correctly to WOULDBLOCK in case of would block. + so by using default #nextPut: with an non-blocking socket you will miss some bytes or + falsely get an error instead of WOULBLOCK + + NEXT WORKAROUND: + use a your own #nextPut: method (#blockingNextPutAllForNonBlockingSocket:) and handle WOULDBLOCK correctly + + Notes: + - all sockets under windows are created as blocking sockets by default + - there is NO query in windows to ask if a socket is blocking or non-blocking" |bytes byteLength returnValue wsaError| @@ -4434,10 +4539,11 @@ %{ # ifdef __win32__ + OBJ fp = __INST(handle); + // ALWAYS check for proper arguments, please - OBJ fp = __INST(handle); - - if (__isExternalAddressLike(bytes) + if ( + __isExternalAddressLike(bytes) && __isSmallInteger(byteLength) && (fp != NULL) ) { @@ -4468,45 +4574,7 @@ ^ returnValue - "Modified: / 03-03-2020 / 15:29:09 / Stefan Vogel" -! - -setNonBlocking - "using #setNonBlocking - because implementing and using Win32OperatingSystem #setBlocking:fd: - would reset to blocking (see senders of #blocking:) - and we need the socket to be non-blocking forever - only called by WebSocketStream #criticalSocketNextPutAll:" - - OperatingSystem isMSWINDOWSlike ifFalse:[ - "/ this method is for windows os only - ^ self - ]. - -%{ -# ifdef __win32__ - // ALWAYS check for proper arguments, please - OBJ fp = __INST(handle); - - if (fp != NULL) { - int result; - u_long nonBlocking = 1; - SOCKET socket = SOCKET_FROM_FILE_OBJECT(fp); - - result = ioctlsocket(socket, FIONBIO, &nonBlocking); - if (result == SOCKET_ERROR) { - console_fprintf(stderr, "Win32OS [info]: ioctlsocket failed with %d\n", WSAGetLastError()); - RETURN(false); - } - - RETURN(true); - } -#endif // __win32__ -%}. - - self primitiveFailed. - - "Modified: / 03-03-2020 / 15:29:26 / Stefan Vogel" + "Created: / 05-03-2020 / 10:48:56 / Stefan Reise" ! ! !Socket methodsFor:'testing'!