use @pony_asio_event_create[AsioEventID](
owner: AsioEventNotify,
fd: U32,
flags: U32,
nsec: U64,
noisy: Bool)
use @pony_asio_event_unsubscribe[None](event: AsioEventID)
use @pony_asio_event_destroy[None](event: AsioEventID)
interface StdinNotify
"""
Notification for data arriving via stdin.
"""
fun ref apply(data: Array[U8] iso) =>
"""
Called when data is available on stdin.
"""
None
fun ref dispose() =>
"""
Called when no more data will arrive on stdin.
"""
None
interface tag DisposableActor
"""
An interface used to asynchronously dispose of an actor.
"""
be dispose()
actor Stdin
"""
Asynchronous access to stdin. The constructor is private to ensure that
access is provided only via an environment.
"""
var _notify: (StdinNotify | None) = None
var _chunk_size: USize = 32
var _event: AsioEventID = AsioEvent.none()
let _use_event: Bool
new _create(use_event: Bool) =>
"""
Create an asynchronous stdin provider.
"""
_use_event = use_event
be apply(notify: (StdinNotify iso | None), chunk_size: USize = 32) =>
"""
Set the notifier. Optionally, also sets the chunk size, dictating the
maximum number of bytes of each chunk that will be passed to the notifier.
"""
_set_notify(consume notify)
_chunk_size = chunk_size
be dispose() =>
"""
Clear the notifier in order to shut down input.
"""
_set_notify(None)
fun ref _set_notify(notify: (StdinNotify iso | None)) =>
"""
Set the notifier.
"""
if notify is None then
if _use_event and not _event.is_null() then
// Unsubscribe the event.
@pony_asio_event_unsubscribe(_event)
_event = AsioEvent.none()
end
elseif _notify is None then
if _use_event then
// Create a new event.
_event = @pony_asio_event_create(this, 0, AsioEvent.read(), 0, true)
else
// Start the read loop.
_loop_read()
end
end
try (_notify as StdinNotify).dispose() end
_notify = consume notify
be _loop_read() =>
"""
If we are able to read from stdin, schedule another read.
"""
if _read() then
_loop_read()
end
be _event_notify(event: AsioEventID, flags: U32, arg: U32) =>
"""
When the event fires, read from stdin.
"""
if AsioEvent.disposable(flags) then
@pony_asio_event_destroy(event)
elseif (_event is event) and AsioEvent.readable(flags) then
_read()
end
be _read_again() =>
"""
Resume reading.
"""
_read()
fun ref _read(): Bool =>
"""
Read a chunk of data from stdin. Read a maximum of _chunk_size bytes, send
ourself a resume message and stop reading to avoid starving other actors.
"""
try
let notify = _notify as StdinNotify
var sum: USize = 0
while true do
let chunk_size = _chunk_size
var data = recover Array[U8] .> undefined(chunk_size) end
var again: Bool = false
let len =
@pony_os_stdin_read[USize](data.cpointer(), data.space(),
addressof again)
match len
| -1 =>
// Error, possibly would block. Try again.
return true
| 0 =>
// EOF. Close everything, stop reading.
_close_event()
notify.dispose()
_notify = None
return false
end
data.truncate(len)
notify(consume data)
if not again then
// Not allowed to call pony_os_stdin_read again yet, exit loop.
return true
end
sum = sum + len
if sum > (1 << 12) then
if _use_event then
_read_again()
end
break
end
end
true
else
// No notifier. Stop reading.
_close_event()
false
end
fun ref _close_event() =>
"""
Close the event.
"""
if not _event.is_null() then
@pony_asio_event_unsubscribe(_event)
_event = AsioEvent.none()
end