#REFACTORING by Stefan Reise
class: Socket
added:
#blockingNextPutAllForNonBlockingSocket:
#systemBlockingNextPutAll:
removed:
#nonBlockingNextPutAll:
#primNonBlockingNextPutAll:
changed: #setNonBlocking
--- 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'!