1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-01 20:30:28 +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
@cindex asyncs
@cindex user asyncs
@cindex system asyncs
Asyncs are a means of deferring the execution of Scheme code until it is
safe to do so.
Guile provides two kinds of asyncs that share the basic concept but are
otherwise quite different: system asyncs and user asyncs. System asyncs
are integrated into the core of Guile and are executed automatically
when the system is in a state to allow the execution of Scheme code.
For example, it is not possible to execute Scheme code in a POSIX signal
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.
Asyncs are integrated into the core of Guile. A running Guile program
will periodically check if there are asyncs to run, invoking them as
needed. For example, it is not possible to execute Scheme code in a
POSIX signal handler, but in Guile a signal handler can enqueue 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.
This way, you can cause threads to asynchronously execute arbitrary
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
Asyncs can also be queued for threads other than the current one. This
way, you can cause threads to asynchronously execute arbitrary code.
To cause the future asynchronous execution of a procedure in a given
thread, use @code{system-async-mark}.
Automatic invocation of system asyncs can be temporarily disabled by
calling @code{call-with-blocked-asyncs}. This function works by
temporarily increasing the @emph{async blocking level} of the current
thread while a given procedure is running. The blocking level starts
out at zero, and whenever a safe point is reached, a blocking level
greater than zero will prevent the execution of queued asyncs.
Automatic invocation of asyncs can be temporarily disabled by calling
@code{call-with-blocked-asyncs}. This function works by temporarily
increasing the @emph{async blocking level} of the current thread while a
given procedure is running. The blocking level starts out at zero, and
whenever a safe point is reached, a blocking level greater than zero
will prevent the execution of queued asyncs.
Analogously, the procedure @code{call-with-unblocked-asyncs} will
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{scm_dynwind_block_asyncs} and @code{scm_dynwind_unblock_asyncs}
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]
@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
@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
signal handlers.
@end deffn
@deffn {Scheme Procedure} 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
for the current thread while it is running. Return the value returned
by @var{proc}. For the first two variants, call @var{proc} with no
Call @var{proc} and block the execution of asyncs by one level for the
current thread while it is running. Return the value returned by
@var{proc}. For the first two variants, call @var{proc} with no
arguments; for the third, call it with @var{data}.
@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
@deffnx {C Function} scm_call_with_unblocked_asyncs (proc)
Call @var{proc} and unblock the execution of system asyncs by one
level for the current thread while it is running. Return the value
returned by @var{proc}. For the first two variants, call @var{proc}
with no arguments; for the third, call it with @var{data}.
Call @var{proc} and unblock the execution of asyncs by one level for the
current thread while it is running. Return the value returned by
@var{proc}. For the first two variants, call @var{proc} with no
arguments; for the third, call it with @var{data}.
@end deffn
@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}).
@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
@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
@code{abandoned-mutex-error} will be signalled.
When a system async (@pxref{System asyncs}) is activated for a thread
blocked in @code{lock-mutex}, the wait is interrupted and the async is
executed. When the async returns, the wait resumes.
When an async (@pxref{Asyncs}) is activated for a thread blocked in
@code{lock-mutex}, the wait is interrupted and the async is executed.
When the async returns, the wait resumes.
@end deffn
@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
before @code{wait-condition-variable} returns.
When a system async is activated for a thread that is blocked in a
call to @code{wait-condition-variable}, the waiting is interrupted,
the mutex is locked, and the async is executed. When the async
returns, the mutex is unlocked again and the waiting is resumed. When
the thread block while re-acquiring the mutex, execution of asyncs is
blocked.
When an async is activated for a thread that is blocked in a call to
@code{wait-condition-variable}, the waiting is interrupted, the mutex is
locked, and the async is executed. When the async returns, the mutex is
unlocked again and the waiting is resumed. When the thread block while
re-acquiring the mutex, execution of asyncs is blocked.
@end deffn
@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)
Like @code{select} but leaves guile mode while waiting. Also, the
delivery of a system async causes this function to be interrupted with
error code @code{EINTR}.
delivery of an async causes this function to be interrupted with error
code @code{EINTR}.
@end deftypefn
@deftypefn {C Function} {unsigned int} scm_std_sleep ({unsigned int} seconds)
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
@deftypefn {C Function} {unsigned long} scm_std_usleep ({unsigned long} usecs)
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
@ -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
immediately by a semicolon.
Executing @code{SCM_CRITICAL_SECTION_START} will lock a recursive
mutex and block the executing of system asyncs. Executing
Executing @code{SCM_CRITICAL_SECTION_START} will lock a recursive mutex
and block the executing of asyncs. Executing
@code{SCM_CRITICAL_SECTION_END} will unblock the execution of system
asyncs and unlock the mutex. Thus, the code that executes between
these two macros can only be executed in one thread at any one time
and no system asyncs will run. However, because the mutex is a
recursive one, the code might still be reentered by the same thread.
You must either allow for this or avoid it, both by careful coding.
asyncs and unlock the mutex. Thus, the code that executes between these
two macros can only be executed in one thread at any one time and no
asyncs will run. However, because the mutex is a recursive one, the
code might still be reentered by the same thread. You must either allow
for this or avoid it, both by careful coding.
On the other hand, critical sections delimited with these macros can
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,
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
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
for threads, the finalizer may be called from any thread that is running
Guile. In Guile 2.0, finalizers are invoked via ``asyncs'', which
interleaves them with running Scheme code; @pxref{System asyncs}. In
Guile 2.2 there will be a dedicated finalization thread, to ensure that
the finalization doesn't run within the critical section of any other
thread known to Guile.
interleaves them with running Scheme code; @pxref{Asyncs}. In Guile 2.2
there will be a dedicated finalization thread, to ensure that the
finalization doesn't run within the critical section of any other thread
known to Guile.
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

View file

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

View file

@ -44,91 +44,17 @@
/* {Asynchronous Events}
*
* There are two kinds of asyncs: system asyncs and user asyncs. The
* two kinds have some concepts in commen but work slightly
* differently and are not interchangeable.
*
* 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.
* 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
* Scheme list of procedures with zero arguments. When a thread
* executes a SCM_ASYNC_TICK statement (which is included in
* SCM_TICK), 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.
* executes a SCM_ASYNC_TICK statement (which is included in SCM_TICK),
* it will call all procedures on this list.
*/
/* 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;
@ -448,8 +374,6 @@ scm_critical_section_end (void)
void
scm_init_async ()
{
tc16_async = scm_make_smob_type ("async", 0);
#include "libguile/async.x"
}

View file

@ -32,8 +32,6 @@
SCM_API void scm_async_tick (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_for_thread (SCM a, SCM 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,
int fd);
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_call_with_blocked_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
/* 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
@ -585,6 +647,7 @@ scm_i_init_deprecated ()
{
scm_tc16_arbiter = scm_make_smob_type ("arbiter", 0);
scm_set_smob_print (scm_tc16_arbiter, arbiter_print);
tc16_async = scm_make_smob_type ("async", 0);
#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);
#endif

View file

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