749 lines
27 KiB
Racket
749 lines
27 KiB
Racket
#lang scribble/doc
|
|
@(require "utils.ss"
|
|
(for-label scheme/tcp))
|
|
|
|
@title[#:tag "threads"]{Threads}
|
|
|
|
The initializer function @cppi{scheme_basic_env} creates the main
|
|
Scheme thread; all other threads are created through calls to
|
|
@cppi{scheme_thread}.
|
|
|
|
Information about each internal Scheme thread is kept in a
|
|
@cppi{Scheme_Thread} structure. A pointer to the current thread's
|
|
structure is available as @cppi{scheme_current_thread}. A
|
|
@cpp{Scheme_Thread} structure includes the following fields:
|
|
|
|
@itemize{
|
|
|
|
@item{@cppi{error_buf} --- the @cppi{mz_jmp_buf} value used to escape
|
|
from errors. The @cpp{error_buf} value of the current thread is
|
|
available as @cppi{scheme_error_buf}.}
|
|
|
|
@item{@cppi{cjs.jumping_to_continuation} --- a flag that
|
|
distinguishes escaping-continuation invocations from error
|
|
escapes. The @cpp{cjs.jumping_to_continuation} value of the current
|
|
thread is available as @cppi{scheme_jumping_to_continuation}.}
|
|
|
|
@item{@cppi{init_config} ---
|
|
the thread's initial parameterization. See also @secref["config"].}
|
|
|
|
@item{@cppi{cell_values} --- The thread's values for thread cells
|
|
(see also @secref["config"]).}
|
|
|
|
@item{@cppi{next} --- The next thread in the linked list of threads;
|
|
this is @cpp{NULL} for the main thread.}
|
|
|
|
}
|
|
|
|
The list of all scheduled threads is kept in a linked list;
|
|
@cppi{scheme_first_thread} points to the first thread in the list.
|
|
The last thread in the list is always the main thread.
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{Integration with Threads}
|
|
|
|
Scheme's threads can break external C code under two circumstances:
|
|
|
|
@itemize{
|
|
|
|
@item{@italic{Pointers to stack-based values can be communicated
|
|
between threads.} For example, if thread A stores a pointer to a
|
|
stack-based variable in a global variable, if thread B uses the
|
|
pointer in the global variable, it may point to data that is not
|
|
currently on the stack.}
|
|
|
|
@item{@italic{C functions that can invoke Scheme (and also be invoked
|
|
by Scheme) depend on strict function-call nesting.} For example,
|
|
suppose a function F uses an internal stack, pushing items on to the
|
|
stack on entry and popping the same items on exit. Suppose also that
|
|
F invokes Scheme to evaluate an expression. If the evaluation of
|
|
this expression invokes F again in a new thread, but then returns to
|
|
the first thread before completing the second F, then F's internal
|
|
stack will be corrupted.}
|
|
|
|
}
|
|
|
|
If either of these circumstances occurs, Scheme will probably crash.
|
|
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section[#:tag "usefuel"]{Allowing Thread Switches}
|
|
|
|
C code that performs substantial or unbounded work should occasionally
|
|
call @cppi{SCHEME_USE_FUEL}---actually a macro---which allows Scheme
|
|
to swap in another Scheme thread to run, and to check for breaks on
|
|
the current thread. In particular, if breaks are enabled, then
|
|
@cpp{SCHEME_USE_FUEL} may trigger an exception.
|
|
|
|
The macro consumes an integer argument. On most platforms, where
|
|
thread scheduling is based on timer interrupts, the argument is
|
|
ignored. On some platforms, however, the integer represents the amount
|
|
of ``fuel'' that has been consumed since the last call to
|
|
@cpp{SCHEME_USE_FUEL}. For example, the implementation of
|
|
@scheme[vector->list] consumes a unit of fuel for each created cons
|
|
cell:
|
|
|
|
@verbatim[#:indent 2]{
|
|
Scheme_Object *scheme_vector_to_list(Scheme_Object *vec)
|
|
{
|
|
int i;
|
|
Scheme_Object *pair = scheme_null;
|
|
|
|
i = SCHEME_VEC_SIZE(vec);
|
|
|
|
for (; i--; ) {
|
|
SCHEME_USE_FUEL(1);
|
|
pair = scheme_make_pair(SCHEME_VEC_ELS(vec)[i], pair);
|
|
}
|
|
|
|
return pair;
|
|
}
|
|
}
|
|
|
|
The @cpp{SCHEME_USE_FUEL} macro expands to a C block, not an
|
|
expression.
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section[#:tag "threadblock"]{Blocking the Current Thread}
|
|
|
|
Embedding or extension code sometimes needs to block, but blocking
|
|
should allow other Scheme threads to execute. To allow other threads
|
|
to run, block using @cppi{scheme_block_until}. This procedure takes
|
|
two functions: a polling function that tests whether the blocking
|
|
operation can be completed, and a prepare-to-sleep function that sets
|
|
bits in @cpp{fd_set}s when Scheme decides to sleep (because all Scheme
|
|
threads are blocked). Under Windows, an ``@cpp{fd_set}'' can also
|
|
accommodate OS-level semaphores or other handles via
|
|
@cpp{scheme_add_fd_handle}.
|
|
|
|
Since the functions passed to @cppi{scheme_block_until} are called by
|
|
the Scheme thread scheduler, they must never raise exceptions, call
|
|
@cpp{scheme_apply}, or trigger the evaluation of Scheme code in any
|
|
way. The @cpp{scheme_block_until} function itself may call the current
|
|
exception handler, however, in reaction to a break (if breaks are
|
|
enabled).
|
|
|
|
When a blocking operation is associated with an object, then the
|
|
object might make sense as an argument to @indexed-scheme[sync]. To
|
|
extend the set of objects accepted by @scheme[sync], either register
|
|
polling and sleeping functions with @cppi{scheme_add_evt}, or register
|
|
a semaphore accessor with @cppi{scheme_add_evt_through_sema}.
|
|
|
|
The @cppi{scheme_signal_received} function can be called to wake up
|
|
Scheme when it is sleeping. In particular, calling
|
|
@cppi{scheme_signal_received} ensures that Scheme will poll all
|
|
blocking synchronizations soon afterward. Furthermore,
|
|
@cpp{scheme_signal_received} can be called from any OS-level thread.
|
|
Thus, when no adequate prepare-to-sleep function can be implemented
|
|
for @cpp{scheme_block_until} in terms of file descriptors or Windows
|
|
handles, calling @cpp{scheme_signal_received} when the poll result
|
|
changes will ensure that a poll is issued.
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section[#:tag "threadtime"]{Threads in Embedded Scheme with Event Loops}
|
|
|
|
When Scheme is embedded in an application with an event-based model
|
|
(i.e., the execution of Scheme code in the main thread is repeatedly
|
|
triggered by external events until the application exits) special
|
|
hooks must be set to ensure that non-main threads execute
|
|
correctly. For example, during the execution in the main thread, a new
|
|
thread may be created; the new thread may still be running when the
|
|
main thread returns to the event loop, and it may be arbitrarily long
|
|
before the main thread continues from the event loop. Under such
|
|
circumstances, the embedding program must explicitly allow Scheme to
|
|
execute the non-main threads; this can be done by periodically calling
|
|
the function @cppi{scheme_check_threads}.
|
|
|
|
Thread-checking only needs to be performed when non-main threads exist
|
|
(or when there are active callback triggers). The embedding
|
|
application can set the global function pointer
|
|
@cppi{scheme_notify_multithread} to a function that takes an integer
|
|
parameter and returns @cpp{void}. This function is be called with 1
|
|
when thread-checking becomes necessary, and then with 0 when thread
|
|
checking is no longer necessary. An embedding program can use this
|
|
information to prevent unnecessary @cpp{scheme_check_threads} polling.
|
|
|
|
The below code illustrates how MrEd formerly set up
|
|
@cpp{scheme_check_threads} polling using the wxWindows @cpp{wxTimer}
|
|
class. (Any regular event-loop-based callback is appropriate.) The
|
|
@cpp{scheme_notify_multithread} pointer is set to
|
|
@cpp{MrEdInstallThreadTimer}. (MrEd no longer work this way, however.)
|
|
|
|
@verbatim[#:indent 2]{
|
|
class MrEdThreadTimer : public wxTimer
|
|
{
|
|
public:
|
|
void Notify(void); /* callback when timer expires */
|
|
};
|
|
|
|
static int threads_go;
|
|
static MrEdThreadTimer *theThreadTimer;
|
|
#define THREAD_WAIT_TIME 40
|
|
|
|
void MrEdThreadTimer::Notify()
|
|
{
|
|
if (threads_go)
|
|
Start(THREAD_WAIT_TIME, TRUE);
|
|
|
|
scheme_check_threads();
|
|
}
|
|
|
|
static void MrEdInstallThreadTimer(int on)
|
|
{
|
|
if (!theThreadTimer)
|
|
theThreadTimer = new MrEdThreadTimer;
|
|
|
|
if (on)
|
|
theThreadTimer->Start(THREAD_WAIT_TIME, TRUE);
|
|
else
|
|
theThreadTimer->Stop();
|
|
|
|
threads_go = on;
|
|
if (on)
|
|
do_this_time = 1;
|
|
}
|
|
}
|
|
|
|
An alternate architecture, which MrEd now uses, is to send the main
|
|
thread into a loop, which blocks until an event is ready to handle.
|
|
Scheme automatically takes care of running all threads, and it does so
|
|
efficiently because the main thread blocks on a file descriptor, as
|
|
explained in @secref["threadblock"].
|
|
|
|
@subsection[#:tag "blockednonmainel"]{Callbacks for Blocked Threads}
|
|
|
|
Scheme threads are sometimes blocked on file descriptors, such as an
|
|
input file or the X event socket. Blocked non-main threads do not
|
|
block the main thread, and therefore do not affect the event loop, so
|
|
@cppi{scheme_check_threads} is sufficient to implement this case
|
|
correctly. However, it is wasteful to poll these descriptors with
|
|
@cpp{scheme_check_threads} when nothing else is happening in the
|
|
application and when a lower-level poll on the file descriptors can be
|
|
installed. If the global function pointer
|
|
@cppi{scheme_wakeup_on_input} is set, then this case is handled more
|
|
efficiently by turning off thread checking and issuing a ``wakeup''
|
|
request on the blocking file descriptors through
|
|
@cpp{scheme_wakeup_on_input}.
|
|
|
|
A @cpp{scheme_wakeup_on_input} procedure takes a pointer to an array
|
|
of three @cpp{fd_set}s (sortof\footnote{To ensure maximum portability,
|
|
use @cpp{MZ_FD_XXX} instead of @cpp{FD_XXX}.}) and returns
|
|
@cpp{void}. The @cpp{scheme_wakeup_on_input} does not sleep; it just
|
|
sets up callbacks on the specified file descriptors. When input is
|
|
ready on any of those file descriptors, the callbacks are removed and
|
|
@cpp{scheme_wake_up} is called.
|
|
|
|
For example, the X Windows version of MrEd formerly set
|
|
@cpp{scheme_wakeup_on_input} to this @cpp{MrEdNeedWakeup}:
|
|
|
|
@verbatim[#:indent 2]{
|
|
static XtInputId *scheme_cb_ids = NULL;
|
|
static int num_cbs;
|
|
|
|
static void MrEdNeedWakeup(void *fds)
|
|
{
|
|
int limit, count, i, p;
|
|
fd_set *rd, *wr, *ex;
|
|
|
|
rd = (fd_set *)fds;
|
|
wr = ((fd_set *)fds) + 1;
|
|
ex = ((fd_set *)fds) + 2;
|
|
|
|
limit = getdtablesize();
|
|
|
|
/* See if we need to do any work, really: */
|
|
count = 0;
|
|
for (i = 0; i < limit; i++) {
|
|
if (MZ_FD_ISSET(i, rd))
|
|
count++;
|
|
if (MZ_FD_ISSET(i, wr))
|
|
count++;
|
|
if (MZ_FD_ISSET(i, ex))
|
|
count++;
|
|
}
|
|
|
|
if (!count)
|
|
return;
|
|
|
|
/* Remove old callbacks: */
|
|
if (scheme_cb_ids)
|
|
for (i = 0; i < num_cbs; i++)
|
|
notify_set_input_func((Notify_client)NULL, (Notify_func)NULL,
|
|
scheme_cb_ids[i]);
|
|
|
|
num_cbs = count;
|
|
scheme_cb_ids = new int[num_cbs];
|
|
|
|
/* Install callbacks */
|
|
p = 0;
|
|
for (i = 0; i < limit; i++) {
|
|
if (MZ_FD_ISSET(i, rd))
|
|
scheme_cb_ids[p++] = XtAppAddInput(wxAPP_CONTEXT, i,
|
|
(XtPointer *)XtInputReadMask,
|
|
(XtInputCallbackProc)MrEdWakeUp, NULL);
|
|
if (MZ_FD_ISSET(i, wr))
|
|
scheme_cb_ids[p++] = XtAppAddInput(wxAPP_CONTEXT, i,
|
|
(XtPointer *)XtInputWriteMask,
|
|
(XtInputCallbackProc)MrEdWakeUp, NULL);
|
|
if (MZ_FD_ISSET(i, ex))
|
|
scheme_cb_ids[p++] = XtAppAddInput(wxAPP_CONTEXT, i,
|
|
(XtPointer *)XtInputExceptMask,
|
|
(XtInputCallbackProc)MrEdWakeUp,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
/* callback function when input/exception is detected: */
|
|
Bool MrEdWakeUp(XtPointer, int *, XtInputId *)
|
|
{
|
|
int i;
|
|
|
|
if (scheme_cb_ids) {
|
|
/* Remove all callbacks: */
|
|
for (i = 0; i < num_cbs; i++)
|
|
XtRemoveInput(scheme_cb_ids[i]);
|
|
|
|
scheme_cb_ids = NULL;
|
|
|
|
/* ``wake up'' */
|
|
scheme_wake_up();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section[#:tag "sleeping"]{Sleeping by Embedded Scheme}
|
|
|
|
When all Scheme threads are blocked, Scheme must ``sleep'' for a
|
|
certain number of seconds or until external input appears on some file
|
|
descriptor. Generally, sleeping should block the main event loop of
|
|
the entire application. However, the way in which sleeping is
|
|
performed may depend on the embedding application. The global function
|
|
pointer @cppi{scheme_sleep} can be set by an embedding application to
|
|
implement a blocking sleep, although Scheme implements this function
|
|
for you.
|
|
|
|
A @cpp{scheme_sleep} function takes two arguments: a @cpp{float} and a
|
|
@cpp{void*}. The latter is really points to an array of three
|
|
``@cpp{fd_set}'' records (one for read, one for write, and one for
|
|
exceptions); these records are described further below. If the
|
|
@cpp{float} argument is non-zero, then the @cpp{scheme_sleep} function
|
|
blocks for the specified number of seconds, at most. The
|
|
@cpp{scheme_sleep} function should block until there is input one of
|
|
the file descriptors specified in the ``@cpp{fd_set},'' indefinitely
|
|
if the @cpp{float} argument is zero.
|
|
|
|
The second argument to @cpp{scheme_sleep} is conceptually an array of
|
|
three @cpp{fd_set} records, but always use @cpp{scheme_get_fdset} to
|
|
get anything other than the zeroth element of this array, and
|
|
manipulate each ``@cpp{fd_set}'' with @cpp{MZ_FD_XXX} instead of
|
|
@cpp{FD_XXX}.
|
|
|
|
The following function @cpp{mzsleep} is an appropriate
|
|
@cpp{scheme_sleep} function for most any Unix or Windows application.
|
|
(This is approximately the built-in sleep used by Scheme.)
|
|
|
|
@verbatim[#:indent 2]{
|
|
void mzsleep(float v, void *fds)
|
|
{
|
|
if (v) {
|
|
sleep(v);
|
|
} else {
|
|
int limit;
|
|
fd_set *rd, *wr, *ex;
|
|
|
|
# ifdef WIN32
|
|
limit = 0;
|
|
# else
|
|
limit = getdtablesize();
|
|
# endif
|
|
|
|
rd = (fd_set *)fds;
|
|
wr = (fd_set *)scheme_get_fdset(fds, 1);
|
|
ex = (fd_set *)scheme_get_fdset(fds, 2);
|
|
|
|
select(limit, rd, wr, ex, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{Thread Functions}
|
|
|
|
@function[(Scheme_Object* scheme_thread
|
|
[Scheme_Object* thunk])]{
|
|
|
|
Creates a new thread, just like @scheme[thread].}
|
|
|
|
@function[(Scheme_Object* scheme_thread_w_details
|
|
[Scheme_Object* thunk]
|
|
[Scheme_Config* config]
|
|
[Scheme_Thread_Cell_Table* cells]
|
|
[Scheme_Custodian* cust]
|
|
[int suspend_to_kill])]{
|
|
|
|
Like @cpp{scheme_thread}, except that the created thread belongs to
|
|
@var{cust} instead of the current custodian, it uses the given
|
|
@var{config} for its initial configuration, it uses @var{cells} for
|
|
its thread-cell table, and if @var{suspend_to_kill} is non-zero, then
|
|
the thread is merely suspended when it would otherwise be killed
|
|
(through either @scheme[kill-thread] or
|
|
@scheme[custodian-shutdown-all]).
|
|
|
|
The @var{config} argument is typically obtained through
|
|
@cpp{scheme_current_config} or @cpp{scheme_extend_config}. A
|
|
@var{config} is immutable, so different threads can safely use the
|
|
same value. The @var{cells} argument should be obtained from
|
|
@cpp{scheme_inherit_cells}; it is mutable, and a particular cell table
|
|
should be used by only one thread.}
|
|
|
|
@function[(Scheme_Object* scheme_make_sema
|
|
[long v])]{
|
|
|
|
Creates a new semaphore.}
|
|
|
|
@function[(void scheme_post_sema
|
|
[Scheme_Object* sema])]{
|
|
|
|
Posts to @var{sema}.}
|
|
|
|
@function[(int scheme_wait_sema
|
|
[Scheme_Object* sema]
|
|
[int try])]{
|
|
|
|
Waits on @var{sema}. If @var{try} is not 0, the wait can fail and 0 is
|
|
returned for failure, otherwise 1 is returned.}
|
|
|
|
@function[(void scheme_thread_block
|
|
[float sleep_time])]{
|
|
|
|
Allows the current thread to be swapped out in favor of other
|
|
threads. If @var{sleep_time} positive, then the current thread will
|
|
sleep for at least @var{sleep_time} seconds.
|
|
|
|
After calling this function, a program should almost always call
|
|
@cppi{scheme_making_progress} next. The exception is when
|
|
@cpp{scheme_thread_block} is called in a polling loop that performs no
|
|
work that affects the progress of other threads. In that case,
|
|
@cpp{scheme_making_progress} should be called immediately after
|
|
exiting the loop.
|
|
|
|
See also @cpp{scheme_block_until}, and see also the
|
|
@cpp{SCHEME_USE_FUEL} macro in @secref["usefuel"].}
|
|
|
|
@function[(void scheme_thread_block_enable_break
|
|
[float sleep_time]
|
|
[int break_on])]{
|
|
|
|
Like @cpp{scheme_thread_block}, but breaks are enabled while blocking if
|
|
@var{break_on} is true.}
|
|
|
|
@function[(void scheme_swap_thread
|
|
[Scheme_Thread* thread])]{
|
|
|
|
Swaps out the current thread in favor of @var{thread}.}
|
|
|
|
@function[(void scheme_break_thread
|
|
[Scheme_Thread* thread])]{
|
|
|
|
Sends a break signal to the given thread.}
|
|
|
|
@function[(int scheme_break_waiting
|
|
[Scheme_Thread* thread])]{
|
|
|
|
Returns @cpp{1} if a break from @scheme[break-thread] or @cpp{scheme_break_thread}
|
|
has occurred in the specified thread but has not yet been handled.}
|
|
|
|
@function[(int scheme_block_until
|
|
[Scheme_Ready_Fun f]
|
|
[Scheme_Needs_Wakeup_Fun fdf]
|
|
[Scheme_Object* data]
|
|
[float sleep])]{
|
|
|
|
The @cpp{Scheme_Ready_Fun} and @cpp{Scheme_Needs_Wakeup_Fun}
|
|
types are defined as follows:
|
|
|
|
@verbatim[#:indent 2]{
|
|
typedef int (*Scheme_Ready_Fun)(Scheme_Object *data);
|
|
typedef void (*Scheme_Needs_Wakeup_Fun)(Scheme_Object *data,
|
|
void *fds);
|
|
}
|
|
|
|
Blocks the current thread until @var{f} with @var{data} returns a true
|
|
value. The @var{f} function is called periodically---at least once
|
|
per potential swap-in of the blocked thread---and it may be called
|
|
multiple times even after it returns a true value. If @var{f}
|
|
with @var{data} ever returns a true value, it must continue to return
|
|
a true value until @cpp{scheme_block_until} returns. The argument
|
|
to @var{f} is the same @var{data} as provided
|
|
to @cpp{scheme_block_until}, and @var{data} is ignored
|
|
otherwise. (The @var{data} argument is not actually required to be
|
|
a @cpp{Scheme_Object*} value, because it is only used by @var{f}
|
|
and @var{fdf}.)
|
|
|
|
If Scheme decides to sleep, then the @var{fdf} function is called to
|
|
sets bits in @var{fds}, conceptually an array of three
|
|
@cpp{fd_set}s: one or reading, one for writing, and one for
|
|
exceptions. Use @cpp{scheme_get_fdset} to get elements of this
|
|
array, and manipulate an ``@cpp{fd_set}'' with @cpp{MZ_FD_XXX}
|
|
instead of @cpp{FD_XXX}. Under Windows, an ``@cpp{fd_set}'' can
|
|
also accommodate OS-level semaphores or other handles via
|
|
@cpp{scheme_add_fd_handle}.
|
|
|
|
The @var{fdf} argument can be @cpp{NULL}, which implies that the thread
|
|
becomes unblocked (i.e., @var{ready} changes its result to true) only
|
|
through Scheme actions, and never through external processes (e.g.,
|
|
through a socket or OS-level semaphore)---with the exception that
|
|
@cpp{scheme_signal_received} may be called to indicate an external
|
|
change.
|
|
|
|
If @var{sleep} is a positive number, then @cpp{scheme_block_until}
|
|
polls @var{f} at least every @var{sleep} seconds, but
|
|
@cpp{scheme_block_until} does not return until @var{f} returns a
|
|
true value. The call to @cpp{scheme_block_until} can return before
|
|
@var{sleep} seconds if @var{f} returns a true value.
|
|
|
|
The return value from @cpp{scheme_block_until} is the return value
|
|
of its most recent call to @var{f}, which enables @var{f} to return
|
|
some information to the @cpp{scheme_block_until} caller.
|
|
|
|
See @secref["threadblock"] for information about restrictions on the
|
|
@var{f} and @var{fdf} functions.}
|
|
|
|
@function[(int scheme_block_until_enable_break
|
|
[Scheme_Ready_Fun f]
|
|
[Scheme_Needs_Wakeup_Fun fdf]
|
|
[Scheme_Object* data]
|
|
[float sleep]
|
|
[int break_on])]{
|
|
|
|
Like @cpp{scheme_block_until}, but breaks are enabled while blocking
|
|
if @var{break_on} is true.}
|
|
|
|
@function[(int scheme_block_until_unless
|
|
[Scheme_Ready_Fun f]
|
|
[Scheme_Needs_Wakeup_Fun fdf]
|
|
[Scheme_Object* data]
|
|
[float sleep]
|
|
[Scheme_Object* unless_evt]
|
|
[int break_on])]{
|
|
|
|
Like @cpp{scheme_block_until_enable_break}, but the function
|
|
returns if @var{unless_evt} becomes ready, where @var{unless_evt}
|
|
is a port progress event implemented by
|
|
@cpp{scheme_progress_evt_via_get}. See
|
|
@cpp{scheme_make_input_port} for more information.}
|
|
|
|
|
|
@function[(void scheme_signal_received)]{
|
|
|
|
Indicates that an external event may have caused the result of a
|
|
synchronization poll to have a different result. Unlike most other
|
|
Scheme functions, this one can be called from any OS-level thread, and
|
|
it wakes up if the Scheme thread if it is sleeping.}
|
|
|
|
@function[(void scheme_check_threads)]{
|
|
|
|
This function is periodically called by the embedding program to give
|
|
background processes time to execute. See @secref["threadtime"]
|
|
for more information.}
|
|
|
|
@function[(void scheme_wake_up)]{
|
|
|
|
This function is called by the embedding program
|
|
when there is input on an external file descriptor. See
|
|
@secref["sleeping"] for more information.}
|
|
|
|
@function[(void* scheme_get_fdset
|
|
[void* fds]
|
|
[int pos])]{
|
|
|
|
Extracts an ``@cpp{fd_set}'' from an array passed to
|
|
@cpp{scheme_sleep}, a callback for @cpp{scheme_block_until}, or an
|
|
input port callback for @cpp{scheme_make_input_port}.}
|
|
|
|
@function[(void scheme_add_fd_handle
|
|
[void* h]
|
|
[void* fds]
|
|
[int repost])]{
|
|
|
|
Adds an OS-level semaphore (Windows) or other waitable handle
|
|
(Windows) to the ``@cpp{fd_set}'' @var{fds}. When Scheme performs
|
|
a ``@cpp{select}'' to sleep on @var{fds}, it also waits on the given
|
|
semaphore or handle. This feature makes it possible for Scheme to
|
|
sleep until it is awakened by an external process.
|
|
|
|
Scheme does not attempt to deallocate the given semaphore or handle,
|
|
and the ``@cpp{select}'' call using @var{fds} may be unblocked due to
|
|
some other file descriptor or handle in @var{fds}. If @var{repost} is
|
|
a true value, then @var{h} must be an OS-level semaphore, and if the
|
|
``@cpp{select}'' unblocks due to a post on @var{h}, then @var{h} is
|
|
reposted; this allows clients to treat @var{fds}-installed semaphores
|
|
uniformly, whether or not a post on the semaphore was consumed by
|
|
``@cpp{select}''.
|
|
|
|
The @cpp{scheme_add_fd_handle} function is useful for implementing
|
|
the second procedure passed to @cpp{scheme_wait_until}, or for
|
|
implementing a custom input port.
|
|
|
|
Under Unix and Mac OS X, this function has no effect.}
|
|
|
|
|
|
@function[(void scheme_add_fd_eventmask
|
|
[void* fds]
|
|
[int mask])]{
|
|
|
|
Adds an OS-level event type (Windows) to the set of types in the
|
|
``@cpp{fd_set}'' @var{fds}. When Scheme performs a
|
|
``@cpp{select}'' to sleep on @var{fds}, it also waits on events of
|
|
them specified type. This feature makes it possible for Scheme to
|
|
sleep until it is awakened by an external process.
|
|
|
|
The event mask is only used when some handle is installed with
|
|
@cpp{scheme_add_fd_handle}. This awkward restriction may force you
|
|
to create a dummy semaphore that is never posted.
|
|
|
|
Under Unix, and Mac OS X, this function has no effect.}
|
|
|
|
@function[(void scheme_add_evt
|
|
[Scheme_Type type]
|
|
[Scheme_Ready_Fun ready]
|
|
[Scheme_Needs_Wakeup_Fun wakeup]
|
|
[Scheme_Wait_Filter_Fun filter]
|
|
[int can_redirect])]{
|
|
|
|
The argument types are defined as follows:
|
|
|
|
@verbatim[#:indent 2]{
|
|
typedef int (*Scheme_Ready_Fun)(Scheme_Object *data);
|
|
typedef void (*Scheme_Needs_Wakeup_Fun)(Scheme_Object *data,
|
|
void *fds);
|
|
typedef int (*Scheme_Wait_Filter_Fun)(Scheme_Object *data);
|
|
}
|
|
|
|
Extends the set of waitable objects for @scheme[sync]
|
|
to those with the type tag @var{type}. If @var{filter} is
|
|
non-@cpp{NULL}, it constrains the new waitable set to those objects
|
|
for which @var{filter} returns a non-zero value.
|
|
|
|
The @var{ready} and @var{wakeup} functions are used in the same way
|
|
was the arguments to @cpp{scheme_block_until}.
|
|
|
|
The @var{can_redirect} argument should be @cpp{0}.}
|
|
|
|
@function[(void scheme_add_evt_through_sema
|
|
[Scheme_Type type]
|
|
[Scheme_Wait_Sema_Fun getsema]
|
|
[Scheme_Wait_Filter_Fun filter])]{
|
|
|
|
Like @cpp{scheme_add_evt}, but for objects where waiting is based
|
|
on a semaphore. Instead of @var{ready} and @var{wakeup} functions,
|
|
the @var{getsema} function extracts a semaphore for a given object:
|
|
|
|
@verbatim[#:indent 2]{
|
|
typedef
|
|
Scheme_Object *(*Scheme_Wait_Sema_Fun)(Scheme_Object *data,
|
|
int *repost);
|
|
}
|
|
|
|
If a successful wait should leave the semaphore waited, then
|
|
@var{getsema} should set @var{*repost} to @cpp{0}. Otherwise, the
|
|
given semaphore will be re-posted after a successful wait. A
|
|
@var{getsema} function should almost always set @var{*repost} to
|
|
@cpp{1}.}
|
|
|
|
|
|
@function[(void scheme_making_progress)]{
|
|
|
|
Notifies the scheduler that the current thread is not simply calling
|
|
@cppi{scheme_thread_block} in a loop, but that it is actually
|
|
making progress.}
|
|
|
|
@function[(int scheme_tls_allocate)]{
|
|
|
|
Allocates a thread local storage index to be used with
|
|
@cpp{scheme_tls_set} and @cpp{scheme_tls_get}.}
|
|
|
|
@function[(void scheme_tls_set
|
|
[int index]
|
|
[void* v])]{
|
|
|
|
Stores a thread-specific value using an index allocated with
|
|
@cpp{scheme_tls_allocate}.}
|
|
|
|
@function[(void* scheme_tls_get
|
|
[int index])]{
|
|
|
|
Retrieves a thread-specific value installed with @cpp{scheme_tls_set}.
|
|
If no thread-specific value is available for the given index, @cpp{NULL} is
|
|
returned.}
|
|
|
|
@function[(Scheme_Object* scheme_call_enable_break
|
|
[Scheme_Prim* prim]
|
|
[int argc]
|
|
[Scheme_Object** argv])]{
|
|
|
|
Calls @var{prim} with the given @var{argc} and @var{argv} with breaks
|
|
enabled. The @var{prim} function can block, in which case it might be
|
|
interrupted by a break. The @var{prim} function should not block,
|
|
yield, or check for breaks after it succeeds, where ``succeeds''
|
|
depends on the operation. For example,
|
|
@scheme[tcp-accept/enable-break] is implemented by wrapping this
|
|
function around the implementation of @scheme[tcp-accept]; the
|
|
@scheme[tcp-accept] implementation does not block or yield after it
|
|
accepts a connection.}
|
|
|
|
@function[(Scheme_Object* scheme_make_thread_cell
|
|
[Scheme_Object* def_val]
|
|
[int preserved]
|
|
[Scheme_Object* cell]
|
|
[Scheme_Thread_Cell_Table* cells]
|
|
[Scheme_Object* cell]
|
|
[Scheme_Thread_Cell_Table* cells]
|
|
[Scheme_Object* v])]{
|
|
|
|
Prevents Scheme thread swaps until @cpp{scheme_end_atomic} or
|
|
@cpp{scheme_end_atomic_no_swap} is called. Start-atomic and
|
|
end-atomic pairs can be nested.}
|
|
|
|
@function[(void scheme_end_atomic)]{
|
|
|
|
Ends an atomic region with respect to Scheme threads. The current
|
|
thread may be swapped out immediately (i.e., the call to
|
|
@cpp{scheme_end_atomic} is assumed to be a safe point for thread
|
|
swaps).}
|
|
|
|
@function[(void scheme_end_atomic_no_swap)]{
|
|
|
|
Ends an atomic region with respect to Scheme threads, and also
|
|
prevents an immediate thread swap. (In other words, no Scheme
|
|
thread swaps will occur until a future safe point.)}
|
|
|
|
@function[(void scheme_add_swap_callback
|
|
[Scheme_Closure_Func f]
|
|
[Scheme_Object* data])]{
|
|
|
|
Registers a callback to be invoked just after a Scheme thread is
|
|
swapped in. The @var{data} is provided back to @var{f} when it is
|
|
called, where @cpp{Closure_Func} is defined as follows:
|
|
|
|
@verbatim[#:indent 2]{
|
|
typedef Scheme_Object *(*Scheme_Closure_Func)(Scheme_Object *);
|
|
}}
|
|
|
|
@function[(void scheme_add_swap_out_callback
|
|
[Scheme_Closure_Func f]
|
|
[Scheme_Object* data])]{
|
|
|
|
Like @cpp{scheme_add_swap_callback}, but registers a callback to be
|
|
invoked just before a Scheme thread is swapped out.}
|