From 5ca758199d77a66f7186450f2e78b0a915d443c4 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 10 Sep 2020 22:15:40 +0200 Subject: [PATCH] replace pkg/locker with github.com/moby/locker Signed-off-by: Sebastiaan van Stijn --- daemon/daemon.go | 2 +- daemon/graphdriver/aufs/aufs.go | 2 +- daemon/graphdriver/devmapper/driver.go | 2 +- .../fuse-overlayfs/fuseoverlayfs.go | 2 +- daemon/graphdriver/overlay/overlay.go | 2 +- daemon/graphdriver/overlay2/overlay.go | 2 +- integration/plugin/logging/helpers_test.go | 2 +- integration/plugin/volumes/helpers_test.go | 2 +- layer/layer_store.go | 2 +- pkg/locker/locker.go | 96 +-------- pkg/locker/locker_test.go | 161 --------------- vendor.conf | 1 + vendor/github.com/moby/locker/LICENSE | 190 ++++++++++++++++++ .../github.com/moby}/locker/README.md | 2 +- vendor/github.com/moby/locker/go.mod | 3 + vendor/github.com/moby/locker/locker.go | 112 +++++++++++ volume/drivers/extpoint.go | 2 +- volume/service/store.go | 5 +- 18 files changed, 326 insertions(+), 264 deletions(-) delete mode 100644 pkg/locker/locker_test.go create mode 100644 vendor/github.com/moby/locker/LICENSE rename {pkg => vendor/github.com/moby}/locker/README.md (97%) create mode 100644 vendor/github.com/moby/locker/go.mod create mode 100644 vendor/github.com/moby/locker/locker.go diff --git a/daemon/daemon.go b/daemon/daemon.go index 98c852b425..f81f45a1ab 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -55,7 +55,6 @@ import ( "github.com/docker/docker/libcontainerd" libcontainerdtypes "github.com/docker/docker/libcontainerd/types" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/truncindex" @@ -68,6 +67,7 @@ import ( "github.com/docker/libnetwork" "github.com/docker/libnetwork/cluster" nwconfig "github.com/docker/libnetwork/config" + "github.com/moby/locker" "github.com/pkg/errors" "golang.org/x/sync/semaphore" ) diff --git a/daemon/graphdriver/aufs/aufs.go b/daemon/graphdriver/aufs/aufs.go index 1238466414..4ecc647bdd 100644 --- a/daemon/graphdriver/aufs/aufs.go +++ b/daemon/graphdriver/aufs/aufs.go @@ -42,8 +42,8 @@ import ( "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/directory" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" "github.com/docker/docker/pkg/system" + "github.com/moby/locker" "github.com/moby/sys/mount" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" diff --git a/daemon/graphdriver/devmapper/driver.go b/daemon/graphdriver/devmapper/driver.go index 5bc280bccd..2cc1bdfa33 100644 --- a/daemon/graphdriver/devmapper/driver.go +++ b/daemon/graphdriver/devmapper/driver.go @@ -13,8 +13,8 @@ import ( "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/devicemapper" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" units "github.com/docker/go-units" + "github.com/moby/locker" "github.com/moby/sys/mount" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" diff --git a/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go b/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go index f0055c1c5a..7535ec4497 100644 --- a/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go +++ b/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go @@ -22,9 +22,9 @@ import ( "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/directory" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/system" + "github.com/moby/locker" "github.com/moby/sys/mount" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" diff --git a/daemon/graphdriver/overlay/overlay.go b/daemon/graphdriver/overlay/overlay.go index c018de1214..1cffc1b896 100644 --- a/daemon/graphdriver/overlay/overlay.go +++ b/daemon/graphdriver/overlay/overlay.go @@ -19,9 +19,9 @@ import ( "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/fsutils" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/system" + "github.com/moby/locker" "github.com/moby/sys/mount" "github.com/opencontainers/selinux/go-selinux/label" "github.com/sirupsen/logrus" diff --git a/daemon/graphdriver/overlay2/overlay.go b/daemon/graphdriver/overlay2/overlay.go index ba94d800a3..29689a459f 100644 --- a/daemon/graphdriver/overlay2/overlay.go +++ b/daemon/graphdriver/overlay2/overlay.go @@ -25,10 +25,10 @@ import ( "github.com/docker/docker/pkg/directory" "github.com/docker/docker/pkg/fsutils" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/system" units "github.com/docker/go-units" + "github.com/moby/locker" "github.com/moby/sys/mount" "github.com/opencontainers/selinux/go-selinux/label" "github.com/sirupsen/logrus" diff --git a/integration/plugin/logging/helpers_test.go b/integration/plugin/logging/helpers_test.go index ec5ae42273..f37d7aa94b 100644 --- a/integration/plugin/logging/helpers_test.go +++ b/integration/plugin/logging/helpers_test.go @@ -9,8 +9,8 @@ import ( "time" "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/locker" "github.com/docker/docker/testutil/fixtures/plugin" + "github.com/moby/locker" "github.com/pkg/errors" ) diff --git a/integration/plugin/volumes/helpers_test.go b/integration/plugin/volumes/helpers_test.go index 223ea9144a..953ed0a584 100644 --- a/integration/plugin/volumes/helpers_test.go +++ b/integration/plugin/volumes/helpers_test.go @@ -9,8 +9,8 @@ import ( "time" "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/locker" "github.com/docker/docker/testutil/fixtures/plugin" + "github.com/moby/locker" "github.com/pkg/errors" "gotest.tools/v3/assert" ) diff --git a/layer/layer_store.go b/layer/layer_store.go index bb4cc1783b..c58f501982 100644 --- a/layer/layer_store.go +++ b/layer/layer_store.go @@ -12,10 +12,10 @@ import ( "github.com/docker/distribution" "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/system" + "github.com/moby/locker" digest "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" "github.com/vbatts/tar-split/tar/asm" diff --git a/pkg/locker/locker.go b/pkg/locker/locker.go index dbd47fc465..4d250ad542 100644 --- a/pkg/locker/locker.go +++ b/pkg/locker/locker.go @@ -14,99 +14,17 @@ waiting for the lock. package locker // import "github.com/docker/docker/pkg/locker" import ( - "errors" - "sync" - "sync/atomic" + "github.com/moby/locker" ) // ErrNoSuchLock is returned when the requested lock does not exist -var ErrNoSuchLock = errors.New("no such lock") +// Deprecated: use github.com/moby/locker.ErrNoSuchLock +var ErrNoSuchLock = locker.ErrNoSuchLock // Locker provides a locking mechanism based on the passed in reference name -type Locker struct { - mu sync.Mutex - locks map[string]*lockCtr -} - -// lockCtr is used by Locker to represent a lock with a given name. -type lockCtr struct { - mu sync.Mutex - // waiters is the number of waiters waiting to acquire the lock - // this is int32 instead of uint32 so we can add `-1` in `dec()` - waiters int32 -} - -// inc increments the number of waiters waiting for the lock -func (l *lockCtr) inc() { - atomic.AddInt32(&l.waiters, 1) -} - -// dec decrements the number of waiters waiting on the lock -func (l *lockCtr) dec() { - atomic.AddInt32(&l.waiters, -1) -} - -// count gets the current number of waiters -func (l *lockCtr) count() int32 { - return atomic.LoadInt32(&l.waiters) -} - -// Lock locks the mutex -func (l *lockCtr) Lock() { - l.mu.Lock() -} - -// Unlock unlocks the mutex -func (l *lockCtr) Unlock() { - l.mu.Unlock() -} +// Deprecated: use github.com/moby/locker.Locker +type Locker = locker.Locker // New creates a new Locker -func New() *Locker { - return &Locker{ - locks: make(map[string]*lockCtr), - } -} - -// Lock locks a mutex with the given name. If it doesn't exist, one is created -func (l *Locker) Lock(name string) { - l.mu.Lock() - if l.locks == nil { - l.locks = make(map[string]*lockCtr) - } - - nameLock, exists := l.locks[name] - if !exists { - nameLock = &lockCtr{} - l.locks[name] = nameLock - } - - // increment the nameLock waiters while inside the main mutex - // this makes sure that the lock isn't deleted if `Lock` and `Unlock` are called concurrently - nameLock.inc() - l.mu.Unlock() - - // Lock the nameLock outside the main mutex so we don't block other operations - // once locked then we can decrement the number of waiters for this lock - nameLock.Lock() - nameLock.dec() -} - -// Unlock unlocks the mutex with the given name -// If the given lock is not being waited on by any other callers, it is deleted -func (l *Locker) Unlock(name string) error { - l.mu.Lock() - nameLock, exists := l.locks[name] - if !exists { - l.mu.Unlock() - return ErrNoSuchLock - } - - if nameLock.count() == 0 { - delete(l.locks, name) - } - nameLock.Unlock() - - l.mu.Unlock() - return nil -} +// Deprecated: use github.com/moby/locker.New +var New = locker.New diff --git a/pkg/locker/locker_test.go b/pkg/locker/locker_test.go deleted file mode 100644 index 2b0a8a55d6..0000000000 --- a/pkg/locker/locker_test.go +++ /dev/null @@ -1,161 +0,0 @@ -package locker // import "github.com/docker/docker/pkg/locker" - -import ( - "math/rand" - "strconv" - "sync" - "testing" - "time" -) - -func TestLockCounter(t *testing.T) { - l := &lockCtr{} - l.inc() - - if l.waiters != 1 { - t.Fatal("counter inc failed") - } - - l.dec() - if l.waiters != 0 { - t.Fatal("counter dec failed") - } -} - -func TestLockerLock(t *testing.T) { - l := New() - l.Lock("test") - ctr := l.locks["test"] - - if ctr.count() != 0 { - t.Fatalf("expected waiters to be 0, got :%d", ctr.waiters) - } - - chDone := make(chan struct{}) - go func() { - l.Lock("test") - close(chDone) - }() - - chWaiting := make(chan struct{}) - go func() { - for range time.Tick(1 * time.Millisecond) { - if ctr.count() == 1 { - close(chWaiting) - break - } - } - }() - - select { - case <-chWaiting: - case <-time.After(3 * time.Second): - t.Fatal("timed out waiting for lock waiters to be incremented") - } - - select { - case <-chDone: - t.Fatal("lock should not have returned while it was still held") - default: - } - - if err := l.Unlock("test"); err != nil { - t.Fatal(err) - } - - select { - case <-chDone: - case <-time.After(3 * time.Second): - t.Fatalf("lock should have completed") - } - - if ctr.count() != 0 { - t.Fatalf("expected waiters to be 0, got: %d", ctr.count()) - } -} - -func TestLockerUnlock(t *testing.T) { - l := New() - - l.Lock("test") - l.Unlock("test") - - chDone := make(chan struct{}) - go func() { - l.Lock("test") - close(chDone) - }() - - select { - case <-chDone: - case <-time.After(3 * time.Second): - t.Fatalf("lock should not be blocked") - } -} - -func TestLockerConcurrency(t *testing.T) { - l := New() - - var wg sync.WaitGroup - for i := 0; i <= 10000; i++ { - wg.Add(1) - go func() { - l.Lock("test") - // if there is a concurrency issue, will very likely panic here - l.Unlock("test") - wg.Done() - }() - } - - chDone := make(chan struct{}) - go func() { - wg.Wait() - close(chDone) - }() - - select { - case <-chDone: - case <-time.After(10 * time.Second): - t.Fatal("timeout waiting for locks to complete") - } - - // Since everything has unlocked this should not exist anymore - if ctr, exists := l.locks["test"]; exists { - t.Fatalf("lock should not exist: %v", ctr) - } -} - -func BenchmarkLocker(b *testing.B) { - l := New() - for i := 0; i < b.N; i++ { - l.Lock("test") - l.Unlock("test") - } -} - -func BenchmarkLockerParallel(b *testing.B) { - l := New() - b.SetParallelism(128) - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - l.Lock("test") - l.Unlock("test") - } - }) -} - -func BenchmarkLockerMoreKeys(b *testing.B) { - l := New() - var keys []string - for i := 0; i < 64; i++ { - keys = append(keys, strconv.Itoa(i)) - } - b.SetParallelism(128) - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - k := keys[rand.Intn(len(keys))] - l.Lock(k) - l.Unlock(k) - } - }) -} diff --git a/vendor.conf b/vendor.conf index 928a678097..ebe8866588 100644 --- a/vendor.conf +++ b/vendor.conf @@ -6,6 +6,7 @@ github.com/golang/gddo 72a348e765d293ed6d1ded7b6995 github.com/google/uuid 0cd6bf5da1e1c83f8b45653022c74f71af0538a4 # v1.1.1 github.com/gorilla/mux 98cb6bf42e086f6af920b965c38cacc07402d51b # v1.8.0 github.com/Microsoft/opengcs a10967154e143a36014584a6f664344e3bb0aa64 +github.com/moby/locker 281af2d563954745bea9d1487c965f24d30742fe # v1.0.1 github.com/moby/term 73f35e472e8f0a3f91347164138ce6bd73b756a9 github.com/creack/pty 3a6a957789163cacdfe0e291617a1c8e80612c11 # v1.1.9 diff --git a/vendor/github.com/moby/locker/LICENSE b/vendor/github.com/moby/locker/LICENSE new file mode 100644 index 0000000000..2e0ec1dcf1 --- /dev/null +++ b/vendor/github.com/moby/locker/LICENSE @@ -0,0 +1,190 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2013-2018 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/pkg/locker/README.md b/vendor/github.com/moby/locker/README.md similarity index 97% rename from pkg/locker/README.md rename to vendor/github.com/moby/locker/README.md index ce787aefb3..a0852f0f8e 100644 --- a/pkg/locker/README.md +++ b/vendor/github.com/moby/locker/README.md @@ -23,7 +23,7 @@ import ( "sync" "time" - "github.com/docker/docker/pkg/locker" + "github.com/moby/locker" ) type important struct { diff --git a/vendor/github.com/moby/locker/go.mod b/vendor/github.com/moby/locker/go.mod new file mode 100644 index 0000000000..15b7adcdb7 --- /dev/null +++ b/vendor/github.com/moby/locker/go.mod @@ -0,0 +1,3 @@ +module github.com/moby/locker + +go 1.13 diff --git a/vendor/github.com/moby/locker/locker.go b/vendor/github.com/moby/locker/locker.go new file mode 100644 index 0000000000..0b22ddfab8 --- /dev/null +++ b/vendor/github.com/moby/locker/locker.go @@ -0,0 +1,112 @@ +/* +Package locker provides a mechanism for creating finer-grained locking to help +free up more global locks to handle other tasks. + +The implementation looks close to a sync.Mutex, however the user must provide a +reference to use to refer to the underlying lock when locking and unlocking, +and unlock may generate an error. + +If a lock with a given name does not exist when `Lock` is called, one is +created. +Lock references are automatically cleaned up on `Unlock` if nothing else is +waiting for the lock. +*/ +package locker + +import ( + "errors" + "sync" + "sync/atomic" +) + +// ErrNoSuchLock is returned when the requested lock does not exist +var ErrNoSuchLock = errors.New("no such lock") + +// Locker provides a locking mechanism based on the passed in reference name +type Locker struct { + mu sync.Mutex + locks map[string]*lockCtr +} + +// lockCtr is used by Locker to represent a lock with a given name. +type lockCtr struct { + mu sync.Mutex + // waiters is the number of waiters waiting to acquire the lock + // this is int32 instead of uint32 so we can add `-1` in `dec()` + waiters int32 +} + +// inc increments the number of waiters waiting for the lock +func (l *lockCtr) inc() { + atomic.AddInt32(&l.waiters, 1) +} + +// dec decrements the number of waiters waiting on the lock +func (l *lockCtr) dec() { + atomic.AddInt32(&l.waiters, -1) +} + +// count gets the current number of waiters +func (l *lockCtr) count() int32 { + return atomic.LoadInt32(&l.waiters) +} + +// Lock locks the mutex +func (l *lockCtr) Lock() { + l.mu.Lock() +} + +// Unlock unlocks the mutex +func (l *lockCtr) Unlock() { + l.mu.Unlock() +} + +// New creates a new Locker +func New() *Locker { + return &Locker{ + locks: make(map[string]*lockCtr), + } +} + +// Lock locks a mutex with the given name. If it doesn't exist, one is created +func (l *Locker) Lock(name string) { + l.mu.Lock() + if l.locks == nil { + l.locks = make(map[string]*lockCtr) + } + + nameLock, exists := l.locks[name] + if !exists { + nameLock = &lockCtr{} + l.locks[name] = nameLock + } + + // increment the nameLock waiters while inside the main mutex + // this makes sure that the lock isn't deleted if `Lock` and `Unlock` are called concurrently + nameLock.inc() + l.mu.Unlock() + + // Lock the nameLock outside the main mutex so we don't block other operations + // once locked then we can decrement the number of waiters for this lock + nameLock.Lock() + nameLock.dec() +} + +// Unlock unlocks the mutex with the given name +// If the given lock is not being waited on by any other callers, it is deleted +func (l *Locker) Unlock(name string) error { + l.mu.Lock() + nameLock, exists := l.locks[name] + if !exists { + l.mu.Unlock() + return ErrNoSuchLock + } + + if nameLock.count() == 0 { + delete(l.locks, name) + } + nameLock.Unlock() + + l.mu.Unlock() + return nil +} diff --git a/volume/drivers/extpoint.go b/volume/drivers/extpoint.go index b2131c20ef..dbe0086ab6 100644 --- a/volume/drivers/extpoint.go +++ b/volume/drivers/extpoint.go @@ -8,10 +8,10 @@ import ( "sync" "github.com/docker/docker/errdefs" - "github.com/docker/docker/pkg/locker" getter "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/plugins" "github.com/docker/docker/volume" + "github.com/moby/locker" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) diff --git a/volume/service/store.go b/volume/service/store.go index 5183d1f7ab..7989f8ddf5 100644 --- a/volume/service/store.go +++ b/volume/service/store.go @@ -10,14 +10,13 @@ import ( "sync" "time" - "github.com/pkg/errors" - "github.com/docker/docker/errdefs" - "github.com/docker/docker/pkg/locker" "github.com/docker/docker/volume" "github.com/docker/docker/volume/drivers" volumemounts "github.com/docker/docker/volume/mounts" "github.com/docker/docker/volume/service/opts" + "github.com/moby/locker" + "github.com/pkg/errors" "github.com/sirupsen/logrus" bolt "go.etcd.io/bbolt" )