class Mutex

new lock locked? sleep try_lock unlock

Mutex implements a simple semaphore that can be used to coordinate access to shared data from multiple concurrent threads.

Example:

require 'thread'
semaphore = Mutex.new

a = Thread.new {
  semaphore.synchronize {
    # access shared resource
  }
}

b = Thread.new {
  semaphore.synchronize {
    # access shared resource
  }
}

Public Class Methods

new → mutex click to toggle source

Creates a new Mutex

static VALUE
mutex_initialize(VALUE self)
{
    return self;
}

Public Instance Methods

lock → self click to toggle source

Attempts to grab the lock and waits if it isn't available. Raises ThreadError if mutex was locked by the current thread.

VALUE
rb_mutex_lock(VALUE self)
{

    if (rb_mutex_trylock(self) == Qfalse) {
        rb_mutex_t *mutex;
        rb_thread_t *th = GET_THREAD();
        GetMutexPtr(self, mutex);

        if (mutex->th == GET_THREAD()) {
            rb_raise(rb_eThreadError, "deadlock; recursive locking");
        }

        while (mutex->th != th) {
            int interrupted;
            enum rb_thread_status prev_status = th->status;
            int timeout_ms = 0;
            struct rb_unblock_callback oldubf;

            set_unblock_function(th, lock_interrupt, mutex, &oldubf);
            th->status = THREAD_STOPPED_FOREVER;
            th->locking_mutex = self;

            native_mutex_lock(&mutex->lock);
            th->vm->sleeper++;
            /*
             * Carefully! while some contended threads are in lock_func(),
             * vm->sleepr is unstable value. we have to avoid both deadlock
             * and busy loop.
             */
            if (vm_living_thread_num(th->vm) == th->vm->sleeper) {
                timeout_ms = 100;
            }
            GVL_UNLOCK_BEGIN();
            interrupted = lock_func(th, mutex, timeout_ms);
            native_mutex_unlock(&mutex->lock);
            GVL_UNLOCK_END();

            reset_unblock_function(th, &oldubf);

            th->locking_mutex = Qfalse;
            if (mutex->th && interrupted == 2) {
                rb_check_deadlock(th->vm);
            }
            if (th->status == THREAD_STOPPED_FOREVER) {
                th->status = prev_status;
            }
            th->vm->sleeper--;

            if (mutex->th == th) mutex_locked(th, self);

            if (interrupted) {
                RUBY_VM_CHECK_INTS();
            }
        }
    }
    return self;
}
locked? → true or false click to toggle source

Returns true if this lock is currently held by some thread.

VALUE
rb_mutex_locked_p(VALUE self)
{
    rb_mutex_t *mutex;
    GetMutexPtr(self, mutex);
    return mutex->th ? Qtrue : Qfalse;
}
sleep(timeout = nil) → number click to toggle source

Releases the lock and sleeps timeout seconds if it is given and non-nil or forever. Raises ThreadError if mutex wasn't locked by the current thread.

static VALUE
mutex_sleep(int argc, VALUE *argv, VALUE self)
{
    VALUE timeout;

    rb_scan_args(argc, argv, "01", &timeout);
    return rb_mutex_sleep(self, timeout);
}
try_lock → true or false click to toggle source

Attempts to obtain the lock and returns immediately. Returns true if the lock was granted.

VALUE
rb_mutex_trylock(VALUE self)
{
    rb_mutex_t *mutex;
    VALUE locked = Qfalse;
    GetMutexPtr(self, mutex);

    native_mutex_lock(&mutex->lock);
    if (mutex->th == 0) {
        mutex->th = GET_THREAD();
        locked = Qtrue;

        mutex_locked(GET_THREAD(), self);
    }
    native_mutex_unlock(&mutex->lock);

    return locked;
}
unlock → self click to toggle source

Releases the lock. Raises ThreadError if mutex wasn't locked by the current thread.

VALUE
rb_mutex_unlock(VALUE self)
{
    const char *err;
    rb_mutex_t *mutex;
    GetMutexPtr(self, mutex);

    err = rb_mutex_unlock_th(mutex, GET_THREAD());
    if (err) rb_raise(rb_eThreadError, "%s", err);

    return self;
}