Provided by: libgii0-dev_0.9.1-0.1ubuntu1_i386 bug

NAME

       ggLockCreate,  ggLockDestroy,  ggLock,  ggUnlock,  ggTryLock  :  Lowest
       common denominator locking facilities

SYNOPSIS

       #include <ggi/gg.h>

       void *ggLockCreate(void);

       int ggLockDestroy(void *lock);

       void ggLock(void *lock);

       void ggUnlock(void *lock);

       int ggTryLock(void *lock);

DESCRIPTION

       These  functions  allow  sensitive  resource  protection   to   prevent
       simultaneous  or  interleaved  access  to  resources.   For  developers
       accustomed to POSIX-like threading  environments  it  is  important  to
       differentiate  a gglock from a "mutex".  A gglock fills Bboth* the role
       of a "mutex" and a  "condition"  (a.k.a.  an  "event"  or  "waitqueue")
       through  a  simplified  API,  and  as  such there is no such thing as a
       gglock "owner".  A LibGG lock is just locked or unlocked, it  does  not
       matter  by  what or when as long as the application takes care never to
       create a deadlock that never gets broken.

       The locking mechanisms are fully functional  even  in  single-threaded,
       uninterrupted-flow-of-control  environments.    They must still be used
       as described below even in these environments; They are  never  reduced
       to non-operations.

       The  locking  mechanisms are threadsafe, and are also safe to call from
       inside LibGG task handlers.  However, they are not safe  to  use  in  a
       thread  that  may be cancelled during their execution, and they are not
       guaranteed to be safe to use in any special context other than a  LibGG
       task, such as a signal handler or asyncronous procedure call.

       Though  the  LibGG  API  does  provide ample functionality for threaded
       environments, do note that LibGG does not itself  define  any  sort  of
       threading  support,  and does not require or guarantee that threads are
       available.  As such, if the aim  of  an  application  developer  is  to
       remain  as  portable  as  possible,  they should keep in mind that when
       coding for both environments, there are only two situations where locks
       are  appropriate  to  use.   These  two situations are described in the
       examples below.

       Cleanup handlers created with ggRegisterCleanup(3) should not call  any
       of these functions.

       LibGG  must be compiled with threading support if multiple threads that
       call any of these functions are to be used in the program.  When  LibGG
       is compiled with threading support, the ggLock, ggUnlock, and ggTryLock
       functions  are  guaranteed  memory  barriers   for   the   purpose   of
       multiprocessor   data  access  synchronization.   (When  LibGG  is  not
       compiled with threading support, it does  not  matter,  since  separate
       threads should not be using these functions in the first place.)

       ggLockCreate creates a new lock.  The new lock is initially unlocked.

       ggLockDestroy  destroys  a lock, and should only be called when lock is
       unlocked, otherwise the results are undefined and probably undesirable.

       ggLock  will lock the lock and return immediately, but only if the lock
       is unlocked.  If the lock is locked, ggLock will not return  until  the
       lock  gets  unlocked  by a later call to ggUnlock.  In either case lock
       will be locked when ggLock returns.  ggLock is "atomic," such that only
       one  waiting  call to ggLock will return (or one call to ggTryLock will
       return successfully) each  time  lock  is  unlocked.   Order  is  Bnot*
       guaranteed  by  LibGG  --  if two calls to ggLock are made at different
       times on the same lock, either one may return when the lock is unlocked
       regardless  of  which  call was made first.  (It is even possible for a
       call to ggTryLock to grab the lock right after  it  is  unlocked,  even
       though a call to ggLock was already waiting on the lock.)

       ggTryLock  attempts  to  lock  the  lock,  but  unlike ggLock it always
       returns immediately whether or not the lock was locked to  begin  with.
       The  return  value  indicates  whether  the lock was locked at the time
       ggTryLock was invoked.   In  either  case  lock  will  be  locked  when
       ggTryLock returns.

       ggUnlock  unlocks  the  lock.   If any calls to ggLock or ggTryLock are
       subsequently invoked, or have previously been invoked on the lock,  one
       of  the  calls will lock lock and return.  As noted above, which ggLock
       call returns is not specified by LibGG and any observed behavior should
       not be relied upon.  Immediacy is also Bnot* guaranteed; a waiting call
       to ggLock may take some  time  to  return.   ggUnlock  may  be  called,
       successfully,  even if lock is already unlocked, in which case, nothing
       will happen (other than a memory barrier.)

       In all the above functions, where required, the lock  parameter  Bmust*
       be  a  valid lock, or the results are undefined, may contradict what is
       written here, and, in general, bad and unexpected things  might  happen
       to  you  and  your  entire  extended  family.   The  functions do Bnot*
       validate the lock; It is the responsibility  of  the  calling  code  to
       ensure it is valid before it is used.

       Remember,  locking  is  a  complicated issue (at least, when coding for
       multiple environments) and should be a last resort.

