Skip to content

Commit

Permalink
Add RWLock#(acquire|release)_(read|write)_lock methods
Browse files Browse the repository at this point in the history
  • Loading branch information
64kramsystem committed Nov 17, 2017
1 parent c9b17c0 commit 2bbc8a8
Show file tree
Hide file tree
Showing 2 changed files with 260 additions and 0 deletions.
112 changes: 112 additions & 0 deletions vm/concurrent_rw_lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,118 @@ func builtinConcurrentRWLockClassMethods() []*BuiltinMethodObject {
// Instance methods -----------------------------------------------------
func builtinConcurrentRWLockInstanceMethods() []*BuiltinMethodObject {
return []*BuiltinMethodObject{
{
// Acquires a read lock.
//
// ```Ruby
// lock = Concurrent::RWLock.new
// lock.acquire_read_lock
// # critical section
// lock.release_read_lock
//
// @return [nil]
// ```
Name: "acquire_read_lock",
Fn: func(receiver Object, sourceLine int) builtinMethodBody {
return func(t *thread, args []Object, blockFrame *normalCallFrame) Object {
if len(args) != 0 {
t.callFrameStack.pop()

return t.vm.initErrorObject(errors.ArgumentError, sourceLine, "Expected 0 arguments, got %d", len(args))
}

lockObject := receiver.(*ConcurrentRWLockObject)

lockObject.mutex.RLock();

return NULL
}
},
},
{
// Acquires a write lock.
//
// ```Ruby
// lock = Concurrent::RWLock.new
// lock.acquire_write_lock
// # critical section
// lock.release_write_lock
//
// @return [nil]
// ```
Name: "acquire_write_lock",
Fn: func(receiver Object, sourceLine int) builtinMethodBody {
return func(t *thread, args []Object, blockFrame *normalCallFrame) Object {
if len(args) != 0 {
t.callFrameStack.pop()

return t.vm.initErrorObject(errors.ArgumentError, sourceLine, "Expected 0 arguments, got %d", len(args))
}

lockObject := receiver.(*ConcurrentRWLockObject)

lockObject.mutex.Lock();

return NULL
}
},
},
{
// Releases a read lock.
//
// ```Ruby
// lock = Concurrent::RWLock.new
// lock.acquire_read_lock
// # critical section
// lock.release_read_lock
//
// @return [nil]
// ```
Name: "release_read_lock",
Fn: func(receiver Object, sourceLine int) builtinMethodBody {
return func(t *thread, args []Object, blockFrame *normalCallFrame) Object {
if len(args) != 0 {
t.callFrameStack.pop()

return t.vm.initErrorObject(errors.ArgumentError, sourceLine, "Expected 0 arguments, got %d", len(args))
}

lockObject := receiver.(*ConcurrentRWLockObject)

lockObject.mutex.RUnlock();

return NULL
}
},
},
{
// Releases a write lock.
//
// ```Ruby
// lock = Concurrent::RWLock.new
// lock.acquire_write_lock
// # critical section
// lock.release_write_lock
//
// @return [nil]
// ```
Name: "release_write_lock",
Fn: func(receiver Object, sourceLine int) builtinMethodBody {
return func(t *thread, args []Object, blockFrame *normalCallFrame) Object {
if len(args) != 0 {
t.callFrameStack.pop()

return t.vm.initErrorObject(errors.ArgumentError, sourceLine, "Expected 0 arguments, got %d", len(args))
}

lockObject := receiver.(*ConcurrentRWLockObject)

lockObject.mutex.Unlock();

return NULL
}
},
},
{
// Executes the block with a read lock.
// The lock is freed upon exiting the block.
Expand Down
148 changes: 148 additions & 0 deletions vm/concurrent_rw_lock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,74 @@ func TestRWLockNewMethodFail(t *testing.T) {
}
}

func TestRWLockAcquireReadLockMethodFail(t *testing.T) {
testsFail := []errorTestCase{
{`
require 'concurrent/rw_lock'
Concurrent::RWLock.new.acquire_read_lock(5)
`, "ArgumentError: Expected 0 arguments, got 1", 3, 1},
}

for i, tt := range testsFail {
v := initTestVM()
evaluated := v.testEval(t, tt.input, getFilename())
checkError(t, i, evaluated, tt.expected, getFilename(), tt.errorLine)
v.checkCFP(t, i, tt.expectedCFP)
v.checkSP(t, i, 1)
}
}

func TestRWLockReleaseReadLockMethodFail(t *testing.T) {
testsFail := []errorTestCase{
{`
require 'concurrent/rw_lock'
Concurrent::RWLock.new.release_read_lock(5)
`, "ArgumentError: Expected 0 arguments, got 1", 3, 1},
}

for i, tt := range testsFail {
v := initTestVM()
evaluated := v.testEval(t, tt.input, getFilename())
checkError(t, i, evaluated, tt.expected, getFilename(), tt.errorLine)
v.checkCFP(t, i, tt.expectedCFP)
v.checkSP(t, i, 1)
}
}

func TestRWLockAcquireWriteLockMethodFail(t *testing.T) {
testsFail := []errorTestCase{
{`
require 'concurrent/rw_lock'
Concurrent::RWLock.new.acquire_write_lock(5)
`, "ArgumentError: Expected 0 arguments, got 1", 3, 1},
}

for i, tt := range testsFail {
v := initTestVM()
evaluated := v.testEval(t, tt.input, getFilename())
checkError(t, i, evaluated, tt.expected, getFilename(), tt.errorLine)
v.checkCFP(t, i, tt.expectedCFP)
v.checkSP(t, i, 1)
}
}

func TestRWLockReleaseWriteLockMethodFail(t *testing.T) {
testsFail := []errorTestCase{
{`
require 'concurrent/rw_lock'
Concurrent::RWLock.new.release_write_lock(5)
`, "ArgumentError: Expected 0 arguments, got 1", 3, 1},
}

for i, tt := range testsFail {
v := initTestVM()
evaluated := v.testEval(t, tt.input, getFilename())
checkError(t, i, evaluated, tt.expected, getFilename(), tt.errorLine)
v.checkCFP(t, i, tt.expectedCFP)
v.checkSP(t, i, 1)
}
}

func TestRWLockWithReadLockMethodFail(t *testing.T) {
testsFail := []errorTestCase{
{`
Expand Down Expand Up @@ -68,6 +136,48 @@ func TestRWLockWithWriteLockMethodFail(t *testing.T) {

// Isolated lock types

func TestRWLockAcquireAndReleaseReadLock(t *testing.T) {
code := `
require 'concurrent/rw_lock'
lock = Concurrent::RWLock.new
lock.acquire_read_lock
lock.release_read_lock
"completed"
`

expected := "completed"

v := initTestVM()
evaluated := v.testEval(t, code, getFilename())
testStringObject(t, i, evaluated, expected)
v.checkCFP(t, i, 0)
v.checkSP(t, i, 1)
}

func TestRWLockAcquireAndReleaseWriteLock(t *testing.T) {
code := `
require 'concurrent/rw_lock'
lock = Concurrent::RWLock.new
lock.acquire_write_lock
lock.release_write_lock
"completed"
`

expected := "completed"

v := initTestVM()
evaluated := v.testEval(t, code, getFilename())
testStringObject(t, i, evaluated, expected)
v.checkCFP(t, i, 0)
v.checkSP(t, i, 1)
}

func TestRWLockWithReadLockMethod(t *testing.T) {
code := `
require 'concurrent/rw_lock'
Expand Down Expand Up @@ -116,6 +226,44 @@ func TestRWLockWithWriteLockMethod(t *testing.T) {

// Mixed locks (functional tests)

func TestRWLockAcquireAndReleaseLocksReadBlocksWriteNoRaceDetection(t *testing.T) {
skipRWLockTestIfRaceDetectionEnabled(t)

code := `
require 'concurrent/rw_lock'
lock = Concurrent::RWLock.new
message = nil
thread do
lock.acquire_read_lock
sleep 2
message ||= "thread 1"
lock.release_read_lock
end
thread do
sleep 1
lock.acquire_write_lock
message ||= "thread 2"
lock.release_write_lock
end
sleep 3
lock.with_read_lock do
message
end
`

expected := "thread 1"

v := initTestVM()
evaluated := v.testEval(t, code, getFilename())
testStringObject(t, i, evaluated, expected)
v.checkCFP(t, i, 0)
v.checkSP(t, i, 1)
}

func TestRWLockWithReadLockReadBlocksWriteNoRaceDetection(t *testing.T) {
skipRWLockTestIfRaceDetectionEnabled(t)

Expand Down

0 comments on commit 2bbc8a8

Please sign in to comment.