/*
 * $Id: nwsem.txt 15102 2010-07-14 12:48:39Z vszakats $
 */

/*  $DOC$
 *  $FUNCNAME$
 *      FT_NWSEMOPEN()
 *  $CATEGORY$
 *      NetWare
 *  $ONELINER$
 *      Open or create a NetWare semaphore
 *  $SYNTAX$
 *      FT_NWSEMOPEN( <cName>, <nInitVal>, <@nHandle>, <@nOpenCnt> ) -> nRc
 *  $ARGUMENTS$
 *      <cName> is the semaphore name, maximum length is 127 characters.
 * 
 *      <nInitVal> is the initial value for the semaphore.  It must start
 *      as a positive number, to a maximum of 127.
 * 
 *      <@nHandle> is the semaphore handle.  THIS MUST BE PASSED BY
 *      REFERENCE!  On exit, <nHandle> will contain a numeric value that
 *      refers to the opened semaphore.  You will need it to pass to
 *      other semaphore functions!  PASS IT BY REFERENCE!
 * 
 *      <@nOpenCnt> is the number of stations that have opened the
 *      semaphore.  THIS MUST BE PASSED BY REFERENCE! On exit, <nOpenCnt>
 *      will contain a numeric value.
 *  $RETURNS$
 *      nRc, a numeric result code, as follows:
 * 
 *            0 - success
 *          254 - Invalid semaphore name length
 *          255 - Invalid semaphore value
 * 
 *      <nHandle> will contain the semaphore handle, and
 *      <nOpenCnt> will contain the number of stations that have opened
 *      the semaphore.
 *  $DESCRIPTION$
 *      A semaphore is simply a label that indirectly controls network
 *      activity.  There is a semaphore name, which can be up to 127
 *      characters, and an associated value, which can range from 0 to
 *      127.
 * 
 *      A semaphore can be used for many things, but is most often used
 *      to limit the number of users in an application, and to control
 *      access to a network resource.
 * 
 *      A semaphore essentially allows you to place locks on resources
 *      other than files.
 * 
 *      An application begins the process by calling FT_NWSEMOPEN().
 *      If the semaphore doesn't exist, NetWare will create it.
 *      FT_NWSEMOPEN() returns a handle that is used in other semaphore
 *      calls.
 * 
 *      Applications use FT_NWSEMWAIT() to wait for a semaphore to
 *      become available.  FT_NWSEMWAIT() decrements the semaphore's
 *      value by 1.  If the value > 0, then the application should
 *      be allowed to access the semaphore's resource.  If the value
 *      goes negative, then the application is placed in a queue.
 *      How long your app is in the queue is determined by how you
 *      set the timeout parameter.  If you can't get the resource in
 *      the time you allot, you're let out of the queue and the
 *      value increments by 1 again.
 * 
 *      When an application finishes with a semaphore, it should
 *      call FT_NWSEMSIG() to increment the value, and then
 *      FT_NWSEMCLOSE() to close the semaphore.  When the semaphore's
 *      open count goes to 0, NetWare deletes it.
 * 
 *      FT_NWSEMEX() can be used to examine the value and open count
 *      without affecting them.
 * 
 *      For an interesting discussion on the operating system aspects
 *      of semaphores, check "Operating Systems Design and Implementation"
 *      by A. Tanenbaum, page 60.  For more details on NetWare's
 *      semaphore facilities, refer to Charles Rose's "Programmer's
 *      Guide to NetWare".  The "Programmer's Guide" will make an
 *      excellent companion guide to the source code for all NetWare
 *      functions in the Nanforum Toolkit.
 *  $EXAMPLES$
 *      LOCAL nInitVal, nRc, nHandle, nOpenCnt
 * 
 *      nInitVal := 2
 *      nRc      := FT_NWSEMOPEN( "Semaphore Test", nInitVal, ;
 *                                @nHandle, @nOpenCnt )
 * 
 *      IF nRc != 0
 *        QOUT =: "Error: " + STR( nRc ) )
 *        QUIT
 *      ENDIF
 *  $SEEALSO$
 *      FT_NWSEMEX() FT_NWSEMWAIT() FT_NWSEMSIG() FT_NWSEMCLOSE() FT_NWSEMLOCK()
 *  $END$
 */

