1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-01 04:10:18 +02:00

Deprecate user asyncs

* libguile/async.c:
* libguile/async.h:
* libguile/deprecated.c:
* libguile/deprecated.h (scm_async, scm_async_mark, scm_run_asyncs):
  Deprecate these functions, which comprise the "users asyncs" facility.
* module/oop/goops.scm: Adapt to <async> deprecation.
* doc/ref/api-scheduling.texi:
* doc/ref/libguile-concepts.texi:
* doc/ref/libguile-foreign-objects.texi:
* doc/ref/posix.texi: Remove documentation on user asyncs, and replace
  references to "system asyncs" to be just "asyncs".
This commit is contained in:
Andy Wingo 2016-10-17 21:58:08 +02:00
parent 56d8d9a257
commit 59f09d185b
9 changed files with 130 additions and 182 deletions

View file

@ -25,45 +25,28 @@
@subsection Asyncs @subsection Asyncs
@cindex asyncs @cindex asyncs
@cindex user asyncs
@cindex system asyncs
Asyncs are a means of deferring the execution of Scheme code until it is Asyncs are a means of deferring the execution of Scheme code until it is
safe to do so. safe to do so.
Guile provides two kinds of asyncs that share the basic concept but are Asyncs are integrated into the core of Guile. A running Guile program
otherwise quite different: system asyncs and user asyncs. System asyncs will periodically check if there are asyncs to run, invoking them as
are integrated into the core of Guile and are executed automatically needed. For example, it is not possible to execute Scheme code in a
when the system is in a state to allow the execution of Scheme code. POSIX signal handler, but in Guile a signal handler can enqueue a system
For example, it is not possible to execute Scheme code in a POSIX signal async to be executed in the near future, when it is safe to do so.
handler, but such a signal handler can queue a system async to be
executed in the near future, when it is safe to do so.
System asyncs can also be queued for threads other than the current one. Asyncs can also be queued for threads other than the current one. This
This way, you can cause threads to asynchronously execute arbitrary way, you can cause threads to asynchronously execute arbitrary code.
code.
User asyncs offer a convenient means of queuing procedures for future
execution and triggering this execution. They will not be executed
automatically.
@menu
* System asyncs::
* User asyncs::
@end menu
@node System asyncs
@subsubsection System asyncs
To cause the future asynchronous execution of a procedure in a given To cause the future asynchronous execution of a procedure in a given
thread, use @code{system-async-mark}. thread, use @code{system-async-mark}.
Automatic invocation of system asyncs can be temporarily disabled by Automatic invocation of asyncs can be temporarily disabled by calling
calling @code{call-with-blocked-asyncs}. This function works by @code{call-with-blocked-asyncs}. This function works by temporarily
temporarily increasing the @emph{async blocking level} of the current increasing the @emph{async blocking level} of the current thread while a
thread while a given procedure is running. The blocking level starts given procedure is running. The blocking level starts out at zero, and
out at zero, and whenever a safe point is reached, a blocking level whenever a safe point is reached, a blocking level greater than zero
greater than zero will prevent the execution of queued asyncs. will prevent the execution of queued asyncs.
Analogously, the procedure @code{call-with-unblocked-asyncs} will Analogously, the procedure @code{call-with-unblocked-asyncs} will
temporarily decrease the blocking level of the current thread. You temporarily decrease the blocking level of the current thread. You
@ -74,7 +57,7 @@ In addition to the C versions of @code{call-with-blocked-asyncs} and
@code{call-with-unblocked-asyncs}, C code can use @code{call-with-unblocked-asyncs}, C code can use
@code{scm_dynwind_block_asyncs} and @code{scm_dynwind_unblock_asyncs} @code{scm_dynwind_block_asyncs} and @code{scm_dynwind_unblock_asyncs}
inside a @dfn{dynamic context} (@pxref{Dynamic Wind}) to block or inside a @dfn{dynamic context} (@pxref{Dynamic Wind}) to block or
unblock system asyncs temporarily. unblock asyncs temporarily.
@deffn {Scheme Procedure} system-async-mark proc [thread] @deffn {Scheme Procedure} system-async-mark proc [thread]
@deffnx {C Function} scm_system_async_mark (proc) @deffnx {C Function} scm_system_async_mark (proc)
@ -85,16 +68,18 @@ in @var{thread}. When @var{proc} has already been marked for
When @var{thread} is omitted, the thread that called When @var{thread} is omitted, the thread that called
@code{system-async-mark} is used. @code{system-async-mark} is used.
This procedure is not safe to be called from signal handlers. Use As we mentioned above, Scheme signal handlers are already called within
an async and so can run any Scheme code. However, note that the C
function is not safe to be called from C signal handlers. Use
@code{scm_sigaction} or @code{scm_sigaction_for_thread} to install @code{scm_sigaction} or @code{scm_sigaction_for_thread} to install
signal handlers. signal handlers.
@end deffn @end deffn
@deffn {Scheme Procedure} call-with-blocked-asyncs proc @deffn {Scheme Procedure} call-with-blocked-asyncs proc
@deffnx {C Function} scm_call_with_blocked_asyncs (proc) @deffnx {C Function} scm_call_with_blocked_asyncs (proc)
Call @var{proc} and block the execution of system asyncs by one level Call @var{proc} and block the execution of asyncs by one level for the
for the current thread while it is running. Return the value returned current thread while it is running. Return the value returned by
by @var{proc}. For the first two variants, call @var{proc} with no @var{proc}. For the first two variants, call @var{proc} with no
arguments; for the third, call it with @var{data}. arguments; for the third, call it with @var{data}.
@end deffn @end deffn
@ -104,10 +89,10 @@ The same but with a C function @var{proc} instead of a Scheme thunk.
@deffn {Scheme Procedure} call-with-unblocked-asyncs proc @deffn {Scheme Procedure} call-with-unblocked-asyncs proc
@deffnx {C Function} scm_call_with_unblocked_asyncs (proc) @deffnx {C Function} scm_call_with_unblocked_asyncs (proc)
Call @var{proc} and unblock the execution of system asyncs by one Call @var{proc} and unblock the execution of asyncs by one level for the
level for the current thread while it is running. Return the value current thread while it is running. Return the value returned by
returned by @var{proc}. For the first two variants, call @var{proc} @var{proc}. For the first two variants, call @var{proc} with no
with no arguments; for the third, call it with @var{data}. arguments; for the third, call it with @var{data}.
@end deffn @end deffn
@deftypefn {C Function} {void *} scm_c_call_with_unblocked_asyncs (void *(*proc) (void *data), void *data) @deftypefn {C Function} {void *} scm_c_call_with_unblocked_asyncs (void *(*proc) (void *data), void *data)
@ -128,32 +113,6 @@ one level. This function must be used inside a pair of calls to
Wind}). Wind}).
@end deftypefn @end deftypefn
@node User asyncs
@subsubsection User asyncs
A user async is a pair of a thunk (a parameterless procedure) and a
mark. Setting the mark on a user async will cause the thunk to be
executed when the user async is passed to @code{run-asyncs}. Setting
the mark more than once is satisfied by one execution of the thunk.
User asyncs are created with @code{async}. They are marked with
@code{async-mark}.
@deffn {Scheme Procedure} async thunk
@deffnx {C Function} scm_async (thunk)
Create a new user async for the procedure @var{thunk}.
@end deffn
@deffn {Scheme Procedure} async-mark a
@deffnx {C Function} scm_async_mark (a)
Mark the user async @var{a} for future execution.
@end deffn
@deffn {Scheme Procedure} run-asyncs list_of_a
@deffnx {C Function} scm_run_asyncs (list_of_a)
Execute all thunks from the marked asyncs of the list @var{list_of_a}.
@end deffn
@node Atomics @node Atomics
@subsection Atomics @subsection Atomics
@ -443,9 +402,9 @@ If @var{mutex} was locked by a thread that exited before unlocking it,
the next attempt to lock @var{mutex} will succeed, but the next attempt to lock @var{mutex} will succeed, but
@code{abandoned-mutex-error} will be signalled. @code{abandoned-mutex-error} will be signalled.
When a system async (@pxref{System asyncs}) is activated for a thread When an async (@pxref{Asyncs}) is activated for a thread blocked in
blocked in @code{lock-mutex}, the wait is interrupted and the async is @code{lock-mutex}, the wait is interrupted and the async is executed.
executed. When the async returns, the wait resumes. When the async returns, the wait resumes.
@end deffn @end deffn
@deftypefn {C Function} void scm_dynwind_lock_mutex (SCM mutex) @deftypefn {C Function} void scm_dynwind_lock_mutex (SCM mutex)
@ -526,12 +485,11 @@ as returned by @code{gettimeofday}. When the waiting is aborted,
signalled, @code{#t} is returned. The mutex is re-locked in any case signalled, @code{#t} is returned. The mutex is re-locked in any case
before @code{wait-condition-variable} returns. before @code{wait-condition-variable} returns.
When a system async is activated for a thread that is blocked in a When an async is activated for a thread that is blocked in a call to
call to @code{wait-condition-variable}, the waiting is interrupted, @code{wait-condition-variable}, the waiting is interrupted, the mutex is
the mutex is locked, and the async is executed. When the async locked, and the async is executed. When the async returns, the mutex is
returns, the mutex is unlocked again and the waiting is resumed. When unlocked again and the waiting is resumed. When the thread block while
the thread block while re-acquiring the mutex, execution of asyncs is re-acquiring the mutex, execution of asyncs is blocked.
blocked.
@end deffn @end deffn
@deffn {Scheme Procedure} signal-condition-variable condvar @deffn {Scheme Procedure} signal-condition-variable condvar
@ -625,18 +583,18 @@ leaves guile mode while waiting for the condition variable.
@deftypefn {C Function} int scm_std_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) @deftypefn {C Function} int scm_std_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
Like @code{select} but leaves guile mode while waiting. Also, the Like @code{select} but leaves guile mode while waiting. Also, the
delivery of a system async causes this function to be interrupted with delivery of an async causes this function to be interrupted with error
error code @code{EINTR}. code @code{EINTR}.
@end deftypefn @end deftypefn
@deftypefn {C Function} {unsigned int} scm_std_sleep ({unsigned int} seconds) @deftypefn {C Function} {unsigned int} scm_std_sleep ({unsigned int} seconds)
Like @code{sleep}, but leaves guile mode while sleeping. Also, the Like @code{sleep}, but leaves guile mode while sleeping. Also, the
delivery of a system async causes this function to be interrupted. delivery of an async causes this function to be interrupted.
@end deftypefn @end deftypefn
@deftypefn {C Function} {unsigned long} scm_std_usleep ({unsigned long} usecs) @deftypefn {C Function} {unsigned long} scm_std_usleep ({unsigned long} usecs)
Like @code{usleep}, but leaves guile mode while sleeping. Also, the Like @code{usleep}, but leaves guile mode while sleeping. Also, the
delivery of a system async causes this function to be interrupted. delivery of an async causes this function to be interrupted.
@end deftypefn @end deftypefn
@ -649,14 +607,14 @@ These two macros can be used to delimit a critical section.
Syntactically, they are both statements and need to be followed Syntactically, they are both statements and need to be followed
immediately by a semicolon. immediately by a semicolon.
Executing @code{SCM_CRITICAL_SECTION_START} will lock a recursive Executing @code{SCM_CRITICAL_SECTION_START} will lock a recursive mutex
mutex and block the executing of system asyncs. Executing and block the executing of asyncs. Executing
@code{SCM_CRITICAL_SECTION_END} will unblock the execution of system @code{SCM_CRITICAL_SECTION_END} will unblock the execution of system
asyncs and unlock the mutex. Thus, the code that executes between asyncs and unlock the mutex. Thus, the code that executes between these
these two macros can only be executed in one thread at any one time two macros can only be executed in one thread at any one time and no
and no system asyncs will run. However, because the mutex is a asyncs will run. However, because the mutex is a recursive one, the
recursive one, the code might still be reentered by the same thread. code might still be reentered by the same thread. You must either allow
You must either allow for this or avoid it, both by careful coding. for this or avoid it, both by careful coding.
On the other hand, critical sections delimited with these macros can On the other hand, critical sections delimited with these macros can
be nested since the mutex is recursive. be nested since the mutex is recursive.

