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 } }
Creates a new Mutex
static VALUE
mutex_initialize(VALUE self)
{
return self;
}
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) {
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 last_thread = 0;
struct rb_unblock_callback oldubf;
set_unblock_function(th, lock_interrupt, mutex, &oldubf);
th->status = THREAD_STOPPED_FOREVER;
th->vm->sleeper++;
th->locking_mutex = self;
if (vm_living_thread_num(th->vm) == th->vm->sleeper) {
last_thread = 1;
}
th->transition_for_lock = 1;
BLOCKING_REGION_CORE({
interrupted = lock_func(th, mutex, last_thread);
});
th->transition_for_lock = 0;
remove_signal_thread_list(th);
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;
}
Returns true if this lock is currently held by some thread.
VALUE
rb_mutex_locked_p(VALUE self)
{
mutex_t *mutex;
GetMutexPtr(self, mutex);
return mutex->th ? Qtrue : Qfalse;
}
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);
}
Obtains a lock, runs the block, and releases the lock when the block completes. See the example under Mutex.
# File prelude.rb, line 7
def synchronize
self.lock
begin
yield
ensure
self.unlock rescue nil
end
end
Attempts to obtain the lock and returns immediately. Returns true if the lock was granted.
VALUE
rb_mutex_trylock(VALUE self)
{
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;
}
Releases the lock. Raises ThreadError if mutex wasn’t locked by the current thread.
VALUE
rb_mutex_unlock(VALUE self)
{
const char *err;
mutex_t *mutex;
GetMutexPtr(self, mutex);
err = mutex_unlock(mutex, GET_THREAD());
if (err) rb_raise(rb_eThreadError, "%s", err);
return self;
}