/*  $DOC$
 *  $FUNCNAME$
 *      FT_NWSEMEX()
 *  $CATEGORY$
 *      NetWare
 *  $ONELINER$
 *      Examine a NetWare semaphore's value and open count
 *  $SYNTAX$
 *      FT_NWSEMEX( <nHandle>, <@nValue>, <@nOpenCnt> ) -> nRc
 *  $ARGUMENTS$
 *      <nHandle> is the semaphore handle, returned from a previous call
 *      to FT_NWSEMOPEN().
 * 
 *      <@nValue> will get the current semaphore value.  THIS NUMERIC
 *      ARGUMENT MUST BE PASSED BY REFERENCE!
 * 
 *      <@nOpenCnt> will get the current number of workstations
 *      that have opened the semaphore.  THIS NUMERIC ARGUMENT MUST BE
 *      PASSED BY REFERENCE!
 *  $RETURNS$
 *      nRc, a numeric, as follows:
 * 
 *            0 - success
 *          255 - invalid semaphore handle
 * 
 *      In addition, nValue will be set to the semaphore's current value,
 *      and nOpenCnt will be set to the number of stations that have
 *      opened the semaphore.
 *  $DESCRIPTION$
 *      See the description for FT_NWSEMOPEN().
 *  $EXAMPLES$
 *    nInitVal := 2
 *    nHandle  := 0
 *    nOpenCnt := 0
 * 
 *    FT_NWSEMOPEN( "Semaphore Test", nInitVal, @nHandle, @nOpenCnt )
 * 
 *    nRc := FT_NWSEMWAIT( nHandle )
 *    IF nRc == 254
 *       QOUT( "All slots for this resource are currently in use" )
 *       QUIT
 *    ENDIF
 * 
 *    FT_NWSEMEX( nHandle, @nValue, @nOpenCnt )
 *    QOUT( "Semaphore test -> Open at [" + ;
 *          ALLTRIM(STR(nOpenCnt))        + ;
 *          "] stations, value is ["      + ;
 *          ALLTRIM(STR(nValue)) + "]" )
 *  $SEEALSO$
 *      FT_NWSEMOPEN() FT_NWSEMWAIT() FT_NWSEMSIG() FT_NWSEMCLOSE() FT_NWSEMLOCK()
 *  $END$
 */

/*  $DOC$
 *  $FUNCNAME$
 *     FT_NWSEMWAIT()
 *  $CATEGORY$
 *     NetWare
 *  $ONELINER$
 *     Wait on a NetWare semaphore (decrement)
 *  $SYNTAX$
 *     FT_NWSEMWAIT( <nHandle> [, nTimeout ] ) -> nRc
 *  $ARGUMENTS$
 *     <nHandle> is the semaphore handle, returned from a previous call
 *     to FT_NWSEMOPEN().
 * 
 *     <nTimeOut> is an optional parameter telling how long you wish to
 *     wait on this semaphore.  This is a numeric indicating the number
 *     of clock ticks (approx 1/18 sec ) to wait.  A zero (the default)
 *     means "don't wait."
 *  $RETURNS$
 *     nRc, a numeric, as follows:
 * 
 *           0 - success
 *         254 - timeout failure
 *         255 - invalid semaphore handle
 *  $DESCRIPTION$
 *     See the description for the FT_NWSEMOPEN() function.
 *  $EXAMPLES$
 *    FT_NWSEMOPEN( "Semaphore Test", nInitVal, @nHandle, @nOpenCnt )
 * 
 *    nRc := FT_NWSEMWAIT( nHandle )
 *    IF nRc == 254
 *       QOUT( "All slots for this resource are currently in use" )
 *       QUIT
 *    ENDIF
 *  $SEEALSO$
 *      FT_NWSEMOPEN() FT_NWSEMEX() FT_NWSEMSIG() FT_NWSEMCLOSE() FT_NWSEMLOCK()
 *  $END$
 */