View file

@ -418,7 +418,7 @@ do such a thing on its own.
If you do not want to allow the running of asynchronous signal handlers, If you do not want to allow the running of asynchronous signal handlers,
you can block them temporarily with @code{scm_dynwind_block_asyncs}, for you can block them temporarily with @code{scm_dynwind_block_asyncs}, for
example. See @xref{System asyncs}. example. @xref{Asyncs}.
Since signal handling in Guile relies on safe points, you need to make Since signal handling in Guile relies on safe points, you need to make
sure that your functions do offer enough of them. Normally, calling sure that your functions do offer enough of them. Normally, calling

View file

@ -279,10 +279,10 @@ Note that the finalizer may be invoked in ways and at times you might
not expect. In particular, if the user's Guile is built with support not expect. In particular, if the user's Guile is built with support
for threads, the finalizer may be called from any thread that is running for threads, the finalizer may be called from any thread that is running
Guile. In Guile 2.0, finalizers are invoked via ``asyncs'', which Guile. In Guile 2.0, finalizers are invoked via ``asyncs'', which
interleaves them with running Scheme code; @pxref{System asyncs}. In interleaves them with running Scheme code; @pxref{Asyncs}. In Guile 2.2
Guile 2.2 there will be a dedicated finalization thread, to ensure that there will be a dedicated finalization thread, to ensure that the
the finalization doesn't run within the critical section of any other finalization doesn't run within the critical section of any other thread
thread known to Guile. known to Guile.
In either case, finalizers run concurrently with the main program, and In either case, finalizers run concurrently with the main program, and
so they need to be async-safe and thread-safe. If for some reason this so they need to be async-safe and thread-safe. If for some reason this

