paction
— actions
in a separate thread
PDEL Library (libpdel, -lpdel)
#include
<sys/types.h>
#include <pthread.h>
#include
<pdel/util/paction.h>
int
paction_start
(struct
paction **actionp,
pthread_mutex_t *mutex,
paction_handler_t
*handler,
paction_finish_t *finish,
void *arg);
void
paction_cancel
(struct
paction **actionp);
These functions provide support for
actions,
which are simply function invocations that happen in separate threads. This
is just a simplified API for spawning and cancelling threads with built-in
support for mutex locking and avoiding certain race conditions.
paction_start
()
creates a new action and stores a pointer to the action, represented by a
struct paction
, in *actionp.
The creation of an action results in handler
() being
invoked in a new thread. paction_cancel
() cancels an
action by canceling its associated thread. In any case, when the action has
completed, finish
() is invoked. If the action was
not canceled via paction_cancel
(), then
*mutex is acquired before
finish
() is invoked and released afterward. If
paction_cancel
() was called, then the mutex is not
acquired for finish
(). The
was_canceled argument to
finish
() reflects this.
handler and finish
must be pointers to functions having these types:
typedef void paction_handler_t(void *arg);
typedef void paction_finish_t(void *arg, int was_canceled);
When
paction_start
()
is invoked, *actionp must be
NULL
. As long as the action is still in progress
(i.e., finish
() has not yet been invoked),
*actionp will be non-NULL. When the action completes
or is canceled, *actionp is set to
NULL
again. Therefore,
*actionp must remain valid and unmodified for the
duration of the action, and can be used as an indicator of whether the
action has completed.
paction_cancel
()
cancels an outstanding action. This results in the action thread being
canceled at the next cancellation point. Therefore,
handler
() may need to register thread cleanup hooks
in order to free any allocated resources in the case of cancellation. Upon
return, *actionp is set to
NULL
. If *actionp is already
NULL
when paction_cancel
()
is invoked, nothing happens.
In any case,
finish
() is
invoked when the action terminates. There are two reasons for an action
terminating: either the action terminated normally, or
paction_cancel
() was invoked. If the action
terminated normally, *mutex is locked,
*actionp is set to NULL
, and
finish
() is invoked with
was_canceled set to zero. When
finish
() returns *mutex is
unlocked.
If the action was canceled by
paction_cancel
(),
then neither mutex nor actionp
are dereferenced;
final
()
is simply called with was_canceled set to non-zero.
Note that *actionp will have already been set to
NULL
previously by
paction_cancel
().
Cancelling the action thread directly via
pthread_cancel(3)
is treated just as if
handler
()
returned early; i.e., the first case above.
There are inherent race conditions between an action's
finish
() function being invoked and reading the
value of *actionp or canceling the action with
paction_cancel
(). The *mutex
should be used to avoid these problems by using it to protect
*actionp.
The user code should acquire
*mutex before calling
paction_start
()
or paction_cancel
(), or before accessing
*actionp. If this protocol is followed, then
*actionp will be non-NULL if and only if the action is
still pending, i.e., finish
() has not yet been
invoked. In addition, the was_canceled argument will
always be accurate, i.e., be non-zero if and only if
paction_cancel
() was called to cancel the
action.
Finally, mutex and
actionp will not be dereferenced after a call to
paction_cancel
(),
so it is always safe to destroy the memory pointed to by
mutex and actionp after calling
paction_cancel
(). However, arg
must remain valid until finish
() is invoked, which
may occur
after
paction_cancel
() returns; alternatively,
finish
() must not dereference
arg if was_canceled is
non-zero.
paction_start
() returns -1 if there is an
error, with errno set appropriately. In particular, if
*actionp is not equal to NULL
,
then errno will be set to
EBUSY
.
The PDEL library was developed at Packet Design, LLC.
http://www.packetdesign.com/
Archie Cobbs
⟨archie@freebsd.org⟩