/*  $DOC$
 *  $FUNCNAME$
 *     FT_NWSEMSIG()
 *  $CATEGORY$
 *     NetWare
 *  $ONELINER$
 *     Signal a NetWare semaphore (increment)
 *  $SYNTAX$
 *     FT_NWSEMSIG( nHandle ) -> nRc
 *  $ARGUMENTS$
 *     <nHandle> is the semaphore handle, returned from a previous call
 *     to FT_NWSEMOPEN().
 *  $RETURNS$
 *     nRc, a numeric, as follows
 * 
 *          0 - success
 *          1 - semaphore overflow ( value > 127 )
 *        255 - invalid semaphore handle
 *  $DESCRIPTION$
 *      Use FT_NWSEMSIG() when your app has finished with the resource
 *      locked by a semaphore.  This will increase the value (thus
 *      making a slot available to another app).
 * 
 *      For more information, see the description under FT_NWSEMOPEN().
 *  $EXAMPLES$
 *      QOUT( "Signal returns: " + STR( FT_NWSEMSIG( nHandle ) ) )
 *  $SEEALSO$
 *      FT_NWSEMOPEN() FT_NWSEMEX() FT_NWSEMWAIT() FT_NWSEMCLOSE() FT_NWSEMLOCK()
 *  $END$
 */

/*  $DOC$
 *  $FUNCNAME$
 *     FT_NWSEMCLOSE()
 *  $CATEGORY$
 *     NetWare
 *  $ONELINER$
 *     Close a NetWare semaphore
 *  $SYNTAX$
 *     FT_NWSEMCLOSE( <nHandle> )  -> nRc
 *  $ARGUMENTS$
 *     <nHandle> is the semaphore handle, returned from a previous call
 *     to FT_NWSEMOPEN().
 *  $RETURNS$
 *     nRc, a numeric, as follows:
 * 
 *            0 - success
 *          255 - invalid semaphore handle
 *  $DESCRIPTION$
 *     Call FT_NWSEMCLOSE() when the app is finished.  This decrements
 *     the open count for the semaphore.  If the open count hits zero,
 *     the semaphore is deleted by NetWare.
 *  $EXAMPLES$
 *     QOUT( "Close returns:  " + STR( FT_NWSEMCLOSE( nHandle ) ) )
 *  $SEEALSO$
 *     FT_NWSEMOPEN() FT_NWSEMEX() FT_NWSEMWAIT() FT_NWSEMSIG() FT_NWSEMLOCK()
 *  $END$
 */