View file

@ -2007,11 +2007,11 @@ information.
The following procedures raise, handle and wait for signals. The following procedures raise, handle and wait for signals.
Scheme code signal handlers are run via a system async (@pxref{System Scheme code signal handlers are run via an async (@pxref{Asyncs}), so
asyncs}), so they're called in the handler's thread at the next safe they're called in the handler's thread at the next safe opportunity.
opportunity. Generally this is after any currently executing Generally this is after any currently executing primitive procedure
primitive procedure finishes (which could be a long time for finishes (which could be a long time for primitives that wait for an
primitives that wait for an external event). external event).
@deffn {Scheme Procedure} kill pid sig @deffn {Scheme Procedure} kill pid sig
@deffnx {C Function} scm_kill (pid, sig) @deffnx {C Function} scm_kill (pid, sig)

View file

@ -44,91 +44,17 @@
/* {Asynchronous Events} /* {Asynchronous Events}
* *
* There are two kinds of asyncs: system asyncs and user asyncs. The * Asyncs are used to run arbitrary code at the next safe point in a
* two kinds have some concepts in commen but work slightly * specified thread. You can use them to trigger execution of Scheme
* differently and are not interchangeable. * code from signal handlers or to interrupt a thread, for example.
*
* System asyncs are used to run arbitrary code at the next safe point
* in a specified thread. You can use them to trigger execution of
* Scheme code from signal handlers or to interrupt a thread, for
* example.
* *
* Each thread has a list of 'activated asyncs', which is a normal * Each thread has a list of 'activated asyncs', which is a normal
* Scheme list of procedures with zero arguments. When a thread * Scheme list of procedures with zero arguments. When a thread
* executes a SCM_ASYNC_TICK statement (which is included in * executes a SCM_ASYNC_TICK statement (which is included in SCM_TICK),
* SCM_TICK), it will call all procedures on this list. * it will call all procedures on this list.
*
* Also, a thread will wake up when a procedure is added to its list
* of active asyncs and call them. After that, it will go to sleep
* again. (Not implemented yet.)
*
*
* User asyncs are a little data structure that consists of a
* procedure of zero arguments and a mark. There are functions for
* setting the mark of a user async and for calling all procedures of
* marked asyncs in a given list. Nothing you couldn't quickly
* implement yourself.
*/ */
/* User asyncs. */
static scm_t_bits tc16_async;
/* cmm: this has SCM_ prefix because SCM_MAKE_VALIDATE expects it.
this is ugly. */
#define SCM_ASYNCP(X) SCM_TYP16_PREDICATE (tc16_async, X)
#define VALIDATE_ASYNC(pos, a) SCM_MAKE_VALIDATE_MSG(pos, a, ASYNCP, "user async")
#define ASYNC_GOT_IT(X) (SCM_SMOB_FLAGS (X))
#define SET_ASYNC_GOT_IT(X, V) (SCM_SET_SMOB_FLAGS ((X), ((V))))
#define ASYNC_THUNK(X) SCM_SMOB_OBJECT_1 (X)
SCM_DEFINE (scm_async, "async", 1, 0, 0,
(SCM thunk),
"Create a new async for the procedure @var{thunk}.")
#define FUNC_NAME s_scm_async
{
SCM_RETURN_NEWSMOB (tc16_async, SCM_UNPACK (thunk));
}
#undef FUNC_NAME
SCM_DEFINE (scm_async_mark, "async-mark", 1, 0, 0,
(SCM a),
"Mark the async @var{a} for future execution.")
#define FUNC_NAME s_scm_async_mark
{
VALIDATE_ASYNC (1, a);
SET_ASYNC_GOT_IT (a, 1);
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
SCM_DEFINE (scm_run_asyncs, "run-asyncs", 1, 0, 0,
(SCM list_of_a),
"Execute all thunks from the asyncs of the list @var{list_of_a}.")
#define FUNC_NAME s_scm_run_asyncs
{
while (! SCM_NULL_OR_NIL_P (list_of_a))
{
SCM a;
SCM_VALIDATE_CONS (1, list_of_a);
a = SCM_CAR (list_of_a);
VALIDATE_ASYNC (SCM_ARG1, a);
if (ASYNC_GOT_IT (a))
{
SET_ASYNC_GOT_IT (a, 0);
scm_call_0 (ASYNC_THUNK (a));
}
list_of_a = SCM_CDR (list_of_a);
}
return SCM_BOOL_T;
}
#undef FUNC_NAME
static scm_i_pthread_mutex_t async_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER; static scm_i_pthread_mutex_t async_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
@ -448,8 +374,6 @@ scm_critical_section_end (void)
void void
scm_init_async () scm_init_async ()
{ {
tc16_async = scm_make_smob_type ("async", 0);
#include "libguile/async.x" #include "libguile/async.x"
} }

View file

@ -32,8 +32,6 @@
SCM_API void scm_async_tick (void); SCM_API void scm_async_tick (void);
SCM_API void scm_switch (void); SCM_API void scm_switch (void);
SCM_API SCM scm_async (SCM thunk);
SCM_API SCM scm_async_mark (SCM a);
SCM_API SCM scm_system_async_mark (SCM a); SCM_API SCM scm_system_async_mark (SCM a);
SCM_API SCM scm_system_async_mark_for_thread (SCM a, SCM thread); SCM_API SCM scm_system_async_mark_for_thread (SCM a, SCM thread);
SCM_INTERNAL void scm_i_queue_async_cell (SCM cell, scm_i_thread *); SCM_INTERNAL void scm_i_queue_async_cell (SCM cell, scm_i_thread *);
@ -41,7 +39,6 @@ SCM_INTERNAL int scm_i_setup_sleep (scm_i_thread *,
SCM obj, scm_i_pthread_mutex_t *m, SCM obj, scm_i_pthread_mutex_t *m,
int fd); int fd);
SCM_INTERNAL void scm_i_reset_sleep (scm_i_thread *); SCM_INTERNAL void scm_i_reset_sleep (scm_i_thread *);
SCM_API SCM scm_run_asyncs (SCM list_of_a);
SCM_API SCM scm_noop (SCM args); SCM_API SCM scm_noop (SCM args);
SCM_API SCM scm_call_with_blocked_asyncs (SCM proc); SCM_API SCM scm_call_with_blocked_asyncs (SCM proc);
SCM_API SCM scm_call_with_unblocked_asyncs (SCM proc); SCM_API SCM scm_call_with_unblocked_asyncs (SCM proc);

