Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
249 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
:import "os/error" | ||
|
||
:trait TCPListenerNotify | ||
:fun ref listening (listen TCPListener'ref): None | ||
:fun ref not_listening (listen TCPListener'ref) None | ||
:fun ref closed (listen TCPListener'ref): None | ||
:fun ref connected! (listen TCPListener'ref) TCPConnectionNotify'iso | ||
|
||
:actor TCPListener | ||
:prop notify TCPListenerNotify | ||
:prop listen_error OSError: EINVAL | ||
|
||
:prop _fd U32 | ||
:prop _event CPointer(AsioEvent): CPointer(AsioEvent).null | ||
|
||
:prop _count USize: 0 | ||
:prop _limit USize | ||
:prop _read_buffer_size USize | ||
:prop _yield_after_reading USize | ||
:prop _yield_after_writing USize | ||
|
||
:prop _closed Bool: False | ||
:prop _paused Bool: False | ||
|
||
:new ( | ||
// TODO: TCPListenerAuth, rather than ambient authority. | ||
notify TCPListenerNotify'iso | ||
host String = "" | ||
service String = "0" | ||
@_limit = 0 | ||
@_read_buffer_size = 16384 | ||
@_yield_after_reading = 16384 | ||
@_yield_after_writing = 16384 | ||
) | ||
@_fd = -1 // TODO: Move default value to prop declaration | ||
@notify = --notify // TODO: is it possible to use param assign sugar for this? | ||
event = _OSSocket.listen_tcp(@, host, service) | ||
case ( | ||
| event <: CPointer(AsioEvent) | @_event = event | ||
| event <: OSError | @listen_error = event | ||
) | ||
@_fd = AsioEvent.fd(@_event) | ||
@_notify_listening | ||
|
||
:fun ref _notify_listening | ||
if @_event.is_not_null ( | ||
@notify.listening(@) | ||
| | ||
@_closed = True | ||
@notify.not_listening(@) | ||
) | ||
|
||
:: This is a special behaviour that hooks into the AsioEventNotify runtime, | ||
:: called whenever an event handle we're subscribed to receives an event. | ||
:be _event_notify (event CPointer(AsioEvent), flags U32, arg U32) | ||
if (@_event is event) ( | ||
if AsioEvent.is_readable(flags) ( | ||
@_accept(arg) | ||
) | ||
if AsioEvent.is_disposable(flags) ( | ||
AsioEvent.destroy(@_event) | ||
@_event = CPointer(AsioEvent).null | ||
) | ||
) | ||
|
||
:be _accept (ns U32 = 0) | ||
if Platform.windows ( | ||
// TODO | ||
| | ||
if @_closed.not ( | ||
try ( | ||
while (@_limit == 0 || @_count < @_limit) ( | ||
conn_fd = _OSSocket.accept(@_event) | ||
case ( | ||
| conn_fd == 0 | error! // EWOULDBLOCK, don't try again | ||
| conn_fd == -1 | None // Some other error, so we can try again | ||
| @_spawn(conn_fd) | ||
) | ||
) | ||
@_paused = True | ||
) | ||
) | ||
) | ||
|
||
:fun ref _spawn (conn_fd U32) | ||
try ( | ||
TCPConnection._accept( | ||
@ | ||
@notify.connected!(@) | ||
conn_fd | ||
@_read_buffer_size | ||
@_yield_after_reading | ||
@_yield_after_writing | ||
) | ||
@_count += 1 | ||
| | ||
_OSSocket.close(conn_fd) | ||
) | ||
|
||
:be _conn_closed | ||
@_count -= 1 | ||
|
||
// If releasing this connection takes us below the limit, | ||
// unpause acceptance and try to accept more connections. | ||
if (@_paused && @_count < @_limit) ( | ||
@_paused = False | ||
@_accept | ||
) | ||
|
||
:be dispose: @close | ||
:fun ref close | ||
if (@_closed.not && @_event.is_not_null) ( | ||
// When not on windows, unsubscribe immediately here instead of later. | ||
if Platform.windows.not AsioEvent.unsubscribe(@_event) | ||
|
||
_OSSocket.close(@_fd) | ||
@_fd = -1 | ||
|
||
@notify.closed(@) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters