The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

parcel Lucy;

/**
 * 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.
 */

public abstract class Lucy::Store::Lock inherits Clownfish::Obj {

    Folder      *folder;
    String      *name;
    String      *lock_path;
    String      *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, String *name,
         String *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
    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
    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
    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
    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);

    String*
    Get_Name(Lock *self);

    String*
    Get_Host(Lock *self);

    String*
    Get_Lock_Path(Lock *self);

    public void
    Destroy(Lock *self);
}

class Lucy::Store::LockFileLock nickname LFLock
    inherits Lucy::Store::Lock {

    String *link_path;

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

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

    public bool
    Shared(LockFileLock *self);

    public bool
    Request(LockFileLock *self);

    public void
    Release(LockFileLock *self);

    public bool
    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
    Maybe_Delete_File(LockFileLock *self, String *filepath,
                      bool delete_mine, bool delete_other);

    public void
    Destroy(LockFileLock *self);
}

/** Lock exception.
 *
 * LockErr is a subclass of L<Err|Clownfish::Err> which indicates
 * that a file locking problem occurred.
 */
public class Lucy::Store::LockErr inherits Clownfish::Err {

    public inert incremented LockErr*
    new(String *message);

    public inert LockErr*
    init(LockErr *self, String *message);
}