[squeak-dev] The Inbox: Network-ul.180.mcz
Levente Uzonyi
leves at caesar.elte.hu
Wed Aug 3 15:10:44 UTC 2016
Hi Chris,
Thanks for tracking this down. I'll write some tests to see if the VM
actually supports half-closed connections properly when I have time.
If not, I'll revert this change before the release.
Levente
On Tue, 2 Aug 2016, Chris Muller wrote:
> Hi Levente, I've narrowed Magma's problem with these changes down to
> one line in Socket>>#waitForSendDoneFor:. If I revert the call to
> #isThisEndConnected back to #isConnected, Magma's test suite works
> fine.
>
> waitForSendDoneFor: timeout
> "Wait up until the given deadline for the current send operation
> to complete. Return true if it completes by the deadline, false if
> not."
>
> | deadline timeleft |
> deadline := Time millisecondClockValue + (timeout * 1000) truncated.
> [
> (self primSocketSendDone: socketHandle) ifTrue: [ ^true ].
> self isThisEndConnected ifFalse: [ ^false ]. "<---- want to
> go back to #isConnected for this line"
> (timeleft := deadline - Time millisecondClockValue) <= 0
> ifTrue: [ ^false ].
> writeSemaphore waitTimeoutMSecs: timeleft ] repeat
>
> It affects Magma's High-Availability test cases, which involve
> terminating the server while its busy serving clients. For each
> release of Squeak, I put out a new release of Magma that works OOTB.
> I would very much like to be able to do that this time too, without
> confusion of a changeset / modified system for Magma users. Would you
> be willing to revert this one line to buy us time to explore this
> change together under greater scrutiny for the duration of the next
> release of Squeak, instead of this release?
>
> Best,
> Chris
>
> On Sun, Jul 31, 2016 at 8:39 PM, Levente Uzonyi <leves at caesar.elte.hu> wrote:
>> Hi Chris,
>>
>> I decide to move this to the Trunk because the feature freeze is here. This
>> should also help getting more feedback. :)
>>
>> Levente
>>
>>
>> On Mon, 25 Jul 2016, Chris Muller wrote:
>>
>>> Hi Levente, Magma's test cases can't seem to get to the end with
>>> this.. I haven't investigated yet..
>>>
>>> On Mon, Jul 25, 2016 at 2:40 PM, Levente Uzonyi <leves at caesar.elte.hu>
>>> wrote:
>>>>
>>>> Hi All,
>>>>
>>>> This is something to test on all platforms before push. I've used it on
>>>> 64-bit linux without problems so far.
>>>>
>>>> Levente
>>>>
>>>>
>>>> On Mon, 25 Jul 2016, commits at source.squeak.org wrote:
>>>>
>>>>> Levente Uzonyi uploaded a new version of Network to project The Inbox:
>>>>> http://source.squeak.org/inbox/Network-ul.180.mcz
>>>>>
>>>>> ==================== Summary ====================
>>>>>
>>>>> Name: Network-ul.180
>>>>> Author: ul
>>>>> Time: 25 July 2016, 8:40:01.001452 pm
>>>>> UUID: 2f23a55c-fec5-41ac-95bd-6a8c2458be95
>>>>> Ancestors: Network-nice.179
>>>>>
>>>>> Socket changes:
>>>>> - fixed the comment of #isOtherEndConnected and #isThisEndConnected
>>>>> - do not slice the data (TCP) in the image in #sendData:. Let the VM,
>>>>> the
>>>>> kernel, the hardware deal with that.
>>>>> - use #isOtherEndConnected when receiving data, and #isThisEndConnected
>>>>> when sending data instead of #isConnected
>>>>> - move away from #milliseconds:since:, since we have a clock that won't
>>>>> roll over
>>>>>
>>>>> =============== Diff against Network-nice.179 ===============
>>>>>
>>>>> Item was changed:
>>>>> ----- Method: Socket>>closeAndDestroy: (in category 'connection
>>>>> open/close') -----
>>>>> closeAndDestroy: timeoutSeconds
>>>>> "First, try to close this connection gracefully. If the close
>>>>> attempt fails or times out, abort the connection. In either case,
>>>>> destroy
>>>>> the socket. Do nothing if the socket has already been destroyed (i.e.,
>>>>> if
>>>>> its socketHandle is nil)."
>>>>>
>>>>> + socketHandle ifNil: [ ^self ].
>>>>> + self isThisEndConnected ifTrue: [
>>>>> + self close. "Close this end." ].
>>>>> + (self waitForDisconnectionFor: timeoutSeconds) ifFalse: [
>>>>> + "The other end has not closed the connect yet, so we
>>>>> will
>>>>> just abort it."
>>>>> + self primSocketAbortConnection: socketHandle ].
>>>>> + self destroy!
>>>>> - socketHandle ifNotNil: [
>>>>> - self isConnected ifTrue: [
>>>>> - self close. "close this end"
>>>>> - (self waitForDisconnectionFor:
>>>>> timeoutSeconds) ifFalse: [
>>>>> - "The other end didn't
>>>>> close so we just abort the connection"
>>>>> - self
>>>>> primSocketAbortConnection: socketHandle]].
>>>>> - self destroy].
>>>>> - !
>>>>>
>>>>> Item was changed:
>>>>> ----- Method: Socket>>discardReceivedData (in category 'receiving')
>>>>> -----
>>>>> discardReceivedData
>>>>> "Discard any data received up until now, and return the number
>>>>> of
>>>>> bytes discarded."
>>>>>
>>>>> | buf totalBytesDiscarded |
>>>>> buf := String new: 10000.
>>>>> totalBytesDiscarded := 0.
>>>>> + [self isOtherEndConnected and: [self dataAvailable]] whileTrue:
>>>>> [
>>>>> - [self isConnected and: [self dataAvailable]] whileTrue: [
>>>>> totalBytesDiscarded :=
>>>>> totalBytesDiscarded + (self receiveDataInto:
>>>>> buf)].
>>>>> ^ totalBytesDiscarded
>>>>> !
>>>>>
>>>>> Item was changed:
>>>>> ----- Method: Socket>>isOtherEndConnected (in category 'queries') -----
>>>>> isOtherEndConnected
>>>>> + "Return true if this socket is connected, or this end has closed
>>>>> the connection but not the other end, so we can still receive data."
>>>>> - "Return true if this socket is connected, or this end has closed
>>>>> the connection but not the other end, so we can still send data."
>>>>>
>>>>> | state |
>>>>> socketHandle ifNil: [ ^false ].
>>>>> (state := self primSocketConnectionStatus: socketHandle) ==
>>>>> Connected ifTrue: [ ^true ].
>>>>> ^state == ThisEndClosed
>>>>> !
>>>>>
>>>>> Item was changed:
>>>>> ----- Method: Socket>>isThisEndConnected (in category 'queries') -----
>>>>> isThisEndConnected
>>>>> + "Return true if this socket is connected, other the other end
>>>>> has
>>>>> closed the connection but not this end, so we can still send data."
>>>>> - "Return true if this socket is connected, other the other end
>>>>> has
>>>>> closed the connection but not this end, so we can still receive data."
>>>>>
>>>>> | state |
>>>>> socketHandle ifNil: [ ^false ].
>>>>> (state := self primSocketConnectionStatus: socketHandle) ==
>>>>> Connected ifTrue: [ ^true ].
>>>>> ^state == OtherEndClosed
>>>>> !
>>>>>
>>>>> Item was changed:
>>>>> ----- Method: Socket>>sendData: (in category 'sending') -----
>>>>> sendData: aStringOrByteArray
>>>>> "Send all of the data in the given array, even if it requires
>>>>> multiple calls to send it all. Return the number of bytes sent."
>>>>>
>>>>> "An experimental version use on slow lines: Longer timeout and
>>>>> smaller writes to try to avoid spurious timeouts."
>>>>>
>>>>> | bytesSent bytesToSend count |
>>>>> bytesToSend := aStringOrByteArray size.
>>>>> bytesSent := 0.
>>>>> [bytesSent < bytesToSend] whileTrue: [
>>>>> (self waitForSendDoneFor: 60)
>>>>> ifFalse: [ConnectionTimedOut signal: 'send data
>>>>> timeout; data not sent'].
>>>>> count := self primSocket: socketHandle
>>>>> sendData: aStringOrByteArray
>>>>> startIndex: bytesSent + 1
>>>>> + count: bytesToSend - bytesSent.
>>>>> - count: (bytesToSend - bytesSent min:
>>>>> DefaultSendBufferSize).
>>>>> bytesSent := bytesSent + count].
>>>>>
>>>>> ^ bytesSent
>>>>> !
>>>>>
>>>>> Item was changed:
>>>>> ----- Method: Socket>>waitForConnectionFor:ifTimedOut:ifRefused: (in
>>>>> category 'waiting') -----
>>>>> waitForConnectionFor: timeout ifTimedOut: timeoutBlock ifRefused:
>>>>> refusedBlock
>>>>> "Wait up until the given deadline for a connection to be
>>>>> established. Return true if it is established by the deadline, false if
>>>>> not."
>>>>>
>>>>> + | deadline timeLeft status |
>>>>> + deadline := Time millisecondClockValue + (timeout * 1000)
>>>>> truncated.
>>>>> + (status := self primSocketConnectionStatus: socketHandle) ==
>>>>> Connected ifTrue: [^true].
>>>>> + [ (status == WaitingForConnection) and: [ (timeLeft := deadline
>>>>> -
>>>>> Time millisecondClockValue) > 0 ] ]
>>>>> - | startTime msecsDelta msecsEllapsed status |
>>>>> - startTime := Time millisecondClockValue.
>>>>> - msecsDelta := (timeout * 1000) truncated.
>>>>> - status := self primSocketConnectionStatus: socketHandle.
>>>>> - status = Connected ifTrue: [^true].
>>>>> - [(status = WaitingForConnection) and: [(msecsEllapsed := Time
>>>>> millisecondsSince: startTime) < msecsDelta]]
>>>>> whileTrue: [
>>>>> + semaphore waitTimeoutMSecs: timeLeft.
>>>>> + status := self primSocketConnectionStatus:
>>>>> socketHandle ].
>>>>> + status == Connected ifTrue: [ ^true ].
>>>>> + status == WaitingForConnection
>>>>> + ifTrue: [ timeoutBlock value ]
>>>>> + ifFalse: [ refusedBlock value ].
>>>>> + ^false!
>>>>> - semaphore waitTimeoutMSecs: msecsDelta -
>>>>> msecsEllapsed.
>>>>> - status := self primSocketConnectionStatus:
>>>>> socketHandle].
>>>>> - status = Connected
>>>>> - ifFalse: [
>>>>> - status = WaitingForConnection
>>>>> - ifTrue: [timeoutBlock value]
>>>>> - ifFalse: [refusedBlock value].
>>>>> - ^false].
>>>>> - ^ true!
>>>>>
>>>>> Item was changed:
>>>>> ----- Method: Socket>>waitForConnectionUntil: (in category 'waiting')
>>>>> -----
>>>>> waitForConnectionUntil: deadline
>>>>> "Wait up until the given deadline for a connection to be
>>>>> established. Return true if it is established by the deadline, false if
>>>>> not."
>>>>>
>>>>> + | status timeLeft |
>>>>> - | status waitTime |
>>>>> [
>>>>> (status := self primSocketConnectionStatus:
>>>>> socketHandle)
>>>>> == Connected ifTrue: [ ^true ].
>>>>> status == WaitingForConnection ifFalse: [ ^false ].
>>>>> + (timeLeft := deadline - Time millisecondClockValue) <= 0
>>>>> ifTrue: [ ^false ].
>>>>> + semaphore waitTimeoutMSecs: timeLeft ] repeat!
>>>>> - (waitTime := deadline - Time millisecondClockValue) > 0
>>>>> ifFalse: [ ^false ].
>>>>> - semaphore waitTimeoutMSecs: waitTime ] repeat!
>>>>>
>>>>> Item was changed:
>>>>> ----- Method: Socket>>waitForDataFor:ifClosed:ifTimedOut: (in category
>>>>> 'waiting') -----
>>>>> waitForDataFor: timeout ifClosed: closedBlock ifTimedOut: timedOutBlock
>>>>> "Wait for the given nr of seconds for data to arrive."
>>>>>
>>>>> + | deadline timeLeft |
>>>>> - | startTime msecsDelta |
>>>>> socketHandle ifNil: [ ^closedBlock value ].
>>>>> + deadline := Time millisecondClockValue + (timeout * 1000)
>>>>> truncated.
>>>>> - startTime := Time millisecondClockValue.
>>>>> - msecsDelta := (timeout * 1000) truncated.
>>>>> [
>>>>> (self primSocketReceiveDataAvailable: socketHandle)
>>>>> ifTrue: [ ^self ].
>>>>> + self isOtherEndConnected ifFalse: [ ^closedBlock value
>>>>> ].
>>>>> + (timeLeft := deadline - Time millisecondClockValue) <= 0
>>>>> ifTrue: [ ^timedOutBlock value ].
>>>>> - self isConnected ifFalse: [ ^closedBlock value ].
>>>>> - (Time millisecondsSince: startTime) < msecsDelta
>>>>> ifFalse:
>>>>> [ ^timedOutBlock value ].
>>>>> "Providing a maximum for the time for waiting is a
>>>>> workaround for a VM bug which causes sockets waiting for data forever in
>>>>> some rare cases, because the semaphore doesn't get signaled. Remove the
>>>>> ""min: self class maximumReadSemaphoreWaitTimeout"" part when the bug is
>>>>> fixed."
>>>>> readSemaphore waitTimeoutMSecs:
>>>>> + (timeLeft min: self class
>>>>> maximumReadSemaphoreWaitTimeout) ] repeat!
>>>>> - (msecsDelta - (Time millisecondsSince:
>>>>> startTime)
>>>>> min: self class maximumReadSemaphoreWaitTimeout) ] repeat!
>>>>>
>>>>> Item was changed:
>>>>> ----- Method: Socket>>waitForDataIfClosed: (in category 'waiting')
>>>>> -----
>>>>> waitForDataIfClosed: closedBlock
>>>>> "Wait indefinitely for data to arrive. This method will block
>>>>> until
>>>>> data is available or the socket is closed."
>>>>>
>>>>> socketHandle ifNil: [ ^closedBlock value ].
>>>>> [
>>>>> (self primSocketReceiveDataAvailable: socketHandle)
>>>>> ifTrue: [ ^self ].
>>>>> + self isOtherEndConnected ifFalse: [ ^closedBlock value
>>>>> ].
>>>>> - self isConnected ifFalse: [ ^closedBlock value ].
>>>>> "ul 8/13/2014 21:16
>>>>> Providing a maximum for the time for waiting is a
>>>>> workaround for a VM bug which
>>>>> causes sockets waiting for data forever in some rare
>>>>> cases, because the semaphore
>>>>> doesn't get signaled. Replace the ""waitTimeoutMSecs:
>>>>> self class maximumReadSemaphoreWaitTimeout""
>>>>> part with ""wait"" when the bug is fixed."
>>>>> readSemaphore waitTimeoutMSecs: self class
>>>>> maximumReadSemaphoreWaitTimeout ] repeat!
>>>>>
>>>>> Item was changed:
>>>>> ----- Method: Socket>>waitForDisconnectionFor: (in category 'waiting')
>>>>> -----
>>>>> waitForDisconnectionFor: timeout
>>>>> "Wait for the given nr of seconds for the connection to be
>>>>> broken.
>>>>> Return true if it is broken by the deadline, false if not.
>>>>> The client should know the connection is really going to be
>>>>> closed
>>>>> (e.g., because he has called 'close' to send a close request to
>>>>> the other end)
>>>>> before calling this method."
>>>>>
>>>>> + | deadline |
>>>>> + deadline := Time millisecondClockValue + (timeout * 1000)
>>>>> truncated.
>>>>> + [ self isOtherEndConnected and: [ deadline - Time
>>>>> millisecondClockValue > 0 ] ]
>>>>> + whileTrue: [
>>>>> + self discardReceivedData.
>>>>> + "Providing a maximum for the time for waiting is
>>>>> a
>>>>> workaround for a VM bug which causes sockets waiting for data forever in
>>>>> some rare cases, because the semaphore doesn't get signaled. Remove the
>>>>> ""min: self class maximumReadSemaphoreWaitTimeout"" part when the bug is
>>>>> fixed."
>>>>> + readSemaphore waitTimeoutMSecs:
>>>>> + (deadline - Time millisecondClockValue
>>>>> min: self class maximumReadSemaphoreWaitTimeout) ].
>>>>> + ^self isOtherEndConnected!
>>>>> - | startTime msecsDelta status |
>>>>> - startTime := Time millisecondClockValue.
>>>>> - msecsDelta := (timeout * 1000) truncated.
>>>>> - status := self primSocketConnectionStatus: socketHandle.
>>>>> - [((status == Connected) or: [(status == ThisEndClosed)]) and:
>>>>> - [(Time millisecondsSince: startTime) < msecsDelta]] whileTrue:
>>>>> [
>>>>> - self discardReceivedData.
>>>>> - "Providing a maximum for the time for waiting is a
>>>>> workaround for a VM bug which causes sockets waiting for data forever in
>>>>> some rare cases, because the semaphore doesn't get signaled. Remove the
>>>>> ""min: self class maximumReadSemaphoreWaitTimeout"" part when the bug is
>>>>> fixed."
>>>>> - readSemaphore waitTimeoutMSecs:
>>>>> - (msecsDelta - (Time millisecondsSince:
>>>>> startTime)
>>>>> min: self class maximumReadSemaphoreWaitTimeout).
>>>>> - status := self primSocketConnectionStatus:
>>>>> socketHandle].
>>>>> - ^ status ~= Connected!
>>>>>
>>>>> Item was changed:
>>>>> ----- Method: Socket>>waitForSendDoneFor: (in category 'waiting') -----
>>>>> waitForSendDoneFor: timeout
>>>>> "Wait up until the given deadline for the current send operation
>>>>> to complete. Return true if it completes by the deadline, false if not."
>>>>>
>>>>> + | deadline timeleft |
>>>>> + deadline := Time millisecondClockValue + (timeout * 1000)
>>>>> truncated.
>>>>> - | startTime msecsDelta msecsEllapsed |
>>>>> - startTime := Time millisecondClockValue.
>>>>> - msecsDelta := (timeout * 1000) truncated.
>>>>> [
>>>>> (self primSocketSendDone: socketHandle) ifTrue: [ ^true
>>>>> ].
>>>>> + self isThisEndConnected ifFalse: [ ^false ].
>>>>> + (timeleft := deadline - Time millisecondClockValue) <= 0
>>>>> ifTrue: [ ^false ].
>>>>> + writeSemaphore waitTimeoutMSecs: timeleft ] repeat!
>>>>> - self isConnected ifFalse: [ ^false ].
>>>>> - (msecsEllapsed := Time millisecondsSince: startTime) <
>>>>> msecsDelta ifFalse: [ ^false ].
>>>>> - writeSemaphore waitTimeoutMSecs: msecsDelta -
>>>>> msecsEllapsed ] repeat!
>>>>>
>>>>>
>>>>>
>>>>
>>>
>>>
>>
>
>
More information about the Squeak-dev
mailing list
|