## microthread-preemptive-scheduler.api
# Compiled by:
#
src/lib/std/standard.lib# This api is implemented in:
#
#
src/lib/src/lib/thread-kit/src/core-thread-kit/microthread-preemptive-scheduler.pkgstipulate
package fat = fate; # fate is from
src/lib/std/src/nj/fate.pkg package hth = hostthread; # hostthread is from
src/lib/std/src/hostthread.pkg package itt = internal_threadkit_types; # internal_threadkit_types is from
src/lib/src/lib/thread-kit/src/core-thread-kit/internal-threadkit-types.pkg package rwq = rw_queue; # rw_queue is from
src/lib/src/rw-queue.pkg package tim = time; # time is from
src/lib/std/time.pkg package wnx = winix__premicrothread; # winix__premicrothread is from
src/lib/std/winix--premicrothread.pkg #
Fate(X) = fat::Fate(X);
Microthread = itt::Microthread;
herein
api Microthread_Preemptive_Scheduler {
#
foreground_run_queue: rwq::Rw_Queue( (Microthread, Fate( Void)) ); # Referenced in
src/lib/src/lib/thread-kit/src/core-thread-kit/mailop.pkg background_run_queue: rwq::Rw_Queue( (Microthread, Fate( Void)) ); # and
src/lib/src/lib/thread-kit/src/glue/threadkit-base-for-os-g.pkg set_condvar__iu: itt::Condition_Variable -> Void;
get_current_microthread: Void -> Microthread;
set_current_microthread: Microthread -> Void;
push_into_run_queue: (Microthread, Fate(Void)) -> Void; # Run fate under thread when we get a chance.
enqueue_old_thread_plus_old_fate_then_install_new_thread # Push (get_current_microthread(), old_fate) onto run queue, then do set_current_microthread(new_thread) and return.
: # Fate of new_thread is whatever caller does upon our return.
{ new_thread: Microthread,
old_fate: Fate(Void)
}
->
Void;
#
# Enqueue the given fate with the
# current thread ID, and make the
# given thread ID be the current one.
# Nomenclature: What I'm calling "uninterruptible mode" is usually called "critical section" or "atomic region"
# in the literature. I dislike "critical" because it is vague. ("critical" in what sense? Who knows?)
# "atomic" is literally correct ("a-tomic" == "not cuttable" -- indivisible) but the modern reader is not
# likely to take it in that sense at first blush. And neither "section" nor "region" are as apropos as "scope".
# (If we were going to use the term a lot I might favor "unit scope", but I do not expect we will.)
assert_not_in_uninterruptible_scope: String -> Void;
enter_uninterruptible_scope: Void -> Void;
exit_uninterruptible_scope: Void -> Void;
#
# Enter/leave a critical section.
# These do NOT nest!
dispatch_next_thread__xu__noreturn: Void -> X; # NEVER RETURNS TO CALLER.
#
# Leave the critical section
# and dispatch the next thread.
dispatch_next_thread__noreturn: Void -> X; # NEVER RETURNS TO CALLER.
#
# Dispatch the next thread.
#
# This should NOT be called
# while in a critical section.
# In a critical section use:
# dispatch_next_thread__xu__noreturn ();
switch_to_thread__xu: (Microthread, Fate(X), X) -> Void;
#
# Switch to the given thread
# while leaving a critical section.
yield_to_next_thread__xu: Fate(Void) -> X;
#
# Yield control to the next thread
# while leaving the critical section.
run_next_runnable_thread__xu__hook: Ref( Fate( Void ) );
#
# This hook points to a fate that
# gets dispatched when a preemption
# is received or when a thread exits
# a critical section and there is a
# signal pending.
no_runnable_threads_left__hook: Ref( Fate( Void ) );
#
# This hook points to a fate that gets invoked when
# when the scheduler has nothing else to do.
thread_scheduler_shutdown_hook: Ref( Fate ((Bool, wnx::process::Status)) );
#
# This hook points to a fate that
# gets invoked when the system is
# otherwise deadlocked. It is
# also invoked by run_threadkit::shutdown.
#
# It takes two arguments:
# o A boolean flag that says whether to do clean-up.
# o The exit status.
#
# This hook gets set in wrap_for_export() in
src/lib/src/lib/thread-kit/src/glue/threadkit-base-for-os-g.pkg # and start_up_thread_scheduler''() in
src/lib/src/lib/thread-kit/src/glue/thread-scheduler-control-g.pkg
get_approximate_time: Void -> tim::Time;
#
# Get an approximation of the current time of day.
#
# The value returned was obtained from the operating
# system via
# tim::get_time ();
# during the current timeslice, so it is off
# by at most the length of that timeslice.
reset_thread_scheduler: Bool -> Void;
# Control over the preemptive timer
#
start_thread_scheduler_timer: tim::Time -> Void; # Called from
src/lib/src/lib/thread-kit/src/glue/thread-scheduler-control-g.pkg # Called from
src/lib/src/lib/thread-kit/src/glue/threadkit-base-for-os-g.pkg stop_thread_scheduler_timer: Void -> Void; # Called from
src/lib/std/src/posix/winix-process.pkg # Called from
src/lib/src/lib/thread-kit/src/glue/thread-scheduler-control-g.pkg # Called from
src/lib/src/lib/thread-kit/src/glue/threadkit-base-for-os-g.pkg # Called from
src/lib/std/src/posix/spawn.pkg restart_thread_scheduler_timer: Void -> Void; # Called from
src/lib/std/src/posix/winix-process.pkg # Called from
src/lib/src/lib/thread-kit/src/glue/thread-scheduler-control-g.pkg # Called from
src/lib/src/lib/thread-kit/src/glue/threadkit-base-for-os-g.pkg # Called from
src/lib/std/src/posix/spawn.pkg block_until_inter_hostthread_request_queue_is_nonempty: Void -> Void; # This gives no_runnable_threads_left__fate in src/lib/src/lib/thread-kit/src/glue/threadkit-base-for-os-g.pkg.
# a graceful way to block until we have something to do.
Do_Echo = { what: String, # 'what' will be passed to 'reply'.
reply: String -> Void
};
echo: Do_Echo -> Void; # To be called from other hostthreads.
do: (Void -> Void) -> Void; # Execute arbitrary code in the context of the scheduler thread.
run_thunk: (Void -> Void) -> Void; # Create a temporary thread (with dummy ID)
# to run the given function and then exit.
#
# NB: The thread is placed at the BACK of the BACKGROUND run queue
# to ensure that every other thread gets a fair chance to run.
run_thunks: List( (Void -> Void) ) -> Void; # As above, submitting multiple thunks as independent threads.
# Currently implemented as just apply run_thunk thunks;
run_thunk_soon: (Void -> Void) -> Void; # Create a temporary thread (with dummy ID)
# to run the given function and then exit.
#
# NB: The thread is placed at the BACK of the FOREGROUND run queue
# for execution when its turn comes up.
run_thunk_immediately__iu: (Void -> Void) -> Void; # Schedule thunk to run "immediately", using the dedicated run_thunk_immediately_thread from
src/lib/src/lib/thread-kit/src/core-thread-kit/internal-threadkit-types.pkg # CALL ONLY FROM WITHIN AN UNINTERRUPTIBLE SCOPE! (That's what the __iu means.)
#
# NB: The thread is placed at the FRONT of the FOREGROUND run queue
# for immediate execution, instead of being placed at
# the back to wait its turn. This represents UNFAIR SCHEDULING;
# heavy use of it could lead to THREAD STARVATION; USE WITH CAUTION!
#
# This is an obscure fn currently used only by
src/lib/src/lib/thread-kit/src/process-deathwatch.pkg inter_hostthread_request_queue_is_empty: Void -> Bool; # Special kludge used by
trace_backpatchfn: Ref( (Void -> String) -> Void ); # A tracelog kludge of no general interest, used (only) in
src/lib/src/lib/thread-kit/src/lib/logger.pkg get_uninterruptible_scope_nesting_depth: Void -> Int; # A unit-test support hack of no general interest.
uninterruptible_scope_mutex: Ref(Int);
alarm_handler_calls: Ref(Int);
alarm_handler_calls_with__uninterruptible_scope_mutex__set: Ref(Int);
alarm_handler_calls_with__microthread_switch_lock__set: Ref(Int);
wake_scheduler_hostthread_if_paused: Void -> Void; # io_bound_task_hostthreads uses this to wake us from an interprocess_signals::pause() call when
# it has made input available for us to process. Without this call, the pause() will continue
# until the next 50HZ (20ms) SIGALRM signal is caught by microthread_preemptive_scheduler::alarm_handler.
# interprocess_signals is from
src/lib/std/src/nj/interprocess-signals.pkg # io_bound_task_hostthreads is from
src/lib/std/src/hostthread/io-bound-task-hostthreads.pkg# Very temporary debug hacks:
kill_count: Ref(Int);
thread_scheduler_statestring: Void -> String;
print_thread_scheduler_state: Void -> Void;
print_int: Int -> Int -> Void;
mutex: hth::Mutex;
condvar: hth::Mutex;
Request = DO_ECHO Do_Echo
| DO_THUNK (Void -> Void);
request_queue: Ref(List(Request)); # Queue of pending requests from client hostthreads.
};
end;