PACTION(3)                BSD Library Functions Manual                PACTION(3)

     paction — actions in a separate thread

     PDEL Library (libpdel, -lpdel)

     #include <sys/types.h>
     #include <pthread.h>
     #include <pdel/util/paction.h>

     paction_start(struct paction **actionp, pthread_mutex_t *mutex,
         paction_handler_t *handler, paction_finish_t *finish, void *arg);

     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.

     libpdel(3), pevent(3), pthread_cancel(3), pthread_create(3)

     The PDEL library was developed at Packet Design, LLC.

     Archie Cobbs <>

BSD                              April 22, 2002                              BSD