View file

@ -578,6 +578,68 @@ SCM_DEFINE (scm_release_arbiter, "release-arbiter", 1, 0, 0,
#undef FUNC_NAME #undef FUNC_NAME
/* User asyncs. */
static scm_t_bits tc16_async;
/* cmm: this has SCM_ prefix because SCM_MAKE_VALIDATE expects it.
this is ugly. */
#define SCM_ASYNCP(X) SCM_TYP16_PREDICATE (tc16_async, X)
#define VALIDATE_ASYNC(pos, a) SCM_MAKE_VALIDATE_MSG(pos, a, ASYNCP, "user async")
#define ASYNC_GOT_IT(X) (SCM_SMOB_FLAGS (X))
#define SET_ASYNC_GOT_IT(X, V) (SCM_SET_SMOB_FLAGS ((X), ((V))))
#define ASYNC_THUNK(X) SCM_SMOB_OBJECT_1 (X)
SCM_DEFINE (scm_async, "async", 1, 0, 0,
(SCM thunk),
"Create a new async for the procedure @var{thunk}.")
#define FUNC_NAME s_scm_async
{
scm_c_issue_deprecation_warning
("\"User asyncs\" are deprecated. Use closures instead.");
SCM_RETURN_NEWSMOB (tc16_async, SCM_UNPACK (thunk));
}
#undef FUNC_NAME
SCM_DEFINE (scm_async_mark, "async-mark", 1, 0, 0,
(SCM a),
"Mark the async @var{a} for future execution.")
#define FUNC_NAME s_scm_async_mark
{
VALIDATE_ASYNC (1, a);
SET_ASYNC_GOT_IT (a, 1);
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
SCM_DEFINE (scm_run_asyncs, "run-asyncs", 1, 0, 0,
(SCM list_of_a),
"Execute all thunks from the asyncs of the list @var{list_of_a}.")
#define FUNC_NAME s_scm_run_asyncs
{
while (! SCM_NULL_OR_NIL_P (list_of_a))
{
SCM a;
SCM_VALIDATE_CONS (1, list_of_a);
a = SCM_CAR (list_of_a);
VALIDATE_ASYNC (SCM_ARG1, a);
if (ASYNC_GOT_IT (a))
{
SET_ASYNC_GOT_IT (a, 0);
scm_call_0 (ASYNC_THUNK (a));
}
list_of_a = SCM_CDR (list_of_a);
}
return SCM_BOOL_T;
}
#undef FUNC_NAME
void void
@ -585,6 +647,7 @@ scm_i_init_deprecated ()
{ {
scm_tc16_arbiter = scm_make_smob_type ("arbiter", 0); scm_tc16_arbiter = scm_make_smob_type ("arbiter", 0);
scm_set_smob_print (scm_tc16_arbiter, arbiter_print); scm_set_smob_print (scm_tc16_arbiter, arbiter_print);
tc16_async = scm_make_smob_type ("async", 0);
#include "libguile/deprecated.x" #include "libguile/deprecated.x"
} }

View file

@ -223,6 +223,12 @@ SCM_DEPRECATED SCM scm_release_arbiter (SCM arb);
SCM_DEPRECATED SCM scm_async (SCM thunk);
SCM_DEPRECATED SCM scm_async_mark (SCM a);
SCM_DEPRECATED SCM scm_run_asyncs (SCM list_of_a);
void scm_i_init_deprecated (void); void scm_i_init_deprecated (void);
#endif #endif

View file

@ -75,7 +75,7 @@
;; once you have an instance. Perhaps FIXME to provide a ;; once you have an instance. Perhaps FIXME to provide a
;; smob-type-name->class procedure. ;; smob-type-name->class procedure.
<promise> <thread> <mutex> <condition-variable> <promise> <thread> <mutex> <condition-variable>
<regexp> <hook> <bitvector> <random-state> <async> <regexp> <hook> <bitvector> <random-state>
<directory> <array> <character-set> <directory> <array> <character-set>
<dynamic-object> <guardian> <macro> <dynamic-object> <guardian> <macro>
@ -3097,7 +3097,8 @@ var{initargs}."
;;; ;;;
(begin-deprecated (begin-deprecated
(define-public <arbiter> (find-subclass <top> '<arbiter>))) (define-public <arbiter> (find-subclass <top> '<arbiter>))
(define-public <async> (find-subclass <top> '<async>)))
(define <promise> (find-subclass <top> '<promise>)) (define <promise> (find-subclass <top> '<promise>))
(define <thread> (find-subclass <top> '<thread>)) (define <thread> (find-subclass <top> '<thread>))
@ -3107,7 +3108,6 @@ var{initargs}."
(define <hook> (find-subclass <top> '<hook>)) (define <hook> (find-subclass <top> '<hook>))
(define <bitvector> (find-subclass <top> '<bitvector>)) (define <bitvector> (find-subclass <top> '<bitvector>))
(define <random-state> (find-subclass <top> '<random-state>)) (define <random-state> (find-subclass <top> '<random-state>))
(define <async> (find-subclass <top> '<async>))
(define <directory> (find-subclass <top> '<directory>)) (define <directory> (find-subclass <top> '<directory>))
(define <array> (find-subclass <top> '<array>)) (define <array> (find-subclass <top> '<array>))
(define <character-set> (find-subclass <top> '<character-set>)) (define <character-set> (find-subclass <top> '<character-set>))