# io-wait-hostthread.api
#
# Interface to a server hostthread designed to basically just
# sit in a loop doing C select() over and over on whatever
# file/pipe/socket descriptors are of current interest.
#
# Our main purpose is to offload I/O blocking from the
# main threadkit hostthread so that it can run full speed # Threadkit is from
src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.api# while we sit around waiting for network packets to arrive. # threadkit is from
src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg#
# A secondary purpose of io_wait_hostthread is to provide the
# clock threadkit needs to drive its pre-emptive timeslicing.
# Reppy's original CML implementation uses SIGALRM for this,
# but this has two critical disadvantages:
#
# o SIGALRM is a bottleneck resource which everyone wants
# to control.
#
# o Worse, SIGALRM interrupts "slow" system calls, causing
# them to return EINTR instead of a useful result, requiring
# the relevant C code to re-try. We can ensure that our own
# C support code does this, but one call -- connect() --
# *cannot* be retried, and we cannot be sure that all C
# libraries linked into the runtime will retry properly
# on EINTR. (In fact, I'd bet good odds that some will not.)
#
# By having our select() time out regularly and generate
# a timeslice clock for threadkit based on those timeouts
# we almost completely avoid both of those problems.
#
# ("Almost" because, for example, SIGCHLD can still interrupt
# "slow" syscalls. But that will usually be very very rare,
# since child process deaths are usually rare and for one
# to hit during a "slow" syscall lacking proper retry-on-EINTR
# will be doubly rare.)
#
# See also:
#
#
src/lib/std/src/hostthread/cpu-bound-task-hostthreads.api#
src/lib/std/src/hostthread/io-bound-task-hostthreads.api# Compiled by:
#
src/lib/std/standard.libstipulate
# package hth = hostthread; # hostthread is from
src/lib/std/src/hostthread.pkg package tim = time; # time is from
src/lib/std/time.pkg package wio = winix__premicrothread::io; # winix__premicrothread::io is from
src/lib/std/src/posix/winix-io--premicrothread.pkgherein
# This api is implemented in:
#
#
src/lib/std/src/hostthread/io-wait-hostthread.pkg #
api Io_Wait_Hostthread {
#
is_running: Void -> Bool; # Returns TRUE iff the server hostthread is running.
#
start_server_hostthread_if_not_running: String -> Void; # Clear the internal wait-request list and start the select() hostthread running.
# This is a no-op if it is already running.
# String arg is (only) used as human-readable caller ID when logging for debug purposes.
Do_Stop
=
{ per_who: String,
reply: Void -> Void
};
stop_server_hostthread_if_running: Do_Stop -> Void;
Do_Echo
=
{ what: String, # Primarily to test that the server is running.
reply: String -> Void
};
echo: Do_Echo -> Void;
Do_Note_Iod_Reader
=
{ io_descriptor: wio::Iod,
read_fn: wio::Iod -> Void # Call this closure ("function") on iod whenever input is available.
};
note_iod_reader: Do_Note_Iod_Reader -> Void;
drop_iod_reader: wio::Iod -> Void;
Do_Note_Iod_Writer
=
{ io_descriptor: wio::Iod,
write_fn: wio::Iod -> Void # Call this closure ("function") on iod whenever input is available.
};
note_iod_writer: Do_Note_Iod_Writer -> Void;
drop_iod_writer: wio::Iod -> Void;
Do_Note_Iod_Oobder
=
{ io_descriptor: wio::Iod,
oobd_fn: wio::Iod -> Void # Call this closure ("function") on iod whenever out-of-band-data ("oobd") is available.
};
note_iod_oobder: Do_Note_Iod_Oobder -> Void;
drop_iod_oobder: wio::Iod -> Void;
# test: String -> Void; # This is nominally temporary debug code; the String (only) identifies the caller for debug purposes.
# WE ASSUME ONLY ONE CALLER AT A TIME, SO WE DON'T WORRY ABOUT MUTUAL EXCLUSION.
get_timeout_interval: Void -> tim::Time;
set_timeout_interval: tim::Time -> Void;
#
# The main job of io-wait-hostthread.pkg is to sit
# eternally in loop running wio::wait_for_io_opportunity.
# These two calls control the timeout for that call.
# drop_per_loop_fn: (Ref (Void -> Void)) -> Void;
# note_per_loop_fn: (Ref (Void -> Void)) -> Void; # Call will be ignored if given refcell is already present in internal list.
#
# The io_wait_hostthread timeslicer uses us to generate
# its (approximately) 50Hz timeslicing "clock". It uses
#
# note_per_loop_fn
#
# to register a function with us to be called once
# per outer loop (and consequently approximately once
# per timeout interval).
#
# For generality we internally support a list of such
# per-loop fns rather than a single such fn, all of which
# get called once per outer loop.
#
# For flexibility we also support removing per-loop fns
# from our internal list via
#
# note_per_loop_fn
#
# (This might be needed if switching to an entirely
# different threadkit scheduler, say.)
#
# To do this we must be able to compare list entries
# for equality. Equality comparisons are not supported
# for Void->Void values, but they are supported for
# refcells -- this is the main reason we wrap per-loop fns
# in refcells in these two calls. (We never assign to
# these refcells, although our client -- threadkit scheduler
# -- could do so if it wished.)
#
# Note that if there is a lot of I/O going on, the
# loop fn may be called much more frequently than the
# timeout interval would suggest. It is up to the
# client to deal with this, if it is a problem.
is_doing_useful_work: Void -> Bool;
#
# This is support for
#
# no_runnable_threads_left__fate
# from
#
src/lib/src/lib/thread-kit/src/glue/threadkit-base-for-os-g.pkg #
# which is tasked with exit()ing if the system is
# deadlocked -- which is to say, no thread ready
# to run and provably no prospect of ever having
# a thread ready to run.
#
# If we are listening on any fd, then we can eventually
# wake up some thread when input arrives on that fd, so
# the system is not provably deadlocked and
# no_runnable_threads_left__fate() should not exit().
};
end;
## Code by Jeff Prothero: Copyright (c) 2010-2015,
## released per terms of SMLNJ-COPYRIGHT.