/*  $DOC$
 *  $FUNCNAME$
 *     FT_NWSEMLOCK()
 *  $CATEGORY$
 *     NetWare
 *  $ONELINER$
 *     Perform a semaphore "lock"
 *  $SYNTAX$
 *     FT_NWSEMLOCK ( <cSemaphore>, <@nHandle> ) -> lRet
 *  $ARGUMENTS$
 *     <cSemaphore> is the name of a semaphore you want to "lock."
 *     <nHandle> is the semaphore's handle, if you get the lock.
 *     THIS MUST BE PASSED BY REFERENCE!
 *  $RETURNS$
 *     lRet == .t. if you get the lock, .f. if you don't.
 *     If the lock succeeds, <nHandle> will contain the semaphore
 *     handle.  If it fails, the value of <nHandle> is undefined.
 * 
 *  $DESCRIPTION$
 *     FT_NWSEMLOCK() uses the Nanforum Toolkit's NetWare Semaphore API
 *     functions in order to provide a general purpose "lock" you can use in
 *     a NetWare environment.
 * 
 *     An interesting byproduct of NetWare's semaphore functions is
 *     the "open count" which tells you how many connections have this
 *     semaphore open.  This is different from the semaphore's _value_,
 *     which is set when the semaphore is opened and changed with
 *     signal() and wait().
 * 
 *     The point of semaphores is that you don't care how many users
 *     are using the resource; you merely wait on a semaphore until
 *     the resource becomes available or you give up.  When you're done,
 *     you signal it and off you go.
 * 
 *     Back to the open count.  FT_NWSEMLOCK() opens the semaphore
 *     as named in <cSemaphore>.  After it is opened, the open count
 *     is checked.  If it is anything other than 1, that means someone
 *     else has it (or you failed in your open) so the semaphore is
 *     closed and the "lock" is refused.  If the value is 1, then your
 *     app is that 1 station so the "lock" is granted.
 * 
 *     You can use a semaphore lock to control access to anything
 *     that Clipper's RLOCK() and FLOCK() can't help you with, such
 *     as text files written with the low level file i/o functions,
 *     etc.
 *  $EXAMPLES$
 *     LOCAL nHandle := 0
 *     IF FT_NWSEMLOCK( "k:\apps\error.log", @nHandle )
 *         // Note, you aren't actually LOCKING this file, you are
 *         // just locking a semaphore by the same name.  As long as
 *         // all apps that might be using this file are cooperating
 *         // with the same kind of semaphore lock, you can effectively
 *         // control access to the file.
 *       ELSE
 *         QOUT("Couldn't lock file.")
 *       ENDIF
 *       * Processing, then:
 *       FT_NWSEMUNLOCK( nHandle )
 * 
 *  $SEEALSO$
 *     FT_NWSEMOPEN() FT_NWSEMEX() FT_NWSEMWAIT() FT_NWSEMSIG() FT_NWSEMUNLOCK()
 *  $END$
 */

/*  $DOC$
 *  $FUNCNAME$
 *     FT_NWSEMUNLOCK()
 *  $CATEGORY$
 *     NetWare
 *  $ONELINER$
 *     "Unlock" a semaphore locked by FT_NWSEMLOCK()
 *  $SYNTAX$
 *     FT_NWSEMUNLOCK( <nHandle> ) -> lRet
 *  $ARGUMENTS$
 *     <nHandle> is the semaphore handle returned from FT_NWSEMLOCK()
 *  $RETURNS$
 *     lRet == .t. if you successfully unlock the semaphore, .f. if
 *     you don't.  If this call fails, it could be that you're passing
 *     an invalid semaphore handle.
 *  $DESCRIPTION$
 *     This call unlocks a semaphore prevsiously locked via FT_NWSEMLOCK().
 *     It is important that you get a valid semaphore handle from
 *     FT_NWSEMLOCK() before you use this call.  Make sure when you call
 *     FT_NWSEMLOCK() that you pass a numeric parameter in for the handle
 *     BY REFERENCE.
 *  $EXAMPLES$
 *     LOCAL nHandle := 0
 *     IF FT_NWSEMLOCK( "k:\apps\error.log", @nHandle )
 *         // Note, you aren't actually LOCKING this file, you are
 *         // just locking a semaphore by the same name.  As long as
 *         // all apps that might be using this file are cooperating
 *         // with the same kind of semaphore lock, you can effectively
 *         // control access to the file.
 *       ELSE
 *         QOUT("Couldn't lock file.")
 *       ENDIF
 *       * Processing, then:
 *       FT_NWSEMUNLOCK( nHandle )
 * 
 *  $SEEALSO$
 *     FT_NWSEMOPEN() FT_NWSEMEX() FT_NWSEMWAIT() FT_NWSEMSIG() FT_NWSEMLOCK()
 *  $END$
 */
