The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
parcel KinoSearch cnick Kino;

/** 
 * Abstract class representing an interprocess mutex lock.
 *
 * The Lock class produces an interprocess mutex lock.  The default subclass
 * uses dot-lock files, but alternative implementations are possible.
 * 
 * Each lock must have a name which is unique per resource to be locked.  Each
 * lock also has a "host" id which should be unique per machine; it is used to
 * help clear away stale locks.
 */

abstract class KinoSearch::Store::Lock inherits KinoSearch::Object::Obj {

    Folder      *folder;
    CharBuf     *name;
    CharBuf     *lock_path;
    CharBuf     *host;
    int32_t      timeout;
    int32_t      interval;  

    /** Abstract constructor.
     *
     * @param folder A Folder.
     * @param name String identifying the resource to be locked, which must
     * consist solely of characters matching [-_.A-Za-z0-9].
     * @param host A unique per-machine identifier.
     * @param timeout Time in milliseconds to keep retrying before abandoning
     * the attempt to Obtain() a lock.
     * @param interval Time in milliseconds between retries.
     */
    public inert Lock*
    init(Lock *self, Folder *folder, const CharBuf *name, 
         const CharBuf *host, int32_t timeout = 0, int32_t interval = 100);

    /** Returns true if the Lock is shared, false if the Lock is exclusive.
     */
    public abstract bool_t
    Shared(Lock *self);

    /** Call Request() once per <code>interval</code> until Request() returns
     * success or the <code>timeout</code> has been reached.
     * 
     * @return true on success, false on failure (sets Err_error).
     */
    public bool_t
    Obtain(Lock *self);

    /** Make one attempt to acquire the lock. 
     *
     * The semantics of Request() differ depending on whether Shared() returns
     * true.  If the Lock is Shared(), then Request() should not fail if
     * another lock is held against the resource identified by
     * <code>name</code> (though it might fail for other reasons).  If it is
     * not Shared() -- i.e. it's an exclusive (write) lock -- then other locks
     * should cause Request() to fail.
     * 
     * @return true on success, false on failure (sets Err_error).
     */
    public abstract bool_t
    Request(Lock *self);

    /** Release the lock.
     */
    public abstract void
    Release(Lock *self);

    /** Indicate whether the resource identified by this lock's name is
     * currently locked.
     *
     * @return true if the resource is locked, false otherwise.
     */
    public abstract bool_t
    Is_Locked(Lock *self);

    /** Release all locks that meet the following three conditions: the lock
     * name matches, the host id matches, and the process id that the lock
     * was created under no longer identifies an active process.
     */
    public abstract void
    Clear_Stale(Lock *self);

    CharBuf*
    Get_Name(Lock *self);

    CharBuf*
    Get_Host(Lock *self);

    CharBuf*
    Get_Lock_Path(Lock *self);

    public void
    Destroy(Lock *self);
}

class KinoSearch::Store::LockFileLock cnick LFLock 
    inherits KinoSearch::Store::Lock {

    CharBuf *link_path;

    inert incremented LockFileLock*
    new(Folder *folder, const CharBuf *name, const CharBuf *host, 
        int32_t timeout = 0, int32_t interval = 100);

    public inert LockFileLock*
    init(LockFileLock *self, Folder *folder, const CharBuf *name, 
         const CharBuf *host, int32_t timeout = 0, int32_t interval = 100);

    public bool_t
    Shared(LockFileLock *self);

    public bool_t
    Request(LockFileLock *self);

    public void
    Release(LockFileLock *self);

    public bool_t
    Is_Locked(LockFileLock *self);

    public void
    Clear_Stale(LockFileLock *self);

    /** Delete a given lock file which meets these conditions...
     *
     *    - lock name matches.
     *    - host id matches.
     *
     * If delete_mine is false, don't delete a lock file which matches this
     * process's pid.  If delete_other is false, don't delete lock files which
     * don't match this process's pid.
     */
    bool_t
    Maybe_Delete_File(LockFileLock *self, const CharBuf *filepath, 
                      bool_t delete_mine, bool_t delete_other);

    public void
    Destroy(LockFileLock *self);
}

/** Lock exception. 
 *
 * LockErr is a subclass of L<Err|KinoSearch::Object::Err> which indicates
 * that a file locking problem occurred.
 */
class KinoSearch::Store::LockErr inherits KinoSearch::Object::Err {
    
    public inert incremented LockErr*
    new(CharBuf *message);

    public inert LockErr*
    init(LockErr *self, CharBuf *message);

    public incremented LockErr*
    Make(LockErr *self);
}

/* Copyright 2006-2011 Marvin Humphrey
 *
 * This program is free software; you can redistribute it and/or modify
 * under the same terms as Perl itself.
 */