From 8e38292891518d22860c26a95b15992f1958f50b Mon Sep 17 00:00:00 2001 From: Nick Robinson Date: Mon, 12 Aug 2019 16:36:10 +0100 Subject: [PATCH 1/7] Update syntax for Julia v1 --- .gitignore | 1 + .travis.yml | 18 +++-- Project.toml | 13 ++++ REQUIRE | 1 - src/ReadWriteLocks.jl | 48 ++++++------- test/REQUIRE | 1 - test/runtests.jl | 8 ++- test/singlethreaded.jl | 149 +++++++++++++++++++++-------------------- 8 files changed, 127 insertions(+), 112 deletions(-) create mode 100644 Project.toml delete mode 100644 REQUIRE delete mode 100644 test/REQUIRE diff --git a/.gitignore b/.gitignore index 8c960ec..97e6a6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.jl.cov *.jl.*.cov *.jl.mem +/Manifest.toml diff --git a/.travis.yml b/.travis.yml index 1121e9a..890561b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,12 +4,20 @@ os: - linux - osx julia: - - 0.4 + - 1.0 + - 1.1 + - 1.2 + - 1.3 - nightly +matrix: + allow_failures: + - julia: nightly + fast_finish: true notifications: email: false -script: - - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi - - julia -e 'Pkg.clone(pwd()); Pkg.build("ReadWriteLocks"); Pkg.test("ReadWriteLocks"; coverage=true)' after_success: - - julia -e 'cd(Pkg.dir("ReadWriteLocks")); Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())' + - julia -e ' + using Pkg; + Pkg.add("Coverage"); + using Coverage; + Codecov.submit(process_folder())' diff --git a/Project.toml b/Project.toml new file mode 100644 index 0000000..cbafd38 --- /dev/null +++ b/Project.toml @@ -0,0 +1,13 @@ +name = "ReadWriteLocks" +uuid = "c9a1755d-84d0-58e0-8f48-2f3129b1cb7a" +authors = "Invenia Technical Computing" +version = "0.1.0" + +[compat] +julia = "1" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/REQUIRE b/REQUIRE deleted file mode 100644 index d5d6467..0000000 --- a/REQUIRE +++ /dev/null @@ -1 +0,0 @@ -julia 0.4 diff --git a/src/ReadWriteLocks.jl b/src/ReadWriteLocks.jl index 1a18ac3..08e1e04 100644 --- a/src/ReadWriteLocks.jl +++ b/src/ReadWriteLocks.jl @@ -1,42 +1,32 @@ module ReadWriteLocks -export ReadWriteLock, read_lock, write_lock, lock!, unlock! - -if isdefined(Base, :lock!) - import Base: lock! -end - -if isdefined(Base, :unlock!) - import Base: unlock! +using Base: lock, unlock +if VERSION < v"1.2.0-" + using Base.Threads: AbstractLock +else + using Base: AbstractLock end -if !isdefined(Base, :Mutex) - typealias Mutex ReentrantLock - - lock!(x::Mutex) = lock(x) - unlock!(x::Mutex) = unlock(x) -end - -abstract _Lock +export ReadWriteLock, read_lock, write_lock, lock!, unlock! -immutable ReadLock{T<:_Lock} +struct ReadLock{T<:AbstractLock} rwlock::T end -immutable WriteLock{T<:_Lock} +struct WriteLock{T<:AbstractLock} rwlock::T end -type ReadWriteLock <: _Lock +mutable struct ReadWriteLock <: AbstractLock readers::Int writer::Bool - lock::Mutex # reentrant mutex + lock::ReentrantLock # reentrant mutex condition::Condition read_lock::ReadLock write_lock::WriteLock function ReadWriteLock() - rwlock = new(false, 0, Mutex(), Condition()) + rwlock = new(false, 0, ReentrantLock(), Condition()) rwlock.read_lock = ReadLock(rwlock) rwlock.write_lock = WriteLock(rwlock) @@ -49,7 +39,7 @@ write_lock(rwlock::ReadWriteLock) = rwlock.write_lock function lock!(read_lock::ReadLock) rwlock = read_lock.rwlock - lock!(rwlock.lock) + lock(rwlock.lock) try while rwlock.writer @@ -58,7 +48,7 @@ function lock!(read_lock::ReadLock) rwlock.readers += 1 finally - unlock!(rwlock.lock) + unlock(rwlock.lock) end return nothing @@ -66,7 +56,7 @@ end function unlock!(read_lock::ReadLock) rwlock = read_lock.rwlock - lock!(rwlock.lock) + lock(rwlock.lock) try rwlock.readers -= 1 @@ -74,7 +64,7 @@ function unlock!(read_lock::ReadLock) notify(rwlock.condition; all=true) end finally - unlock!(rwlock.lock) + unlock(rwlock.lock) end return nothing @@ -82,7 +72,7 @@ end function lock!(write_lock::WriteLock) rwlock = write_lock.rwlock - lock!(rwlock.lock) + lock(rwlock.lock) try while rwlock.readers > 0 || rwlock.writer @@ -91,7 +81,7 @@ function lock!(write_lock::WriteLock) rwlock.writer = true finally - unlock!(rwlock.lock) + unlock(rwlock.lock) end return nothing @@ -99,13 +89,13 @@ end function unlock!(write_lock::WriteLock) rwlock = write_lock.rwlock - lock!(rwlock.lock) + lock(rwlock.lock) try rwlock.writer = false notify(rwlock.condition; all=true) finally - unlock!(rwlock.lock) + unlock(rwlock.lock) end return nothing diff --git a/test/REQUIRE b/test/REQUIRE deleted file mode 100644 index bc3e234..0000000 --- a/test/REQUIRE +++ /dev/null @@ -1 +0,0 @@ -FactCheck diff --git a/test/runtests.jl b/test/runtests.jl index 962b460..cc28767 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,2 +1,6 @@ -# write your own tests here -include("singlethreaded.jl") +using ReadWriteLocks +using Test + +@testset "ReadWriteLocks" begin + include("singlethreaded.jl") +end diff --git a/test/singlethreaded.jl b/test/singlethreaded.jl index 5564f03..281b16b 100644 --- a/test/singlethreaded.jl +++ b/test/singlethreaded.jl @@ -1,58 +1,59 @@ -using FactCheck using ReadWriteLocks +using Test -facts("Single-threaded tests") do - context("Initialization") do +@testset "Single-threaded tests" begin + @testset "Initialization" begin rwlock = ReadWriteLock() - @fact rwlock.read_lock.rwlock --> rwlock - @fact rwlock.write_lock.rwlock --> rwlock - @fact rwlock.readers --> 0 - @fact rwlock.writer --> false - @fact read_lock(rwlock) --> rwlock.read_lock - @fact write_lock(rwlock) --> rwlock.write_lock + @test rwlock.read_lock.rwlock == rwlock + @test rwlock.write_lock.rwlock == rwlock + @test rwlock.readers == 0 + @test rwlock.writer == false + @test read_lock(rwlock) == rwlock.read_lock + @test write_lock(rwlock) == rwlock.write_lock end end -facts("Two-threaded tests") do - context("Read locks") do +@testset "Two-threaded tests" begin + @testset "Read locks" begin NUM_LOCKS = 10 - context("$NUM_LOCKS locks") do + @testset "$NUM_LOCKS locks" begin rwlock = ReadWriteLock() rlock = read_lock(rwlock) @async begin - @fact rwlock.writer --> false - @fact rwlock.readers --> 0 + @test rwlock.writer == false + @test rwlock.readers == 0 for i = 1:NUM_LOCKS - @fact lock!(rlock) --> nothing - @fact rwlock.readers --> i + @test lock!(rlock) == nothing + # lock(rlock) + @test rwlock.readers == i end end sleep(1) - @fact rwlock.readers --> NUM_LOCKS + @test rwlock.readers == NUM_LOCKS @async begin - @fact rwlock.writer --> false - @fact rwlock.readers --> NUM_LOCKS - for i = NUM_LOCKS:-1:1 - @fact unlock!(rlock) --> nothing - @fact rwlock.readers --> i - 1 + @test rwlock.writer == false + @test rwlock.readers == NUM_LOCKS + for i in NUM_LOCKS:-1:1 + @test unlock!(rlock) == nothing + @test rwlock.readers == i - 1 end - @fact rwlock.readers --> 0 + @test rwlock.readers == 0 end sleep(1) - @fact rwlock.readers --> 0 + @test rwlock.readers == 0 end end - context("Write locks") do - context("two locks") do + @testset "Write locks" begin + @testset "two locks" begin rwlock = ReadWriteLock() wlock = write_lock(rwlock) @@ -60,30 +61,30 @@ facts("Two-threaded tests") do put!(c, :pretest) @async begin - @fact rwlock.writer --> false - @fact rwlock.readers --> 0 - @fact lock!(wlock) --> nothing - @fact rwlock.writer --> true - @fact rwlock.readers --> 0 - @fact take!(c) --> :pretest + @test rwlock.writer == false + @test rwlock.readers == 0 + @test lock!(wlock) == nothing + @test rwlock.writer == true + @test rwlock.readers == 0 + @test take!(c) == :pretest put!(c, :prelock) - @fact lock!(wlock) --> nothing + @test lock!(wlock) == nothing # this code should never be reached - @fact take!(c) --> :prelock + @test take!(c) == :prelock put!(c, :postlock) end sleep(1) - @fact take!(c) --> :prelock + @test take!(c) == :prelock put!(c, :posttest) - @fact rwlock.writer --> true - @fact rwlock.readers --> 0 + @test rwlock.writer == true + @test rwlock.readers == 0 end - context("unlock") do + @testset "unlock" begin rwlock = ReadWriteLock() wlock = write_lock(rwlock) @@ -91,30 +92,30 @@ facts("Two-threaded tests") do put!(c, :pretest) @async begin - @fact rwlock.writer --> false - @fact rwlock.readers --> 0 - @fact lock!(wlock) --> nothing - @fact rwlock.writer --> true - @fact rwlock.readers --> 0 - @fact take!(c) --> :pretest + @test rwlock.writer == false + @test rwlock.readers == 0 + @test lock!(wlock) == nothing + @test rwlock.writer == true + @test rwlock.readers == 0 + @test take!(c) == :pretest put!(c, :preunlock) - @fact unlock!(wlock) --> nothing - @fact take!(c) --> :preunlock + @test unlock!(wlock) == nothing + @test take!(c) == :preunlock put!(c, :postunlock) end sleep(1) - @fact take!(c) --> :postunlock + @test take!(c) == :postunlock put!(c, :posttest) - @fact rwlock.writer --> false - @fact rwlock.readers --> 0 + @test rwlock.writer == false + @test rwlock.readers == 0 end end - context("read and write locks") do - context("write then read") do + @testset "read and write locks" begin + @testset "write then read" begin rwlock = ReadWriteLock() wlock = write_lock(rwlock) rlock = read_lock(rwlock) @@ -123,30 +124,30 @@ facts("Two-threaded tests") do put!(c, :pretest) @async begin - @fact rwlock.writer --> false - @fact rwlock.readers --> 0 - @fact lock!(wlock) --> nothing - @fact rwlock.writer --> true - @fact rwlock.readers --> 0 - @fact take!(c) --> :pretest + @test rwlock.writer == false + @test rwlock.readers == 0 + @test lock!(wlock) == nothing + @test rwlock.writer == true + @test rwlock.readers == 0 + @test take!(c) == :pretest put!(c, :prelock) - @fact lock!(rlock) --> nothing + @test lock!(rlock) == nothing # this code should never be reached - @fact take!(c) --> :prelock + @test take!(c) == :prelock put!(c, :postlock) end sleep(1) - @fact take!(c) --> :prelock + @test take!(c) == :prelock put!(c, :posttest) - @fact rwlock.writer --> true - @fact rwlock.readers --> 0 + @test rwlock.writer == true + @test rwlock.readers == 0 end - context("read then write") do + @testset "read then write" begin rwlock = ReadWriteLock() wlock = write_lock(rwlock) rlock = read_lock(rwlock) @@ -155,27 +156,27 @@ facts("Two-threaded tests") do put!(c, :pretest) @async begin - @fact rwlock.writer --> false - @fact rwlock.readers --> 0 - @fact lock!(rlock) --> nothing - @fact rwlock.writer --> false - @fact rwlock.readers --> 1 - @fact take!(c) --> :pretest + @test rwlock.writer == false + @test rwlock.readers == 0 + @test lock!(rlock) == nothing + @test rwlock.writer == false + @test rwlock.readers == 1 + @test take!(c) == :pretest put!(c, :prelock) - @fact lock!(wlock) --> nothing + @test lock!(wlock) == nothing # this code should never be reached - @fact take!(c) --> :prelock + @test take!(c) == :prelock put!(c, :postlock) end sleep(1) - @fact take!(c) --> :prelock + @test take!(c) == :prelock put!(c, :posttest) - @fact rwlock.writer --> false - @fact rwlock.readers --> 1 + @test rwlock.writer == false + @test rwlock.readers == 1 end end From 2ec75998d13b3de32d6d1c7f5ade6958f556a8cb Mon Sep 17 00:00:00 2001 From: Nick Robinson Date: Mon, 12 Aug 2019 16:54:15 +0100 Subject: [PATCH 2/7] Try using Travis for Windows --- .travis.yml | 1 + appveyor.yml | 34 ---------------------------------- 2 files changed, 1 insertion(+), 34 deletions(-) delete mode 100644 appveyor.yml diff --git a/.travis.yml b/.travis.yml index 890561b..398008b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: julia os: - linux - osx + - windows julia: - 1.0 - 1.1 diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 26612de..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,34 +0,0 @@ -environment: - matrix: - - JULIAVERSION: "julialang/bin/winnt/x86/0.4/julia-0.4-latest-win32.exe" - - JULIAVERSION: "julialang/bin/winnt/x64/0.4/julia-0.4-latest-win64.exe" - - JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe" - - JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe" - -branches: - only: - - master - - /release-.*/ - -notifications: - - provider: Email - on_build_success: false - on_build_failure: false - on_build_status_changed: false - -install: -# Download most recent Julia Windows binary - - ps: (new-object net.webclient).DownloadFile( - $("http://s3.amazonaws.com/"+$env:JULIAVERSION), - "C:\projects\julia-binary.exe") -# Run installer silently, output to C:\projects\julia - - C:\projects\julia-binary.exe /S /D=C:\projects\julia - -build_script: -# Need to convert from shallow to complete for Pkg.clone to work - - IF EXIST .git\shallow (git fetch --unshallow) - - C:\projects\julia\bin\julia -e "versioninfo(); - Pkg.clone(pwd(), \"ReadWriteLocks\"); Pkg.build(\"ReadWriteLocks\")" - -test_script: - - C:\projects\julia\bin\julia --check-bounds=yes -e "Pkg.test(\"ReadWriteLocks\")" From 17ba6bac0d5fe53b5576375db13623c79fcd2a34 Mon Sep 17 00:00:00 2001 From: Nick Robinson Date: Mon, 12 Aug 2019 16:54:30 +0100 Subject: [PATCH 3/7] Use exact Julia version in check --- src/ReadWriteLocks.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReadWriteLocks.jl b/src/ReadWriteLocks.jl index 08e1e04..fbe7c28 100644 --- a/src/ReadWriteLocks.jl +++ b/src/ReadWriteLocks.jl @@ -1,7 +1,7 @@ module ReadWriteLocks using Base: lock, unlock -if VERSION < v"1.2.0-" +if VERSION < v"1.2.0-DEV.28" using Base.Threads: AbstractLock else using Base: AbstractLock From c8649f21d5f7cc7d63bcc817945fe650750784be Mon Sep 17 00:00:00 2001 From: Nick Robinson Date: Mon, 12 Aug 2019 16:57:20 +0100 Subject: [PATCH 4/7] Consolidate tests into one file --- test/runtests.jl | 186 ++++++++++++++++++++++++++++++++++++++++- test/singlethreaded.jl | 183 ---------------------------------------- 2 files changed, 183 insertions(+), 186 deletions(-) delete mode 100644 test/singlethreaded.jl diff --git a/test/runtests.jl b/test/runtests.jl index cc28767..131b899 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,186 @@ using ReadWriteLocks using Test -@testset "ReadWriteLocks" begin - include("singlethreaded.jl") -end +@testset "ReadWriteLock" begin + +@testset "Single-threaded tests" begin + @testset "Initialization" begin + rwlock = ReadWriteLock() + + @test rwlock.read_lock.rwlock == rwlock + @test rwlock.write_lock.rwlock == rwlock + @test rwlock.readers == 0 + @test rwlock.writer == false + @test read_lock(rwlock) == rwlock.read_lock + @test write_lock(rwlock) == rwlock.write_lock + end +end # Single-threaded + +@testset "Two-threaded tests" begin + @testset "Read locks" begin + NUM_LOCKS = 10 + + @testset "$NUM_LOCKS locks" begin + rwlock = ReadWriteLock() + rlock = read_lock(rwlock) + + @async begin + @test rwlock.writer == false + @test rwlock.readers == 0 + for i = 1:NUM_LOCKS + @test lock!(rlock) == nothing + # lock(rlock) + @test rwlock.readers == i + end + end + + sleep(1) + + @test rwlock.readers == NUM_LOCKS + + @async begin + @test rwlock.writer == false + @test rwlock.readers == NUM_LOCKS + for i in NUM_LOCKS:-1:1 + @test unlock!(rlock) == nothing + @test rwlock.readers == i - 1 + end + @test rwlock.readers == 0 + end + + sleep(1) + + @test rwlock.readers == 0 + end + end + + @testset "Write locks" begin + @testset "two locks" begin + rwlock = ReadWriteLock() + wlock = write_lock(rwlock) + + c = Channel{Symbol}(1) + put!(c, :pretest) + + @async begin + @test rwlock.writer == false + @test rwlock.readers == 0 + @test lock!(wlock) == nothing + @test rwlock.writer == true + @test rwlock.readers == 0 + @test take!(c) == :pretest + put!(c, :prelock) + @test lock!(wlock) == nothing + + # this code should never be reached + @test take!(c) == :prelock + put!(c, :postlock) + end + + sleep(1) + + @test take!(c) == :prelock + put!(c, :posttest) + + @test rwlock.writer == true + @test rwlock.readers == 0 + end + + @testset "unlock" begin + rwlock = ReadWriteLock() + wlock = write_lock(rwlock) + + c = Channel{Symbol}(1) + put!(c, :pretest) + + @async begin + @test rwlock.writer == false + @test rwlock.readers == 0 + @test lock!(wlock) == nothing + @test rwlock.writer == true + @test rwlock.readers == 0 + @test take!(c) == :pretest + put!(c, :preunlock) + @test unlock!(wlock) == nothing + @test take!(c) == :preunlock + put!(c, :postunlock) + end + + sleep(1) + + @test take!(c) == :postunlock + put!(c, :posttest) + + @test rwlock.writer == false + @test rwlock.readers == 0 + end + end + + @testset "read and write locks" begin + @testset "write then read" begin + rwlock = ReadWriteLock() + wlock = write_lock(rwlock) + rlock = read_lock(rwlock) + + c = Channel{Symbol}(1) + put!(c, :pretest) + + @async begin + @test rwlock.writer == false + @test rwlock.readers == 0 + @test lock!(wlock) == nothing + @test rwlock.writer == true + @test rwlock.readers == 0 + @test take!(c) == :pretest + put!(c, :prelock) + @test lock!(rlock) == nothing + + # this code should never be reached + @test take!(c) == :prelock + put!(c, :postlock) + end + + sleep(1) + + @test take!(c) == :prelock + put!(c, :posttest) + + @test rwlock.writer == true + @test rwlock.readers == 0 + end + + @testset "read then write" begin + rwlock = ReadWriteLock() + wlock = write_lock(rwlock) + rlock = read_lock(rwlock) + + c = Channel{Symbol}(1) + put!(c, :pretest) + + @async begin + @test rwlock.writer == false + @test rwlock.readers == 0 + @test lock!(rlock) == nothing + @test rwlock.writer == false + @test rwlock.readers == 1 + @test take!(c) == :pretest + put!(c, :prelock) + @test lock!(wlock) == nothing + + # this code should never be reached + @test take!(c) == :prelock + put!(c, :postlock) + end + + sleep(1) + + @test take!(c) == :prelock + put!(c, :posttest) + + @test rwlock.writer == false + @test rwlock.readers == 1 + end + end +end # Two-threaded + +end # ReadWriteLocks diff --git a/test/singlethreaded.jl b/test/singlethreaded.jl deleted file mode 100644 index 281b16b..0000000 --- a/test/singlethreaded.jl +++ /dev/null @@ -1,183 +0,0 @@ -using ReadWriteLocks -using Test - -@testset "Single-threaded tests" begin - @testset "Initialization" begin - rwlock = ReadWriteLock() - - @test rwlock.read_lock.rwlock == rwlock - @test rwlock.write_lock.rwlock == rwlock - @test rwlock.readers == 0 - @test rwlock.writer == false - @test read_lock(rwlock) == rwlock.read_lock - @test write_lock(rwlock) == rwlock.write_lock - end -end - -@testset "Two-threaded tests" begin - @testset "Read locks" begin - NUM_LOCKS = 10 - - @testset "$NUM_LOCKS locks" begin - rwlock = ReadWriteLock() - rlock = read_lock(rwlock) - - @async begin - @test rwlock.writer == false - @test rwlock.readers == 0 - for i = 1:NUM_LOCKS - @test lock!(rlock) == nothing - # lock(rlock) - @test rwlock.readers == i - end - end - - sleep(1) - - @test rwlock.readers == NUM_LOCKS - - @async begin - @test rwlock.writer == false - @test rwlock.readers == NUM_LOCKS - for i in NUM_LOCKS:-1:1 - @test unlock!(rlock) == nothing - @test rwlock.readers == i - 1 - end - @test rwlock.readers == 0 - end - - sleep(1) - - @test rwlock.readers == 0 - end - end - - @testset "Write locks" begin - @testset "two locks" begin - rwlock = ReadWriteLock() - wlock = write_lock(rwlock) - - c = Channel{Symbol}(1) - put!(c, :pretest) - - @async begin - @test rwlock.writer == false - @test rwlock.readers == 0 - @test lock!(wlock) == nothing - @test rwlock.writer == true - @test rwlock.readers == 0 - @test take!(c) == :pretest - put!(c, :prelock) - @test lock!(wlock) == nothing - - # this code should never be reached - @test take!(c) == :prelock - put!(c, :postlock) - end - - sleep(1) - - @test take!(c) == :prelock - put!(c, :posttest) - - @test rwlock.writer == true - @test rwlock.readers == 0 - end - - @testset "unlock" begin - rwlock = ReadWriteLock() - wlock = write_lock(rwlock) - - c = Channel{Symbol}(1) - put!(c, :pretest) - - @async begin - @test rwlock.writer == false - @test rwlock.readers == 0 - @test lock!(wlock) == nothing - @test rwlock.writer == true - @test rwlock.readers == 0 - @test take!(c) == :pretest - put!(c, :preunlock) - @test unlock!(wlock) == nothing - @test take!(c) == :preunlock - put!(c, :postunlock) - end - - sleep(1) - - @test take!(c) == :postunlock - put!(c, :posttest) - - @test rwlock.writer == false - @test rwlock.readers == 0 - end - end - - @testset "read and write locks" begin - @testset "write then read" begin - rwlock = ReadWriteLock() - wlock = write_lock(rwlock) - rlock = read_lock(rwlock) - - c = Channel{Symbol}(1) - put!(c, :pretest) - - @async begin - @test rwlock.writer == false - @test rwlock.readers == 0 - @test lock!(wlock) == nothing - @test rwlock.writer == true - @test rwlock.readers == 0 - @test take!(c) == :pretest - put!(c, :prelock) - @test lock!(rlock) == nothing - - # this code should never be reached - @test take!(c) == :prelock - put!(c, :postlock) - end - - sleep(1) - - @test take!(c) == :prelock - put!(c, :posttest) - - @test rwlock.writer == true - @test rwlock.readers == 0 - end - - @testset "read then write" begin - rwlock = ReadWriteLock() - wlock = write_lock(rwlock) - rlock = read_lock(rwlock) - - c = Channel{Symbol}(1) - put!(c, :pretest) - - @async begin - @test rwlock.writer == false - @test rwlock.readers == 0 - @test lock!(rlock) == nothing - @test rwlock.writer == false - @test rwlock.readers == 1 - @test take!(c) == :pretest - put!(c, :prelock) - @test lock!(wlock) == nothing - - # this code should never be reached - @test take!(c) == :prelock - put!(c, :postlock) - end - - sleep(1) - - @test take!(c) == :prelock - put!(c, :posttest) - - @test rwlock.writer == false - @test rwlock.readers == 1 - end - end - -end From 2fad52557ee482396d7002b443fb7f67b5fcec2c Mon Sep 17 00:00:00 2001 From: Nick Robinson Date: Mon, 12 Aug 2019 17:56:59 +0100 Subject: [PATCH 5/7] Parameterise ReadWriteLock by inner Lock type --- src/ReadWriteLocks.jl | 15 +++++++++++---- test/runtests.jl | 5 +++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/ReadWriteLocks.jl b/src/ReadWriteLocks.jl index fbe7c28..b1d0868 100644 --- a/src/ReadWriteLocks.jl +++ b/src/ReadWriteLocks.jl @@ -17,16 +17,23 @@ struct WriteLock{T<:AbstractLock} rwlock::T end -mutable struct ReadWriteLock <: AbstractLock +# Need Julia VERSION > v"1.2.0-DEV.28` to have `ReentrantLock <: AbstractLock` +LockTypes = Union{AbstractLock, ReentrantLock} +mutable struct ReadWriteLock{L<:LockTypes} <: AbstractLock readers::Int writer::Bool - lock::ReentrantLock # reentrant mutex + lock::L # reentrant mutex condition::Condition read_lock::ReadLock write_lock::WriteLock - function ReadWriteLock() - rwlock = new(false, 0, ReentrantLock(), Condition()) + function ReadWriteLock( + readers::Int=0, + writer::Bool=false, + lock::L=ReentrantLock(), + condition::Condition=Condition(), + ) where L <: LockTypes + rwlock = new{L}(readers, writer, lock, condition) rwlock.read_lock = ReadLock(rwlock) rwlock.write_lock = WriteLock(rwlock) diff --git a/test/runtests.jl b/test/runtests.jl index 131b899..89a27f6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,6 +3,11 @@ using Test @testset "ReadWriteLock" begin +@testset "Constructor" begin + @test ReadWriteLock() isa ReadWriteLock{ReentrantLock} + @test ReadWriteLock(0, false, Threads.Mutex()) isa ReadWriteLock{Threads.Mutex} +end + @testset "Single-threaded tests" begin @testset "Initialization" begin rwlock = ReadWriteLock() From b006f13c1c58bee2dbc0c5bd5734367a33f88bc6 Mon Sep 17 00:00:00 2001 From: Nick Robinson Date: Mon, 12 Aug 2019 17:14:26 +0100 Subject: [PATCH 6/7] Remove `@test` from (un)lock! calls --- test/runtests.jl | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 89a27f6..b70c9f3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,8 +33,7 @@ end # Single-threaded @test rwlock.writer == false @test rwlock.readers == 0 for i = 1:NUM_LOCKS - @test lock!(rlock) == nothing - # lock(rlock) + lock!(rlock) @test rwlock.readers == i end end @@ -47,7 +46,7 @@ end # Single-threaded @test rwlock.writer == false @test rwlock.readers == NUM_LOCKS for i in NUM_LOCKS:-1:1 - @test unlock!(rlock) == nothing + unlock!(rlock) @test rwlock.readers == i - 1 end @test rwlock.readers == 0 @@ -70,12 +69,12 @@ end # Single-threaded @async begin @test rwlock.writer == false @test rwlock.readers == 0 - @test lock!(wlock) == nothing + lock!(rlock) @test rwlock.writer == true @test rwlock.readers == 0 @test take!(c) == :pretest put!(c, :prelock) - @test lock!(wlock) == nothing + lock!(rlock) # this code should never be reached @test take!(c) == :prelock @@ -101,12 +100,12 @@ end # Single-threaded @async begin @test rwlock.writer == false @test rwlock.readers == 0 - @test lock!(wlock) == nothing + lock!(rlock) @test rwlock.writer == true @test rwlock.readers == 0 @test take!(c) == :pretest put!(c, :preunlock) - @test unlock!(wlock) == nothing + unlock!(rlock) @test take!(c) == :preunlock put!(c, :postunlock) end @@ -133,12 +132,12 @@ end # Single-threaded @async begin @test rwlock.writer == false @test rwlock.readers == 0 - @test lock!(wlock) == nothing + lock!(wlock) @test rwlock.writer == true @test rwlock.readers == 0 @test take!(c) == :pretest put!(c, :prelock) - @test lock!(rlock) == nothing + lock!(wlock) # this code should never be reached @test take!(c) == :prelock @@ -165,12 +164,12 @@ end # Single-threaded @async begin @test rwlock.writer == false @test rwlock.readers == 0 - @test lock!(rlock) == nothing + lock!(wlock) @test rwlock.writer == false @test rwlock.readers == 1 @test take!(c) == :pretest put!(c, :prelock) - @test lock!(wlock) == nothing + lock!(wlock) # this code should never be reached @test take!(c) == :prelock From 85ceec7c27be1387cb68ab17c05c4311a4fdffaf Mon Sep 17 00:00:00 2001 From: Nick Robinson Date: Sat, 17 Aug 2019 12:56:37 +0100 Subject: [PATCH 7/7] Revert "Remove `@test` from (un)lock! calls" This reverts commit b006f13c1c58bee2dbc0c5bd5734367a33f88bc6. --- test/runtests.jl | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index b70c9f3..89a27f6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,7 +33,8 @@ end # Single-threaded @test rwlock.writer == false @test rwlock.readers == 0 for i = 1:NUM_LOCKS - lock!(rlock) + @test lock!(rlock) == nothing + # lock(rlock) @test rwlock.readers == i end end @@ -46,7 +47,7 @@ end # Single-threaded @test rwlock.writer == false @test rwlock.readers == NUM_LOCKS for i in NUM_LOCKS:-1:1 - unlock!(rlock) + @test unlock!(rlock) == nothing @test rwlock.readers == i - 1 end @test rwlock.readers == 0 @@ -69,12 +70,12 @@ end # Single-threaded @async begin @test rwlock.writer == false @test rwlock.readers == 0 - lock!(rlock) + @test lock!(wlock) == nothing @test rwlock.writer == true @test rwlock.readers == 0 @test take!(c) == :pretest put!(c, :prelock) - lock!(rlock) + @test lock!(wlock) == nothing # this code should never be reached @test take!(c) == :prelock @@ -100,12 +101,12 @@ end # Single-threaded @async begin @test rwlock.writer == false @test rwlock.readers == 0 - lock!(rlock) + @test lock!(wlock) == nothing @test rwlock.writer == true @test rwlock.readers == 0 @test take!(c) == :pretest put!(c, :preunlock) - unlock!(rlock) + @test unlock!(wlock) == nothing @test take!(c) == :preunlock put!(c, :postunlock) end @@ -132,12 +133,12 @@ end # Single-threaded @async begin @test rwlock.writer == false @test rwlock.readers == 0 - lock!(wlock) + @test lock!(wlock) == nothing @test rwlock.writer == true @test rwlock.readers == 0 @test take!(c) == :pretest put!(c, :prelock) - lock!(wlock) + @test lock!(rlock) == nothing # this code should never be reached @test take!(c) == :prelock @@ -164,12 +165,12 @@ end # Single-threaded @async begin @test rwlock.writer == false @test rwlock.readers == 0 - lock!(wlock) + @test lock!(rlock) == nothing @test rwlock.writer == false @test rwlock.readers == 1 @test take!(c) == :pretest put!(c, :prelock) - lock!(wlock) + @test lock!(wlock) == nothing # this code should never be reached @test take!(c) == :prelock