From 30388ff5919df33e7184719dfc6c0d9cb110b2e2 Mon Sep 17 00:00:00 2001 From: Jamie Liu Date: Thu, 2 Apr 2020 11:55:55 -0700 Subject: Rename files in //pkg/sync to better reflect what they contain. PiperOrigin-RevId: 304447031 --- pkg/sync/BUILD | 10 +- pkg/sync/downgradable_rwmutex_test.go | 205 -------------------------------- pkg/sync/downgradable_rwmutex_unsafe.go | 198 ------------------------------ pkg/sync/mutex_test.go | 71 +++++++++++ pkg/sync/mutex_unsafe.go | 49 ++++++++ pkg/sync/rwmutex_test.go | 205 ++++++++++++++++++++++++++++++++ pkg/sync/rwmutex_unsafe.go | 198 ++++++++++++++++++++++++++++++ pkg/sync/sync.go | 7 ++ pkg/sync/syncutil.go | 7 -- pkg/sync/tmutex_test.go | 71 ----------- pkg/sync/tmutex_unsafe.go | 49 -------- 11 files changed, 535 insertions(+), 535 deletions(-) delete mode 100644 pkg/sync/downgradable_rwmutex_test.go delete mode 100644 pkg/sync/downgradable_rwmutex_unsafe.go create mode 100644 pkg/sync/mutex_test.go create mode 100644 pkg/sync/mutex_unsafe.go create mode 100644 pkg/sync/rwmutex_test.go create mode 100644 pkg/sync/rwmutex_unsafe.go create mode 100644 pkg/sync/sync.go delete mode 100644 pkg/sync/syncutil.go delete mode 100644 pkg/sync/tmutex_test.go delete mode 100644 pkg/sync/tmutex_unsafe.go (limited to 'pkg/sync') diff --git a/pkg/sync/BUILD b/pkg/sync/BUILD index 5340cf0d6..0e35d7d17 100644 --- a/pkg/sync/BUILD +++ b/pkg/sync/BUILD @@ -31,13 +31,13 @@ go_library( name = "sync", srcs = [ "aliases.go", - "downgradable_rwmutex_unsafe.go", "memmove_unsafe.go", + "mutex_unsafe.go", "norace_unsafe.go", "race_unsafe.go", + "rwmutex_unsafe.go", "seqcount.go", - "syncutil.go", - "tmutex_unsafe.go", + "sync.go", ], ) @@ -45,9 +45,9 @@ go_test( name = "sync_test", size = "small", srcs = [ - "downgradable_rwmutex_test.go", + "mutex_test.go", + "rwmutex_test.go", "seqcount_test.go", - "tmutex_test.go", ], library = ":sync", ) diff --git a/pkg/sync/downgradable_rwmutex_test.go b/pkg/sync/downgradable_rwmutex_test.go deleted file mode 100644 index ce667e825..000000000 --- a/pkg/sync/downgradable_rwmutex_test.go +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Copyright 2019 The gVisor Authors. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// GOMAXPROCS=10 go test - -// Copy/pasted from the standard library's sync/rwmutex_test.go, except for the -// addition of downgradingWriter and the renaming of num_iterations to -// numIterations to shut up Golint. - -package sync - -import ( - "fmt" - "runtime" - "sync/atomic" - "testing" -) - -func parallelReader(m *RWMutex, clocked, cunlock, cdone chan bool) { - m.RLock() - clocked <- true - <-cunlock - m.RUnlock() - cdone <- true -} - -func doTestParallelReaders(numReaders, gomaxprocs int) { - runtime.GOMAXPROCS(gomaxprocs) - var m RWMutex - clocked := make(chan bool) - cunlock := make(chan bool) - cdone := make(chan bool) - for i := 0; i < numReaders; i++ { - go parallelReader(&m, clocked, cunlock, cdone) - } - // Wait for all parallel RLock()s to succeed. - for i := 0; i < numReaders; i++ { - <-clocked - } - for i := 0; i < numReaders; i++ { - cunlock <- true - } - // Wait for the goroutines to finish. - for i := 0; i < numReaders; i++ { - <-cdone - } -} - -func TestParallelReaders(t *testing.T) { - defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) - doTestParallelReaders(1, 4) - doTestParallelReaders(3, 4) - doTestParallelReaders(4, 2) -} - -func reader(rwm *RWMutex, numIterations int, activity *int32, cdone chan bool) { - for i := 0; i < numIterations; i++ { - rwm.RLock() - n := atomic.AddInt32(activity, 1) - if n < 1 || n >= 10000 { - panic(fmt.Sprintf("wlock(%d)\n", n)) - } - for i := 0; i < 100; i++ { - } - atomic.AddInt32(activity, -1) - rwm.RUnlock() - } - cdone <- true -} - -func writer(rwm *RWMutex, numIterations int, activity *int32, cdone chan bool) { - for i := 0; i < numIterations; i++ { - rwm.Lock() - n := atomic.AddInt32(activity, 10000) - if n != 10000 { - panic(fmt.Sprintf("wlock(%d)\n", n)) - } - for i := 0; i < 100; i++ { - } - atomic.AddInt32(activity, -10000) - rwm.Unlock() - } - cdone <- true -} - -func downgradingWriter(rwm *RWMutex, numIterations int, activity *int32, cdone chan bool) { - for i := 0; i < numIterations; i++ { - rwm.Lock() - n := atomic.AddInt32(activity, 10000) - if n != 10000 { - panic(fmt.Sprintf("wlock(%d)\n", n)) - } - for i := 0; i < 100; i++ { - } - atomic.AddInt32(activity, -10000) - rwm.DowngradeLock() - n = atomic.AddInt32(activity, 1) - if n < 1 || n >= 10000 { - panic(fmt.Sprintf("wlock(%d)\n", n)) - } - for i := 0; i < 100; i++ { - } - n = atomic.AddInt32(activity, -1) - rwm.RUnlock() - } - cdone <- true -} - -func HammerDowngradableRWMutex(gomaxprocs, numReaders, numIterations int) { - runtime.GOMAXPROCS(gomaxprocs) - // Number of active readers + 10000 * number of active writers. - var activity int32 - var rwm RWMutex - cdone := make(chan bool) - go writer(&rwm, numIterations, &activity, cdone) - go downgradingWriter(&rwm, numIterations, &activity, cdone) - var i int - for i = 0; i < numReaders/2; i++ { - go reader(&rwm, numIterations, &activity, cdone) - } - go writer(&rwm, numIterations, &activity, cdone) - go downgradingWriter(&rwm, numIterations, &activity, cdone) - for ; i < numReaders; i++ { - go reader(&rwm, numIterations, &activity, cdone) - } - // Wait for the 4 writers and all readers to finish. - for i := 0; i < 4+numReaders; i++ { - <-cdone - } -} - -func TestDowngradableRWMutex(t *testing.T) { - defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) - n := 1000 - if testing.Short() { - n = 5 - } - HammerDowngradableRWMutex(1, 1, n) - HammerDowngradableRWMutex(1, 3, n) - HammerDowngradableRWMutex(1, 10, n) - HammerDowngradableRWMutex(4, 1, n) - HammerDowngradableRWMutex(4, 3, n) - HammerDowngradableRWMutex(4, 10, n) - HammerDowngradableRWMutex(10, 1, n) - HammerDowngradableRWMutex(10, 3, n) - HammerDowngradableRWMutex(10, 10, n) - HammerDowngradableRWMutex(10, 5, n) -} - -func TestRWDoubleTryLock(t *testing.T) { - var rwm RWMutex - if !rwm.TryLock() { - t.Fatal("failed to aquire lock") - } - if rwm.TryLock() { - t.Fatal("unexpectedly succeeded in aquiring locked mutex") - } -} - -func TestRWTryLockAfterLock(t *testing.T) { - var rwm RWMutex - rwm.Lock() - if rwm.TryLock() { - t.Fatal("unexpectedly succeeded in aquiring locked mutex") - } -} - -func TestRWTryLockUnlock(t *testing.T) { - var rwm RWMutex - if !rwm.TryLock() { - t.Fatal("failed to aquire lock") - } - rwm.Unlock() - if !rwm.TryLock() { - t.Fatal("failed to aquire lock after unlock") - } -} - -func TestTryRLockAfterLock(t *testing.T) { - var rwm RWMutex - rwm.Lock() - if rwm.TryRLock() { - t.Fatal("unexpectedly succeeded in aquiring locked mutex") - } -} - -func TestTryLockAfterRLock(t *testing.T) { - var rwm RWMutex - rwm.RLock() - if rwm.TryLock() { - t.Fatal("unexpectedly succeeded in aquiring locked mutex") - } -} - -func TestDoubleTryRLock(t *testing.T) { - var rwm RWMutex - if !rwm.TryRLock() { - t.Fatal("failed to aquire lock") - } - if !rwm.TryRLock() { - t.Fatal("failed to read aquire read locked lock") - } -} diff --git a/pkg/sync/downgradable_rwmutex_unsafe.go b/pkg/sync/downgradable_rwmutex_unsafe.go deleted file mode 100644 index ea6cdc447..000000000 --- a/pkg/sync/downgradable_rwmutex_unsafe.go +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Copyright 2019 The gVisor Authors. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.13 -// +build !go1.15 - -// Check go:linkname function signatures when updating Go version. - -// This is mostly copied from the standard library's sync/rwmutex.go. -// -// Happens-before relationships indicated to the race detector: -// - Unlock -> Lock (via writerSem) -// - Unlock -> RLock (via readerSem) -// - RUnlock -> Lock (via writerSem) -// - DowngradeLock -> RLock (via readerSem) - -package sync - -import ( - "sync/atomic" - "unsafe" -) - -//go:linkname runtimeSemacquire sync.runtime_Semacquire -func runtimeSemacquire(s *uint32) - -//go:linkname runtimeSemrelease sync.runtime_Semrelease -func runtimeSemrelease(s *uint32, handoff bool, skipframes int) - -// RWMutex is identical to sync.RWMutex, but adds the DowngradeLock, -// TryLock and TryRLock methods. -type RWMutex struct { - w Mutex // held if there are pending writers - writerSem uint32 // semaphore for writers to wait for completing readers - readerSem uint32 // semaphore for readers to wait for completing writers - readerCount int32 // number of pending readers - readerWait int32 // number of departing readers -} - -const rwmutexMaxReaders = 1 << 30 - -// TryRLock locks rw for reading. It returns true if it succeeds and false -// otherwise. It does not block. -func (rw *RWMutex) TryRLock() bool { - if RaceEnabled { - RaceDisable() - } - for { - rc := atomic.LoadInt32(&rw.readerCount) - if rc < 0 { - if RaceEnabled { - RaceEnable() - } - return false - } - if !atomic.CompareAndSwapInt32(&rw.readerCount, rc, rc+1) { - continue - } - if RaceEnabled { - RaceEnable() - RaceAcquire(unsafe.Pointer(&rw.readerSem)) - } - return true - } -} - -// RLock locks rw for reading. -func (rw *RWMutex) RLock() { - if RaceEnabled { - RaceDisable() - } - if atomic.AddInt32(&rw.readerCount, 1) < 0 { - // A writer is pending, wait for it. - runtimeSemacquire(&rw.readerSem) - } - if RaceEnabled { - RaceEnable() - RaceAcquire(unsafe.Pointer(&rw.readerSem)) - } -} - -// RUnlock undoes a single RLock call. -func (rw *RWMutex) RUnlock() { - if RaceEnabled { - RaceReleaseMerge(unsafe.Pointer(&rw.writerSem)) - RaceDisable() - } - if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 { - if r+1 == 0 || r+1 == -rwmutexMaxReaders { - panic("RUnlock of unlocked RWMutex") - } - // A writer is pending. - if atomic.AddInt32(&rw.readerWait, -1) == 0 { - // The last reader unblocks the writer. - runtimeSemrelease(&rw.writerSem, false, 0) - } - } - if RaceEnabled { - RaceEnable() - } -} - -// TryLock locks rw for writing. It returns true if it succeeds and false -// otherwise. It does not block. -func (rw *RWMutex) TryLock() bool { - if RaceEnabled { - RaceDisable() - } - // First, resolve competition with other writers. - if !rw.w.TryLock() { - if RaceEnabled { - RaceEnable() - } - return false - } - // Only proceed if there are no readers. - if !atomic.CompareAndSwapInt32(&rw.readerCount, 0, -rwmutexMaxReaders) { - rw.w.Unlock() - if RaceEnabled { - RaceEnable() - } - return false - } - if RaceEnabled { - RaceEnable() - RaceAcquire(unsafe.Pointer(&rw.writerSem)) - } - return true -} - -// Lock locks rw for writing. -func (rw *RWMutex) Lock() { - if RaceEnabled { - RaceDisable() - } - // First, resolve competition with other writers. - rw.w.Lock() - // Announce to readers there is a pending writer. - r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders - // Wait for active readers. - if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { - runtimeSemacquire(&rw.writerSem) - } - if RaceEnabled { - RaceEnable() - RaceAcquire(unsafe.Pointer(&rw.writerSem)) - } -} - -// Unlock unlocks rw for writing. -func (rw *RWMutex) Unlock() { - if RaceEnabled { - RaceRelease(unsafe.Pointer(&rw.writerSem)) - RaceRelease(unsafe.Pointer(&rw.readerSem)) - RaceDisable() - } - // Announce to readers there is no active writer. - r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) - if r >= rwmutexMaxReaders { - panic("Unlock of unlocked RWMutex") - } - // Unblock blocked readers, if any. - for i := 0; i < int(r); i++ { - runtimeSemrelease(&rw.readerSem, false, 0) - } - // Allow other writers to proceed. - rw.w.Unlock() - if RaceEnabled { - RaceEnable() - } -} - -// DowngradeLock atomically unlocks rw for writing and locks it for reading. -func (rw *RWMutex) DowngradeLock() { - if RaceEnabled { - RaceRelease(unsafe.Pointer(&rw.readerSem)) - RaceDisable() - } - // Announce to readers there is no active writer and one additional reader. - r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders+1) - if r >= rwmutexMaxReaders+1 { - panic("DowngradeLock of unlocked RWMutex") - } - // Unblock blocked readers, if any. Note that this loop starts as 1 since r - // includes this goroutine. - for i := 1; i < int(r); i++ { - runtimeSemrelease(&rw.readerSem, false, 0) - } - // Allow other writers to proceed to rw.w.Lock(). Note that they will still - // block on rw.writerSem since at least this reader exists, such that - // DowngradeLock() is atomic with the previous write lock. - rw.w.Unlock() - if RaceEnabled { - RaceEnable() - } -} diff --git a/pkg/sync/mutex_test.go b/pkg/sync/mutex_test.go new file mode 100644 index 000000000..0838248b4 --- /dev/null +++ b/pkg/sync/mutex_test.go @@ -0,0 +1,71 @@ +// Copyright 2019 The gVisor Authors. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sync + +import ( + "sync" + "testing" + "unsafe" +) + +// TestStructSize verifies that syncMutex's size hasn't drifted from the +// standard library's version. +// +// The correctness of this package relies on these remaining in sync. +func TestStructSize(t *testing.T) { + const ( + got = unsafe.Sizeof(syncMutex{}) + want = unsafe.Sizeof(sync.Mutex{}) + ) + if got != want { + t.Errorf("got sizeof(syncMutex) = %d, want = sizeof(sync.Mutex) = %d", got, want) + } +} + +// TestFieldValues verifies that the semantics of syncMutex.state from the +// standard library's implementation. +// +// The correctness of this package relies on these remaining in sync. +func TestFieldValues(t *testing.T) { + var m Mutex + m.Lock() + if got := *m.state(); got != mutexLocked { + t.Errorf("got locked sync.Mutex.state = %d, want = %d", got, mutexLocked) + } + m.Unlock() + if got := *m.state(); got != mutexUnlocked { + t.Errorf("got unlocked sync.Mutex.state = %d, want = %d", got, mutexUnlocked) + } +} + +func TestDoubleTryLock(t *testing.T) { + var m Mutex + if !m.TryLock() { + t.Fatal("failed to aquire lock") + } + if m.TryLock() { + t.Fatal("unexpectedly succeeded in aquiring locked mutex") + } +} + +func TestTryLockAfterLock(t *testing.T) { + var m Mutex + m.Lock() + if m.TryLock() { + t.Fatal("unexpectedly succeeded in aquiring locked mutex") + } +} + +func TestTryLockUnlock(t *testing.T) { + var m Mutex + if !m.TryLock() { + t.Fatal("failed to aquire lock") + } + m.Unlock() + if !m.TryLock() { + t.Fatal("failed to aquire lock after unlock") + } +} diff --git a/pkg/sync/mutex_unsafe.go b/pkg/sync/mutex_unsafe.go new file mode 100644 index 000000000..3dd15578b --- /dev/null +++ b/pkg/sync/mutex_unsafe.go @@ -0,0 +1,49 @@ +// Copyright 2019 The gVisor Authors. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.13 +// +build !go1.15 + +// When updating the build constraint (above), check that syncMutex matches the +// standard library sync.Mutex definition. + +package sync + +import ( + "sync" + "sync/atomic" + "unsafe" +) + +// Mutex is a try lock. +type Mutex struct { + sync.Mutex +} + +type syncMutex struct { + state int32 + sema uint32 +} + +func (m *Mutex) state() *int32 { + return &(*syncMutex)(unsafe.Pointer(&m.Mutex)).state +} + +const ( + mutexUnlocked = 0 + mutexLocked = 1 +) + +// TryLock tries to aquire the mutex. It returns true if it succeeds and false +// otherwise. TryLock does not block. +func (m *Mutex) TryLock() bool { + if atomic.CompareAndSwapInt32(m.state(), mutexUnlocked, mutexLocked) { + if RaceEnabled { + RaceAcquire(unsafe.Pointer(&m.Mutex)) + } + return true + } + return false +} diff --git a/pkg/sync/rwmutex_test.go b/pkg/sync/rwmutex_test.go new file mode 100644 index 000000000..ce667e825 --- /dev/null +++ b/pkg/sync/rwmutex_test.go @@ -0,0 +1,205 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Copyright 2019 The gVisor Authors. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// GOMAXPROCS=10 go test + +// Copy/pasted from the standard library's sync/rwmutex_test.go, except for the +// addition of downgradingWriter and the renaming of num_iterations to +// numIterations to shut up Golint. + +package sync + +import ( + "fmt" + "runtime" + "sync/atomic" + "testing" +) + +func parallelReader(m *RWMutex, clocked, cunlock, cdone chan bool) { + m.RLock() + clocked <- true + <-cunlock + m.RUnlock() + cdone <- true +} + +func doTestParallelReaders(numReaders, gomaxprocs int) { + runtime.GOMAXPROCS(gomaxprocs) + var m RWMutex + clocked := make(chan bool) + cunlock := make(chan bool) + cdone := make(chan bool) + for i := 0; i < numReaders; i++ { + go parallelReader(&m, clocked, cunlock, cdone) + } + // Wait for all parallel RLock()s to succeed. + for i := 0; i < numReaders; i++ { + <-clocked + } + for i := 0; i < numReaders; i++ { + cunlock <- true + } + // Wait for the goroutines to finish. + for i := 0; i < numReaders; i++ { + <-cdone + } +} + +func TestParallelReaders(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) + doTestParallelReaders(1, 4) + doTestParallelReaders(3, 4) + doTestParallelReaders(4, 2) +} + +func reader(rwm *RWMutex, numIterations int, activity *int32, cdone chan bool) { + for i := 0; i < numIterations; i++ { + rwm.RLock() + n := atomic.AddInt32(activity, 1) + if n < 1 || n >= 10000 { + panic(fmt.Sprintf("wlock(%d)\n", n)) + } + for i := 0; i < 100; i++ { + } + atomic.AddInt32(activity, -1) + rwm.RUnlock() + } + cdone <- true +} + +func writer(rwm *RWMutex, numIterations int, activity *int32, cdone chan bool) { + for i := 0; i < numIterations; i++ { + rwm.Lock() + n := atomic.AddInt32(activity, 10000) + if n != 10000 { + panic(fmt.Sprintf("wlock(%d)\n", n)) + } + for i := 0; i < 100; i++ { + } + atomic.AddInt32(activity, -10000) + rwm.Unlock() + } + cdone <- true +} + +func downgradingWriter(rwm *RWMutex, numIterations int, activity *int32, cdone chan bool) { + for i := 0; i < numIterations; i++ { + rwm.Lock() + n := atomic.AddInt32(activity, 10000) + if n != 10000 { + panic(fmt.Sprintf("wlock(%d)\n", n)) + } + for i := 0; i < 100; i++ { + } + atomic.AddInt32(activity, -10000) + rwm.DowngradeLock() + n = atomic.AddInt32(activity, 1) + if n < 1 || n >= 10000 { + panic(fmt.Sprintf("wlock(%d)\n", n)) + } + for i := 0; i < 100; i++ { + } + n = atomic.AddInt32(activity, -1) + rwm.RUnlock() + } + cdone <- true +} + +func HammerDowngradableRWMutex(gomaxprocs, numReaders, numIterations int) { + runtime.GOMAXPROCS(gomaxprocs) + // Number of active readers + 10000 * number of active writers. + var activity int32 + var rwm RWMutex + cdone := make(chan bool) + go writer(&rwm, numIterations, &activity, cdone) + go downgradingWriter(&rwm, numIterations, &activity, cdone) + var i int + for i = 0; i < numReaders/2; i++ { + go reader(&rwm, numIterations, &activity, cdone) + } + go writer(&rwm, numIterations, &activity, cdone) + go downgradingWriter(&rwm, numIterations, &activity, cdone) + for ; i < numReaders; i++ { + go reader(&rwm, numIterations, &activity, cdone) + } + // Wait for the 4 writers and all readers to finish. + for i := 0; i < 4+numReaders; i++ { + <-cdone + } +} + +func TestDowngradableRWMutex(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) + n := 1000 + if testing.Short() { + n = 5 + } + HammerDowngradableRWMutex(1, 1, n) + HammerDowngradableRWMutex(1, 3, n) + HammerDowngradableRWMutex(1, 10, n) + HammerDowngradableRWMutex(4, 1, n) + HammerDowngradableRWMutex(4, 3, n) + HammerDowngradableRWMutex(4, 10, n) + HammerDowngradableRWMutex(10, 1, n) + HammerDowngradableRWMutex(10, 3, n) + HammerDowngradableRWMutex(10, 10, n) + HammerDowngradableRWMutex(10, 5, n) +} + +func TestRWDoubleTryLock(t *testing.T) { + var rwm RWMutex + if !rwm.TryLock() { + t.Fatal("failed to aquire lock") + } + if rwm.TryLock() { + t.Fatal("unexpectedly succeeded in aquiring locked mutex") + } +} + +func TestRWTryLockAfterLock(t *testing.T) { + var rwm RWMutex + rwm.Lock() + if rwm.TryLock() { + t.Fatal("unexpectedly succeeded in aquiring locked mutex") + } +} + +func TestRWTryLockUnlock(t *testing.T) { + var rwm RWMutex + if !rwm.TryLock() { + t.Fatal("failed to aquire lock") + } + rwm.Unlock() + if !rwm.TryLock() { + t.Fatal("failed to aquire lock after unlock") + } +} + +func TestTryRLockAfterLock(t *testing.T) { + var rwm RWMutex + rwm.Lock() + if rwm.TryRLock() { + t.Fatal("unexpectedly succeeded in aquiring locked mutex") + } +} + +func TestTryLockAfterRLock(t *testing.T) { + var rwm RWMutex + rwm.RLock() + if rwm.TryLock() { + t.Fatal("unexpectedly succeeded in aquiring locked mutex") + } +} + +func TestDoubleTryRLock(t *testing.T) { + var rwm RWMutex + if !rwm.TryRLock() { + t.Fatal("failed to aquire lock") + } + if !rwm.TryRLock() { + t.Fatal("failed to read aquire read locked lock") + } +} diff --git a/pkg/sync/rwmutex_unsafe.go b/pkg/sync/rwmutex_unsafe.go new file mode 100644 index 000000000..ea6cdc447 --- /dev/null +++ b/pkg/sync/rwmutex_unsafe.go @@ -0,0 +1,198 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Copyright 2019 The gVisor Authors. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.13 +// +build !go1.15 + +// Check go:linkname function signatures when updating Go version. + +// This is mostly copied from the standard library's sync/rwmutex.go. +// +// Happens-before relationships indicated to the race detector: +// - Unlock -> Lock (via writerSem) +// - Unlock -> RLock (via readerSem) +// - RUnlock -> Lock (via writerSem) +// - DowngradeLock -> RLock (via readerSem) + +package sync + +import ( + "sync/atomic" + "unsafe" +) + +//go:linkname runtimeSemacquire sync.runtime_Semacquire +func runtimeSemacquire(s *uint32) + +//go:linkname runtimeSemrelease sync.runtime_Semrelease +func runtimeSemrelease(s *uint32, handoff bool, skipframes int) + +// RWMutex is identical to sync.RWMutex, but adds the DowngradeLock, +// TryLock and TryRLock methods. +type RWMutex struct { + w Mutex // held if there are pending writers + writerSem uint32 // semaphore for writers to wait for completing readers + readerSem uint32 // semaphore for readers to wait for completing writers + readerCount int32 // number of pending readers + readerWait int32 // number of departing readers +} + +const rwmutexMaxReaders = 1 << 30 + +// TryRLock locks rw for reading. It returns true if it succeeds and false +// otherwise. It does not block. +func (rw *RWMutex) TryRLock() bool { + if RaceEnabled { + RaceDisable() + } + for { + rc := atomic.LoadInt32(&rw.readerCount) + if rc < 0 { + if RaceEnabled { + RaceEnable() + } + return false + } + if !atomic.CompareAndSwapInt32(&rw.readerCount, rc, rc+1) { + continue + } + if RaceEnabled { + RaceEnable() + RaceAcquire(unsafe.Pointer(&rw.readerSem)) + } + return true + } +} + +// RLock locks rw for reading. +func (rw *RWMutex) RLock() { + if RaceEnabled { + RaceDisable() + } + if atomic.AddInt32(&rw.readerCount, 1) < 0 { + // A writer is pending, wait for it. + runtimeSemacquire(&rw.readerSem) + } + if RaceEnabled { + RaceEnable() + RaceAcquire(unsafe.Pointer(&rw.readerSem)) + } +} + +// RUnlock undoes a single RLock call. +func (rw *RWMutex) RUnlock() { + if RaceEnabled { + RaceReleaseMerge(unsafe.Pointer(&rw.writerSem)) + RaceDisable() + } + if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 { + if r+1 == 0 || r+1 == -rwmutexMaxReaders { + panic("RUnlock of unlocked RWMutex") + } + // A writer is pending. + if atomic.AddInt32(&rw.readerWait, -1) == 0 { + // The last reader unblocks the writer. + runtimeSemrelease(&rw.writerSem, false, 0) + } + } + if RaceEnabled { + RaceEnable() + } +} + +// TryLock locks rw for writing. It returns true if it succeeds and false +// otherwise. It does not block. +func (rw *RWMutex) TryLock() bool { + if RaceEnabled { + RaceDisable() + } + // First, resolve competition with other writers. + if !rw.w.TryLock() { + if RaceEnabled { + RaceEnable() + } + return false + } + // Only proceed if there are no readers. + if !atomic.CompareAndSwapInt32(&rw.readerCount, 0, -rwmutexMaxReaders) { + rw.w.Unlock() + if RaceEnabled { + RaceEnable() + } + return false + } + if RaceEnabled { + RaceEnable() + RaceAcquire(unsafe.Pointer(&rw.writerSem)) + } + return true +} + +// Lock locks rw for writing. +func (rw *RWMutex) Lock() { + if RaceEnabled { + RaceDisable() + } + // First, resolve competition with other writers. + rw.w.Lock() + // Announce to readers there is a pending writer. + r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders + // Wait for active readers. + if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { + runtimeSemacquire(&rw.writerSem) + } + if RaceEnabled { + RaceEnable() + RaceAcquire(unsafe.Pointer(&rw.writerSem)) + } +} + +// Unlock unlocks rw for writing. +func (rw *RWMutex) Unlock() { + if RaceEnabled { + RaceRelease(unsafe.Pointer(&rw.writerSem)) + RaceRelease(unsafe.Pointer(&rw.readerSem)) + RaceDisable() + } + // Announce to readers there is no active writer. + r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) + if r >= rwmutexMaxReaders { + panic("Unlock of unlocked RWMutex") + } + // Unblock blocked readers, if any. + for i := 0; i < int(r); i++ { + runtimeSemrelease(&rw.readerSem, false, 0) + } + // Allow other writers to proceed. + rw.w.Unlock() + if RaceEnabled { + RaceEnable() + } +} + +// DowngradeLock atomically unlocks rw for writing and locks it for reading. +func (rw *RWMutex) DowngradeLock() { + if RaceEnabled { + RaceRelease(unsafe.Pointer(&rw.readerSem)) + RaceDisable() + } + // Announce to readers there is no active writer and one additional reader. + r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders+1) + if r >= rwmutexMaxReaders+1 { + panic("DowngradeLock of unlocked RWMutex") + } + // Unblock blocked readers, if any. Note that this loop starts as 1 since r + // includes this goroutine. + for i := 1; i < int(r); i++ { + runtimeSemrelease(&rw.readerSem, false, 0) + } + // Allow other writers to proceed to rw.w.Lock(). Note that they will still + // block on rw.writerSem since at least this reader exists, such that + // DowngradeLock() is atomic with the previous write lock. + rw.w.Unlock() + if RaceEnabled { + RaceEnable() + } +} diff --git a/pkg/sync/sync.go b/pkg/sync/sync.go new file mode 100644 index 000000000..b16cf5333 --- /dev/null +++ b/pkg/sync/sync.go @@ -0,0 +1,7 @@ +// Copyright 2019 The gVisor Authors. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package sync provides synchronization primitives. +package sync diff --git a/pkg/sync/syncutil.go b/pkg/sync/syncutil.go deleted file mode 100644 index b16cf5333..000000000 --- a/pkg/sync/syncutil.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package sync provides synchronization primitives. -package sync diff --git a/pkg/sync/tmutex_test.go b/pkg/sync/tmutex_test.go deleted file mode 100644 index 0838248b4..000000000 --- a/pkg/sync/tmutex_test.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sync - -import ( - "sync" - "testing" - "unsafe" -) - -// TestStructSize verifies that syncMutex's size hasn't drifted from the -// standard library's version. -// -// The correctness of this package relies on these remaining in sync. -func TestStructSize(t *testing.T) { - const ( - got = unsafe.Sizeof(syncMutex{}) - want = unsafe.Sizeof(sync.Mutex{}) - ) - if got != want { - t.Errorf("got sizeof(syncMutex) = %d, want = sizeof(sync.Mutex) = %d", got, want) - } -} - -// TestFieldValues verifies that the semantics of syncMutex.state from the -// standard library's implementation. -// -// The correctness of this package relies on these remaining in sync. -func TestFieldValues(t *testing.T) { - var m Mutex - m.Lock() - if got := *m.state(); got != mutexLocked { - t.Errorf("got locked sync.Mutex.state = %d, want = %d", got, mutexLocked) - } - m.Unlock() - if got := *m.state(); got != mutexUnlocked { - t.Errorf("got unlocked sync.Mutex.state = %d, want = %d", got, mutexUnlocked) - } -} - -func TestDoubleTryLock(t *testing.T) { - var m Mutex - if !m.TryLock() { - t.Fatal("failed to aquire lock") - } - if m.TryLock() { - t.Fatal("unexpectedly succeeded in aquiring locked mutex") - } -} - -func TestTryLockAfterLock(t *testing.T) { - var m Mutex - m.Lock() - if m.TryLock() { - t.Fatal("unexpectedly succeeded in aquiring locked mutex") - } -} - -func TestTryLockUnlock(t *testing.T) { - var m Mutex - if !m.TryLock() { - t.Fatal("failed to aquire lock") - } - m.Unlock() - if !m.TryLock() { - t.Fatal("failed to aquire lock after unlock") - } -} diff --git a/pkg/sync/tmutex_unsafe.go b/pkg/sync/tmutex_unsafe.go deleted file mode 100644 index 3dd15578b..000000000 --- a/pkg/sync/tmutex_unsafe.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.13 -// +build !go1.15 - -// When updating the build constraint (above), check that syncMutex matches the -// standard library sync.Mutex definition. - -package sync - -import ( - "sync" - "sync/atomic" - "unsafe" -) - -// Mutex is a try lock. -type Mutex struct { - sync.Mutex -} - -type syncMutex struct { - state int32 - sema uint32 -} - -func (m *Mutex) state() *int32 { - return &(*syncMutex)(unsafe.Pointer(&m.Mutex)).state -} - -const ( - mutexUnlocked = 0 - mutexLocked = 1 -) - -// TryLock tries to aquire the mutex. It returns true if it succeeds and false -// otherwise. TryLock does not block. -func (m *Mutex) TryLock() bool { - if atomic.CompareAndSwapInt32(m.state(), mutexUnlocked, mutexLocked) { - if RaceEnabled { - RaceAcquire(unsafe.Pointer(&m.Mutex)) - } - return true - } - return false -} -- cgit v1.2.3