Class: Mutex

Inherits:
Object show all
Defined in:
thread.c,
thread.c

Overview

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
  }
}

Instance Method Summary collapse

Constructor Details

#newObject

Creates a new Mutex


4111
4112
4113
4114
4115
# File 'thread.c', line 4111

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

Instance Method Details

#lockself

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

Returns:

  • (self)

4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
# File 'thread.c', line 4239

VALUE
rb_mutex_lock(VALUE self)
{
    rb_thread_t *th = GET_THREAD();
    rb_mutex_t *mutex;
    GetMutexPtr(self, mutex);

    /* When running trap handler */
    if (!mutex->allow_trap && th->interrupt_mask & TRAP_INTERRUPT_MASK) {
	rb_raise(rb_eThreadError, "can't be called from trap context");
    }

    if (rb_mutex_trylock(self) == Qfalse) {
	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;
	    volatile int timeout_ms = 0;
	    struct rb_unblock_callback oldubf;

	    set_unblock_function(th, lock_interrupt, mutex, &oldubf, FALSE);
	    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) &&
		!patrol_thread) {
		timeout_ms = 100;
		patrol_thread = th;
	    }

	    GVL_UNLOCK_BEGIN();
	    interrupted = lock_func(th, mutex, (int)timeout_ms);
	    native_mutex_unlock(&mutex->lock);
	    GVL_UNLOCK_END();

	    if (patrol_thread == th)
		patrol_thread = NULL;

	    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_BLOCKING(th);
	    }
	}
    }
    return self;
}

#locked?Boolean

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

Returns:

  • (Boolean)

Returns:

  • (Boolean)

4129
4130
4131
4132
4133
4134
4135
# File 'thread.c', line 4129

VALUE
rb_mutex_locked_p(VALUE self)
{
    rb_mutex_t *mutex;
    GetMutexPtr(self, mutex);
    return mutex->th ? Qtrue : Qfalse;
}

#owned?Boolean

Returns true if this lock is currently held by current thread. This API is experimental, and subject to change.

Returns:

  • (Boolean)

Returns:

  • (Boolean)

4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
# File 'thread.c', line 4315

VALUE
rb_mutex_owned_p(VALUE self)
{
    VALUE owned = Qfalse;
    rb_thread_t *th = GET_THREAD();
    rb_mutex_t *mutex;

    GetMutexPtr(self, mutex);

    if (mutex->th == th)
	owned = Qtrue;

    return owned;
}

#sleep(timeout = nil) ⇒ Numeric

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.

Note that this method can wakeup without explicit Thread#wakeup call. For example, receiving signal and so on.

Returns:


4448
4449
4450
4451
4452
4453
4454
4455
# File 'thread.c', line 4448

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);
}

#synchronize { ... } ⇒ Object

Obtains a lock, runs the block, and releases the lock when the block completes. See the example under Mutex.

Yields:


4479
4480
4481
4482
4483
4484
4485
4486
4487
# File 'thread.c', line 4479

static VALUE
rb_mutex_synchronize_m(VALUE self, VALUE args)
{
    if (!rb_block_given_p()) {
	rb_raise(rb_eThreadError, "must be called with a block");
    }

    return rb_mutex_synchronize(self, rb_yield, Qnil);
}

#try_lockBoolean

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

Returns:

  • (Boolean)

4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
# File 'thread.c', line 4156

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;
}

#unlockself

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

Returns:

  • (self)

4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
# File 'thread.c', line 4370

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

    /* When running trap handler */
    if (!mutex->allow_trap && GET_THREAD()->interrupt_mask & TRAP_INTERRUPT_MASK) {
	rb_raise(rb_eThreadError, "can't be called from trap context");
    }

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

    return self;
}