/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
  QC_Mutex.qpp

  Qore Programming Language

  Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.

  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the "Software"),
  to deal in the Software without restriction, including without limitation
  the rights to use, copy, modify, merge, publish, distribute, sublicense,
  and/or sell copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  DEALINGS IN THE SOFTWARE.

  Note that the Qore library is released under a choice of three open-source
  licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
  information.
*/

#include <qore/Qore.h>
#include "qore/intern/QC_Mutex.h"
#include "qore/intern/QC_AbstractSmartLock.h"

//! A class providing an implementation for a simple thread lock
/** @par Overview
    This class inherits @ref Qore::Thread::AbstractSmartLock "AbstractSmartLock", so it can be used by  @ref Qore::Thread::Condition "Condition" objects.\n\n
    The Mutex class implements a mutual-exclusion lock for thread locking. Like all %Qore thread primitives, objects of this class participate in deadlock detection and throw exceptions when threading errors occur (ex: unlocking a Mutex object locked by another thread, etc). See individual methods for more information on exceptions thrown.\n\n
    See the  @ref Qore::Thread::AutoLock "AutoLock" class for a class that assists in exception-safe Mutex locking.\n\n
    Additionally, the @ref on_exit "on_exit statement" can provide exception-safe unlocking at the lexical block level for Mutex objects as in the following example:
    @code{.py}
{
    m.lock();
    on_exit
        m.unlock();

    # ... when this block exits the lock will be released, even in the
    #     case of return statements or exceptions
}
    @endcode

    @par Thread Resource Handling
    The Mutex class manages the lock as a @ref thread_resources "thread resource"; if the lock is not released when the
    thread exits (or when @ref Qore::throw_thread_resource_exceptions() or
    @ref Qore::throw_thread_resource_exceptions_to_mark() is called), the lock is released automatically and a
    \c LOCK-ERROR exception is thrown describing the situation.\n\n
    Being an builtin class, the Mutex class does not
    inherit @ref Qore::Thread::AbstractThreadResource "AbstractThreadResource" explicitly as a part of the exported API,
    and the internal @ref Qore::Thread::AbstractThreadResource::cleanup() "AbstractThreadResource::cleanup()" method
    cannot be overridden or suppressed.

    @note This class is not available with the @ref PO_NO_THREAD_CLASSES parse option
 */
qclass Mutex [dom=THREAD_CLASS; arg=SmartMutex* m; ns=Qore::Thread; vparent=AbstractSmartLock];

//! Creates the Mutex object
/** @par Example:
    @code{.py}
Mutex mutex();
    @endcode
 */
Mutex::constructor() {
    self->setPrivate(CID_MUTEX, new SmartMutex);
}

//! Destroys the object
/** Note that it is a programming error to delete this object while other threads are blocked on it; in this case an exception is thrown in the deleting thread, and in each thread blocked on this object when it is deleted.

    @par Example:
    @code{.py}
delete mutex;
    @endcode

    @throw LOCK-ERROR Object deleted while other threads blocked on it
 */
Mutex::destructor() {
    m->destructor(xsink);
    m->deref(xsink);
}

//! Creates a new Mutex object, not based on the original
/** @par Example:
    @code{.py}
Mutex nm = m.copy();
    @endcode
*/
Mutex::copy() {
    self->setPrivate(CID_MUTEX, new SmartMutex);
}

//! Locks the Mutex object; blocks if the lock is already held
/** To release the Mutex, use @ref Mutex::unlock()

    @par Example:
    @code{.py}
mutex.lock();
    @endcode

    @throw LOCK-ERROR lock called twice in the same thread, object deleted in another thread, etc
    @throw THREAD-DEADLOCK a deadlock was detected while trying to acquire the lock
*/
nothing Mutex::lock() {
    m->grab(xsink);
}

//! Locks the Mutex object; blocks if the lock is already held
/** An optional timeout value may be passed to this method, giving a time in milliseconds to wait for the lock to become free. Like all %Qore functions and methods taking @ref timeout_type "timeout" values, a @ref relative_dates "relative time value" may be passed instead of an integer to make the timeout units clear

    To release the Mutex, use @ref Mutex::unlock()

    @par Example:
    @code{.py}
if (mutex.lock(1250ms))
    throw "TIMEOUT-ERROR", "lock acquisition timed out after 1.25s";
    @endcode

    @param timeout_ms a @ref timeout_type "timeout" value to wait to acquire the lock; integers are interpreted as milliseconds; @ref relative_dates "relative date/time values" are interpreted literally (with a resolution of milliseconds)

    @return returns -1 for error, 0 for success

    @throw LOCK-ERROR lock called twice in the same thread, object deleted in another thread, etc
    @throw THREAD-DEADLOCK a deadlock was detected while trying to acquire the lock
*/
int Mutex::lock(timeout timeout_ms) {
    return m->grab(xsink, timeout_ms);
}

//! Acquires the lock only if it is not already held; returns 0 for success (lock acquired) or -1 if the call would block
/**
    @return 0 for success (lock acquired) or -1 if the call would block (lock not acquired)

    @par Example:
    @code{.py}
int i = mutex.trylock();
    @endcode

    @throw LOCK-ERROR object deleted in another thread, etc
    @throw THREAD-DEADLOCK a deadlock was detected while trying to acquire the lock
*/
int Mutex::trylock() {
    return m->tryGrab();
}

//! Unlocks the Mutex object; wakes up one thread if any threads are blocked on this lock
/**
    @par Example:
    @code{.py}
mutex.unlock();
    @endcode

    @throw LOCK-ERROR unlock called by a thread that does not own the lock or the lock is not locked, object deleted in another thread, etc
*/
nothing Mutex::unlock() {
    m->release(xsink);
}