RETURN VALUE

       ggLockCreate returns a non-NULL opaque pointer to a mutex,  hiding  its
       internal implementation.  On failure, ggLockCreate returns NULL.

       ggTryLock  returns 0 if the lock was unlocked, or GGI_EBUSY if the lock
       was already locked.

       ggLockDestroy returns 0 on success, otherwise an gg-error(3) code.

EXAMPLES

       One use of gglocks is to protect a critical section, for example access
       to  a  global variable, such that the critical section is never entered
       by more than one thread when a function is called in  a  multi-threaded
       environment.   It  is  important  for  developers  working in a single-
       threaded  environment  to  consider   the   needs   of   multi-threaded
       environments when they provide a function for use by others.

       static int foo = 0;
       static gglock *l;

       void increment_foo(void) {
           ggLock(l);
           foo++;
           ggUnlock(l);
       }

       In  the  above  example, it is assumed that gglock is initialized using
       ggLockCreate before any calls to increment_foo  are  made.   Also  note
       that  in  the  above  example,  when  writing  for maximum portability,
       increment_foo should not be called directly or  indirectly  by  a  task
       handler  which  was  registered  via  ggAddTask  because a deadlock may
       result (unless it is somehow known  that  increment_foo  is  not  being
       executed by any code outside the task handler.)

       Another  use of gglocks is to delay or skip execution of a task handler
       registered with ggAddTask(3).  It is important for  developers  working
       in  a  multi-threaded environment to consider this when they use tasks,
       because in single-threaded environments tasks  interrupt  the  flow  of
       control  and may in fact themselves be immune to interruption.  As such
       they cannot wait for a locked lock to become  unlocked  --  that  would
       create a deadlock.

       static gglock *t, *l, *s;
       int misscnt = 0;

       void do_foo (void) {
              ggLock(t);              /* prevent reentry            */
              ggLock(l);              /* keep task out              */
              do_something();
              ggUnlock(l);            /* task OK to run again       */
              if (!ggTryLock(s)) {    /* run task if it was missed  */
                      if (misscnt) while (misscnt--) do_something_else();
                      ggUnlock(s);
              }
              ggUnlock(t);            /* end of critical section    */
       }

       /* This is called at intervals by the LibGG scheduler */
       static int task_handler(struct gg_task *task) {
             int do_one;

             /* We know the main application never locks s and l at the
              * same time.  We also know it never locks either of the
              * two more than once (e.g. from more than one thread.)
              */

             if (!ggTryLock(s)) {
                    /* Tell the main application to run our code for us
                     * in case we get locked out and cannot run it ourselves.
                     */
                    misscnt++;
                    ggUnlock(s);
                    if (ggTryLock(l)) return; /* We got locked out. */
             } else {
                    /* The main application is currently running old missed
                     * tasks.  But it is using misscnt, so we can’t just ask
                     * it to do one more.
                     *
                     * If this is a threaded environment, we may spin here for
                     * while in the rare case that the main application
                     * unlocked s and locked l between the above ggTryLock(s)
                     * and the below ggLock(l).  However we will get control
                     * back eventually.
                     *
                     * In a non-threaded environment, the below ggLock cannot
                     * wedge, because the main application is stuck inside the
                     * section where s is locked, so we know l is unlocked.
                     */
                    ggLock(l);
                    do_something_else();
                    ggUnlock(l);
                    return;
             }

             /* now we know it is safe to run do_something_else() as
              * do_something() cannot be run until we unlock l.
              * However, in threaded environments, the main application may
              * have just started running do_something_else() for us already.
              * If so, we are done, since we already incremented misscnt.
              * Otherwise we must run it ourselves, and decrement misscnt
              * so it won’t get run an extra time when we unlock s.
              */
             if (ggTryLock(s)) return;
             if (misscnt) while (misscnt--) do_something_else();
             ggUnlock(s);
             ggUnlock(l);
       }

       In  the  above  example,  the  lock  t  prevents reentry into the dofoo
       subroutine  the  same  as  the  last  example.   The  lock  l  prevents
       do_something_else()  from being called while do_something() is running.
       The lock s is being used to protect the misscnt variable and also  acts
       as  a memory barrier to guarantee that the value seen in misscnt is up-
       to-date.   The code in  function  dofoo  will  run  do_something_else()
       after  do_something()  if  the  task  happened while do_something() was
       running.  The above code will work in  multi-threaded-single-processor,
       multi-threaded-multi-processor, and single-threaded environments.

       (Note the above code assumes do_something_else() is reentrant)

SEE ALSO

       pthread_mutex_init(3)