@@ -465,11 +465,18 @@ Socket >> connectTo: hostAddress port: port [
465
465
Socket >> connectTo: hostAddress port: port waitForConnectionFor: timeout [
466
466
" Initiate a connection to the given port at the given host
467
467
address. Waits until the connection is established or time outs."
468
+
468
469
self connectNonBlockingTo: hostAddress port: port.
469
470
self
470
471
waitForConnectionFor: timeout
471
- ifTimedOut: [ConnectionTimedOut signal : ' Cannot connect to '
472
- , (NetNameResolver stringFromAddress: hostAddress) , ' :' , port asString]
472
+ ifClosed: [
473
+ ConnectionClosed signal : ' Connection aborted to '
474
+ , (NetNameResolver stringFromAddress: hostAddress) , ' :'
475
+ , port asString ]
476
+ ifTimedOut: [
477
+ ConnectionTimedOut signal : ' Cannot connect to '
478
+ , (NetNameResolver stringFromAddress: hostAddress) , ' :'
479
+ , port asString ]
473
480
]
474
481
475
482
{ #category : ' connection open/close' }
@@ -489,18 +496,30 @@ Socket >> dataAvailable [
489
496
490
497
{ #category : ' initialize - destroy' }
491
498
Socket >> destroy [
492
- " Destroy this socket. Its connection, if any, is aborted and its resources are freed. Do nothing if the socket has already been destroyed (i.e., if its socketHandle is nil)."
499
+ " Destroy this socket. Its connection, if any, is aborted and its resources are freed.
500
+ Any processes waiting on the socket are freed immediately, but it is up to them to
501
+ recognize that the socket has been destroyed.
502
+ Do nothing if the socket has already been destroyed (i.e., if its socketHandle is nil)."
493
503
494
- socketHandle
495
- ifNotNil: [
496
- self isValid
497
- ifTrue: [ self primSocketDestroy: socketHandle ].
498
- Smalltalk unregisterExternalObject: semaphore.
499
- Smalltalk unregisterExternalObject: readSemaphore.
500
- Smalltalk unregisterExternalObject: writeSemaphore.
501
- socketHandle := nil .
502
- readSemaphore := writeSemaphore := semaphore := nil .
503
- self unregister ]
504
+ socketHandle ifNotNil: [
505
+ | saveSemaphores |
506
+ self isValid ifTrue: [ self primSocketDestroy: socketHandle ].
507
+ socketHandle := nil .
508
+ Smalltalk unregisterExternalObject: semaphore.
509
+ Smalltalk unregisterExternalObject: readSemaphore.
510
+ Smalltalk unregisterExternalObject: writeSemaphore.
511
+ " Stash the semaphores and nil them before signaling to make sure
512
+ no caller gets a chance to wait on them again and block forever."
513
+ saveSemaphores := {
514
+ semaphore.
515
+ readSemaphore.
516
+ writeSemaphore }.
517
+ semaphore := readSemaphore := writeSemaphore := nil .
518
+ " A single #signal should be sufficient, as multiple processes trying to
519
+ read or write at once will result in undefined behavior anyway as their
520
+ data gets all mixed up together."
521
+ saveSemaphores do: [ :each | each signal ].
522
+ self unregister ]
504
523
]
505
524
506
525
{ #category : ' receiving' }
@@ -1334,6 +1353,7 @@ Socket >> retryIfWaitingForConnection: aBlock [
1334
1353
ifTrue: [
1335
1354
self
1336
1355
waitForConnectionFor: Socket standardTimeout
1356
+ ifClosed: nil
1337
1357
ifTimedOut: nil .
1338
1358
aBlock value ]
1339
1359
ifFalse: [ e pass ] ]
@@ -1529,15 +1549,19 @@ Socket >> setPort: port [
1529
1549
1530
1550
{ #category : ' queries' }
1531
1551
Socket >> socketError [
1532
- ^ self primSocketError: socketHandle
1552
+
1553
+ ^ socketHandle ifNotNil: [ self primSocketError: socketHandle ]
1533
1554
]
1534
1555
1535
1556
{ #category : ' queries' }
1536
1557
Socket >> socketErrorMessage [
1537
1558
1538
- ^ [ OSPlatform current getErrorMessage: self socketError ]
1539
- on: Error
1540
- do: [ ' Error code: ' , self socketError printString ]
1559
+ ^ self socketError
1560
+ ifNil: [ ' Socket destroyed, cannot retrieve error message' ]
1561
+ ifNotNil: [ :err |
1562
+ [ OSPlatform current getErrorMessage: err ]
1563
+ on: Error
1564
+ do: [ ' Error code: ' , err printString ] ]
1541
1565
]
1542
1566
1543
1567
{ #category : ' accessing' }
@@ -1569,40 +1593,57 @@ Socket >> unregister [
1569
1593
{ #category : ' waiting' }
1570
1594
Socket >> waitForAcceptFor: timeout [
1571
1595
" Wait and accept an incoming connection. Return nil if it fails"
1572
- ^ self waitForAcceptFor: timeout ifTimedOut: [nil ]
1596
+
1597
+ ^ self waitForAcceptFor: timeout ifClosed: nil ifTimedOut: nil
1573
1598
]
1574
1599
1575
1600
{ #category : ' waiting' }
1576
- Socket >> waitForAcceptFor: timeout ifTimedOut: timeoutBlock [
1601
+ Socket >> waitForAcceptFor: timeout ifClosed: closedBlock ifTimedOut: timeoutBlock [
1577
1602
" Wait and accept an incoming connection"
1578
- self waitForConnectionFor: timeout ifTimedOut: [^ timeoutBlock value].
1579
- ^ self accept
1603
+
1604
+ self
1605
+ waitForConnectionFor: timeout
1606
+ ifClosed: [ ^ closedBlock value ]
1607
+ ifTimedOut: [ ^ timeoutBlock value ].
1608
+ ^ self accept
1580
1609
]
1581
1610
1582
1611
{ #category : ' waiting' }
1583
1612
Socket >> waitForConnectionFor: timeout [
1584
1613
" Wait up until the given deadline for a connection to be established. Return true if it is established by the deadline, false if not."
1585
1614
1586
- ^ self
1587
- waitForConnectionFor: timeout
1588
- ifTimedOut: [ConnectionTimedOut signal : ' Failed to connect in ' , timeout asString, ' seconds' ]
1615
+ ^ self
1616
+ waitForConnectionFor: timeout
1617
+ ifClosed: [
1618
+ ConnectionClosed signal : (socketHandle
1619
+ ifNil: [ ' Socket destroyed while connecting' ]
1620
+ ifNotNil: [
1621
+ ' Connection aborted or failed: ' , self socketErrorMessage ]) ]
1622
+ ifTimedOut: [
1623
+ ConnectionTimedOut signal :
1624
+ ' Failed to connect in ' , timeout asString , ' seconds' ]
1589
1625
]
1590
1626
1591
1627
{ #category : ' waiting' }
1592
- Socket >> waitForConnectionFor: timeout ifTimedOut: timeoutBlock [
1593
- " Wait up until the given deadline for a connection to be established. Return true if it is established by the deadline, false if not."
1628
+ Socket >> waitForConnectionFor: timeout ifClosed: closedBlock ifTimedOut: timeoutBlock [
1629
+ " Wait up until the given deadline for a connection to be established.
1630
+ Evaluate closedBlock if the connection is closed locally,
1631
+ or timeoutBlock if the deadline expires.
1632
+
1633
+ We should separately detect the case of a connection being refused here as well."
1594
1634
1595
- | startTime msecsDelta msecsEllapsed status |
1635
+ | startTime msecsDelta msecsElapsed status |
1596
1636
startTime := Time millisecondClockValue.
1597
1637
msecsDelta := (timeout * 1000 ) truncated.
1638
+
1639
+ [
1598
1640
status := self primSocketConnectionStatus: socketHandle.
1599
- [(status = WaitingForConnection ) and : [(msecsEllapsed := Time millisecondsSince: startTime) < msecsDelta]]
1600
- whileTrue: [
1601
- semaphore waitTimeoutMilliseconds: msecsDelta - msecsEllapsed.
1602
- status := self primSocketConnectionStatus: socketHandle].
1641
+ status == WaitingForConnection and : [
1642
+ (msecsElapsed := Time millisecondsSince: startTime) < msecsDelta ] ]
1643
+ whileTrue: [ semaphore waitTimeoutMilliseconds: msecsDelta - msecsElapsed ].
1603
1644
1604
- status = Connected ifFalse : [^ timeoutBlock value].
1605
- ^ true
1645
+ status == WaitingForConnection ifTrue : [ ^ timeoutBlock value ].
1646
+ status == Connected ifFalse: [ ^ closedBlock value ]
1606
1647
]
1607
1648
1608
1649
{ #category : ' waiting' }
@@ -1628,7 +1669,9 @@ Socket >> waitForDataFor: timeout [
1628
1669
1629
1670
{ #category : ' waiting' }
1630
1671
Socket >> waitForDataFor: timeout ifClosed: closedBlock ifTimedOut: timedOutBlock [
1631
- " Wait for the given nr of seconds for data to arrive."
1672
+ " Wait for the given nr of seconds for data to arrive.
1673
+ If it does not, execute <timedOutBlock>. If the connection
1674
+ is closed before any data arrives, execute <closedBlock>."
1632
1675
1633
1676
| startTime msecsDelta msecsElapsed |
1634
1677
startTime := Time millisecondClockValue.
@@ -1637,21 +1680,17 @@ Socket >> waitForDataFor: timeout ifClosed: closedBlock ifTimedOut: timedOutBloc
1637
1680
self isConnected ifFalse: [ ^ closedBlock value ].
1638
1681
(msecsElapsed := Time millisecondsSince: startTime) < msecsDelta
1639
1682
ifFalse: [ ^ timedOutBlock value ].
1640
- self readSemaphore waitTimeoutMilliseconds: msecsDelta - msecsElapsed ]
1683
+ readSemaphore waitTimeoutMilliseconds: msecsDelta - msecsElapsed ]
1641
1684
]
1642
1685
1643
1686
{ #category : ' waiting' }
1644
1687
Socket >> waitForDataIfClosed: closedBlock [
1645
1688
" Wait indefinitely for data to arrive. This method will block until
1646
1689
data is available or the socket is closed."
1647
1690
1648
- [true ]
1649
- whileTrue: [
1650
- self dataAvailable
1651
- ifTrue: [^ self ].
1652
- self isConnected
1653
- ifFalse: [^ closedBlock value].
1654
- self readSemaphore wait]
1691
+ [ self dataAvailable ] whileFalse: [
1692
+ self isConnected ifFalse: [ ^ closedBlock value ].
1693
+ readSemaphore wait ]
1655
1694
]
1656
1695
1657
1696
{ #category : ' waiting' }
@@ -1662,22 +1701,23 @@ Socket >> waitForDisconnectionFor: timeout [
1662
1701
(e.g., because he has called 'close' to send a close request to the other end)
1663
1702
before calling this method."
1664
1703
1665
- | startTime msecsDelta status |
1704
+ | startTime msecsDelta msecsElapsed status |
1666
1705
startTime := Time millisecondClockValue.
1667
1706
msecsDelta := (timeout * 1000 ) truncated.
1707
+
1708
+ [
1668
1709
status := self primSocketConnectionStatus: socketHandle.
1669
- [((status == Connected ) or : [(status == ThisEndClosed )]) and :
1670
- [(Time millisecondsSince: startTime) < msecsDelta]] whileTrue: [
1671
- self discardReceivedData.
1672
- self readSemaphore waitTimeoutMilliseconds:
1673
- (msecsDelta - (Time millisecondsSince: startTime) max: 0 ).
1674
- status := self primSocketConnectionStatus: socketHandle].
1710
+ (status == Connected or : [ status == ThisEndClosed ]) and : [
1711
+ (msecsElapsed := Time millisecondsSince: startTime) < msecsDelta ] ]
1712
+ whileTrue: [
1713
+ self discardReceivedData.
1714
+ readSemaphore waitTimeoutMilliseconds: msecsDelta - msecsElapsed ].
1675
1715
^ status ~= Connected
1676
1716
]
1677
1717
1678
1718
{ #category : ' waiting' }
1679
1719
Socket >> waitForSendDoneFor: timeout [
1680
- " Wait up until the given deadline for the current send operation to complete.
1720
+ " Wait up until the given deadline for the current send operation to complete.
1681
1721
Raise an exception if the timeout expires or the connection is closed before sending finishes."
1682
1722
1683
1723
^ self
@@ -1691,18 +1731,21 @@ Socket >> waitForSendDoneFor: timeout ifClosed: closedBlock ifTimedOut: timedOut
1691
1731
" Wait up until the given deadline for the current send operation to complete.
1692
1732
If it does not, execute <timedOutBlock>. If the connection is closed before
1693
1733
the send completes, execute <closedBlock>."
1694
- | startTime msecsDelta msecsElapsed sendDone |
1695
-
1734
+
1735
+ | startTime msecsDelta msecsElapsed |
1696
1736
startTime := Time millisecondClockValue.
1697
1737
msecsDelta := (timeout * 1000 ) truncated.
1698
1738
" Connection end and final data can happen fast, so test in this order"
1699
- [ sendDone := self sendDone ] whileFalse: [
1739
+ [ self sendDone ] whileFalse: [
1700
1740
self isConnected ifFalse: [ ^ closedBlock value ].
1701
1741
(msecsElapsed := Time millisecondsSince: startTime) < msecsDelta
1702
1742
ifFalse: [ ^ timedOutBlock value ].
1703
- self writeSemaphore waitTimeoutMilliseconds: msecsDelta - msecsElapsed ].
1704
-
1705
- ^ sendDone
1743
+ writeSemaphore waitTimeoutMilliseconds: msecsDelta - msecsElapsed ].
1744
+
1745
+ " For backward compatibility with Pharo <= 11, return a boolean indicating
1746
+ whether the send is completed. The loop will only terminate when this
1747
+ is the case, so simply return true."
1748
+ ^ true
1706
1749
]
1707
1750
1708
1751
{ #category : ' accessing' }
0 commit comments