Browse code

replace pkg/locker with github.com/moby/locker

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2020/09/11 05:15:40
Showing 19 changed files
... ...
@@ -55,7 +55,6 @@ import (
55 55
 	"github.com/docker/docker/libcontainerd"
56 56
 	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
57 57
 	"github.com/docker/docker/pkg/idtools"
58
-	"github.com/docker/docker/pkg/locker"
59 58
 	"github.com/docker/docker/pkg/plugingetter"
60 59
 	"github.com/docker/docker/pkg/system"
61 60
 	"github.com/docker/docker/pkg/truncindex"
... ...
@@ -68,6 +67,7 @@ import (
68 68
 	"github.com/docker/libnetwork"
69 69
 	"github.com/docker/libnetwork/cluster"
70 70
 	nwconfig "github.com/docker/libnetwork/config"
71
+	"github.com/moby/locker"
71 72
 	"github.com/pkg/errors"
72 73
 	"golang.org/x/sync/semaphore"
73 74
 )
... ...
@@ -42,8 +42,8 @@ import (
42 42
 	"github.com/docker/docker/pkg/containerfs"
43 43
 	"github.com/docker/docker/pkg/directory"
44 44
 	"github.com/docker/docker/pkg/idtools"
45
-	"github.com/docker/docker/pkg/locker"
46 45
 	"github.com/docker/docker/pkg/system"
46
+	"github.com/moby/locker"
47 47
 	"github.com/moby/sys/mount"
48 48
 	"github.com/opencontainers/selinux/go-selinux/label"
49 49
 	"github.com/pkg/errors"
... ...
@@ -13,8 +13,8 @@ import (
13 13
 	"github.com/docker/docker/pkg/containerfs"
14 14
 	"github.com/docker/docker/pkg/devicemapper"
15 15
 	"github.com/docker/docker/pkg/idtools"
16
-	"github.com/docker/docker/pkg/locker"
17 16
 	units "github.com/docker/go-units"
17
+	"github.com/moby/locker"
18 18
 	"github.com/moby/sys/mount"
19 19
 	"github.com/sirupsen/logrus"
20 20
 	"golang.org/x/sys/unix"
... ...
@@ -22,9 +22,9 @@ import (
22 22
 	"github.com/docker/docker/pkg/containerfs"
23 23
 	"github.com/docker/docker/pkg/directory"
24 24
 	"github.com/docker/docker/pkg/idtools"
25
-	"github.com/docker/docker/pkg/locker"
26 25
 	"github.com/docker/docker/pkg/parsers/kernel"
27 26
 	"github.com/docker/docker/pkg/system"
27
+	"github.com/moby/locker"
28 28
 	"github.com/moby/sys/mount"
29 29
 	"github.com/opencontainers/selinux/go-selinux/label"
30 30
 	"github.com/pkg/errors"
... ...
@@ -19,9 +19,9 @@ import (
19 19
 	"github.com/docker/docker/pkg/containerfs"
20 20
 	"github.com/docker/docker/pkg/fsutils"
21 21
 	"github.com/docker/docker/pkg/idtools"
22
-	"github.com/docker/docker/pkg/locker"
23 22
 	"github.com/docker/docker/pkg/parsers"
24 23
 	"github.com/docker/docker/pkg/system"
24
+	"github.com/moby/locker"
25 25
 	"github.com/moby/sys/mount"
26 26
 	"github.com/opencontainers/selinux/go-selinux/label"
27 27
 	"github.com/sirupsen/logrus"
... ...
@@ -25,10 +25,10 @@ import (
25 25
 	"github.com/docker/docker/pkg/directory"
26 26
 	"github.com/docker/docker/pkg/fsutils"
27 27
 	"github.com/docker/docker/pkg/idtools"
28
-	"github.com/docker/docker/pkg/locker"
29 28
 	"github.com/docker/docker/pkg/parsers"
30 29
 	"github.com/docker/docker/pkg/system"
31 30
 	units "github.com/docker/go-units"
31
+	"github.com/moby/locker"
32 32
 	"github.com/moby/sys/mount"
33 33
 	"github.com/opencontainers/selinux/go-selinux/label"
34 34
 	"github.com/sirupsen/logrus"
... ...
@@ -9,8 +9,8 @@ import (
9 9
 	"time"
10 10
 
11 11
 	"github.com/docker/docker/api/types"
12
-	"github.com/docker/docker/pkg/locker"
13 12
 	"github.com/docker/docker/testutil/fixtures/plugin"
13
+	"github.com/moby/locker"
14 14
 	"github.com/pkg/errors"
15 15
 )
16 16
 
... ...
@@ -9,8 +9,8 @@ import (
9 9
 	"time"
10 10
 
11 11
 	"github.com/docker/docker/api/types"
12
-	"github.com/docker/docker/pkg/locker"
13 12
 	"github.com/docker/docker/testutil/fixtures/plugin"
13
+	"github.com/moby/locker"
14 14
 	"github.com/pkg/errors"
15 15
 	"gotest.tools/v3/assert"
16 16
 )
... ...
@@ -12,10 +12,10 @@ import (
12 12
 	"github.com/docker/distribution"
13 13
 	"github.com/docker/docker/daemon/graphdriver"
14 14
 	"github.com/docker/docker/pkg/idtools"
15
-	"github.com/docker/docker/pkg/locker"
16 15
 	"github.com/docker/docker/pkg/plugingetter"
17 16
 	"github.com/docker/docker/pkg/stringid"
18 17
 	"github.com/docker/docker/pkg/system"
18
+	"github.com/moby/locker"
19 19
 	digest "github.com/opencontainers/go-digest"
20 20
 	"github.com/sirupsen/logrus"
21 21
 	"github.com/vbatts/tar-split/tar/asm"
22 22
deleted file mode 100644
... ...
@@ -1,65 +0,0 @@
1
-Locker
2
-=====
3
-
4
-locker provides a mechanism for creating finer-grained locking to help
5
-free up more global locks to handle other tasks.
6
-
7
-The implementation looks close to a sync.Mutex, however, the user must provide a
8
-reference to use to refer to the underlying lock when locking and unlocking,
9
-and unlock may generate an error.
10
-
11
-If a lock with a given name does not exist when `Lock` is called, one is
12
-created.
13
-Lock references are automatically cleaned up on `Unlock` if nothing else is
14
-waiting for the lock.
15
-
16
-
17
-## Usage
18
-
19
-```go
20
-package important
21
-
22
-import (
23
-	"sync"
24
-	"time"
25
-
26
-	"github.com/docker/docker/pkg/locker"
27
-)
28
-
29
-type important struct {
30
-	locks *locker.Locker
31
-	data  map[string]interface{}
32
-	mu    sync.Mutex
33
-}
34
-
35
-func (i *important) Get(name string) interface{} {
36
-	i.locks.Lock(name)
37
-	defer i.locks.Unlock(name)
38
-	return i.data[name]
39
-}
40
-
41
-func (i *important) Create(name string, data interface{}) {
42
-	i.locks.Lock(name)
43
-	defer i.locks.Unlock(name)
44
-
45
-	i.createImportant(data)
46
-
47
-	i.mu.Lock()
48
-	i.data[name] = data
49
-	i.mu.Unlock()
50
-}
51
-
52
-func (i *important) createImportant(data interface{}) {
53
-	time.Sleep(10 * time.Second)
54
-}
55
-```
56
-
57
-For functions dealing with a given name, always lock at the beginning of the
58
-function (or before doing anything with the underlying state), this ensures any
59
-other function that is dealing with the same name will block.
60
-
61
-When needing to modify the underlying data, use the global lock to ensure nothing
62
-else is modifying it at the same time.
63
-Since name lock is already in place, no reads will occur while the modification
64
-is being performed.
65
-
... ...
@@ -14,99 +14,17 @@ waiting for the lock.
14 14
 package locker // import "github.com/docker/docker/pkg/locker"
15 15
 
16 16
 import (
17
-	"errors"
18
-	"sync"
19
-	"sync/atomic"
17
+	"github.com/moby/locker"
20 18
 )
21 19
 
22 20
 // ErrNoSuchLock is returned when the requested lock does not exist
23
-var ErrNoSuchLock = errors.New("no such lock")
21
+// Deprecated: use github.com/moby/locker.ErrNoSuchLock
22
+var ErrNoSuchLock = locker.ErrNoSuchLock
24 23
 
25 24
 // Locker provides a locking mechanism based on the passed in reference name
26
-type Locker struct {
27
-	mu    sync.Mutex
28
-	locks map[string]*lockCtr
29
-}
30
-
31
-// lockCtr is used by Locker to represent a lock with a given name.
32
-type lockCtr struct {
33
-	mu sync.Mutex
34
-	// waiters is the number of waiters waiting to acquire the lock
35
-	// this is int32 instead of uint32 so we can add `-1` in `dec()`
36
-	waiters int32
37
-}
38
-
39
-// inc increments the number of waiters waiting for the lock
40
-func (l *lockCtr) inc() {
41
-	atomic.AddInt32(&l.waiters, 1)
42
-}
43
-
44
-// dec decrements the number of waiters waiting on the lock
45
-func (l *lockCtr) dec() {
46
-	atomic.AddInt32(&l.waiters, -1)
47
-}
48
-
49
-// count gets the current number of waiters
50
-func (l *lockCtr) count() int32 {
51
-	return atomic.LoadInt32(&l.waiters)
52
-}
53
-
54
-// Lock locks the mutex
55
-func (l *lockCtr) Lock() {
56
-	l.mu.Lock()
57
-}
58
-
59
-// Unlock unlocks the mutex
60
-func (l *lockCtr) Unlock() {
61
-	l.mu.Unlock()
62
-}
25
+// Deprecated: use github.com/moby/locker.Locker
26
+type Locker = locker.Locker
63 27
 
64 28
 // New creates a new Locker
65
-func New() *Locker {
66
-	return &Locker{
67
-		locks: make(map[string]*lockCtr),
68
-	}
69
-}
70
-
71
-// Lock locks a mutex with the given name. If it doesn't exist, one is created
72
-func (l *Locker) Lock(name string) {
73
-	l.mu.Lock()
74
-	if l.locks == nil {
75
-		l.locks = make(map[string]*lockCtr)
76
-	}
77
-
78
-	nameLock, exists := l.locks[name]
79
-	if !exists {
80
-		nameLock = &lockCtr{}
81
-		l.locks[name] = nameLock
82
-	}
83
-
84
-	// increment the nameLock waiters while inside the main mutex
85
-	// this makes sure that the lock isn't deleted if `Lock` and `Unlock` are called concurrently
86
-	nameLock.inc()
87
-	l.mu.Unlock()
88
-
89
-	// Lock the nameLock outside the main mutex so we don't block other operations
90
-	// once locked then we can decrement the number of waiters for this lock
91
-	nameLock.Lock()
92
-	nameLock.dec()
93
-}
94
-
95
-// Unlock unlocks the mutex with the given name
96
-// If the given lock is not being waited on by any other callers, it is deleted
97
-func (l *Locker) Unlock(name string) error {
98
-	l.mu.Lock()
99
-	nameLock, exists := l.locks[name]
100
-	if !exists {
101
-		l.mu.Unlock()
102
-		return ErrNoSuchLock
103
-	}
104
-
105
-	if nameLock.count() == 0 {
106
-		delete(l.locks, name)
107
-	}
108
-	nameLock.Unlock()
109
-
110
-	l.mu.Unlock()
111
-	return nil
112
-}
29
+// Deprecated: use github.com/moby/locker.New
30
+var New = locker.New
113 31
deleted file mode 100644
... ...
@@ -1,161 +0,0 @@
1
-package locker // import "github.com/docker/docker/pkg/locker"
2
-
3
-import (
4
-	"math/rand"
5
-	"strconv"
6
-	"sync"
7
-	"testing"
8
-	"time"
9
-)
10
-
11
-func TestLockCounter(t *testing.T) {
12
-	l := &lockCtr{}
13
-	l.inc()
14
-
15
-	if l.waiters != 1 {
16
-		t.Fatal("counter inc failed")
17
-	}
18
-
19
-	l.dec()
20
-	if l.waiters != 0 {
21
-		t.Fatal("counter dec failed")
22
-	}
23
-}
24
-
25
-func TestLockerLock(t *testing.T) {
26
-	l := New()
27
-	l.Lock("test")
28
-	ctr := l.locks["test"]
29
-
30
-	if ctr.count() != 0 {
31
-		t.Fatalf("expected waiters to be 0, got :%d", ctr.waiters)
32
-	}
33
-
34
-	chDone := make(chan struct{})
35
-	go func() {
36
-		l.Lock("test")
37
-		close(chDone)
38
-	}()
39
-
40
-	chWaiting := make(chan struct{})
41
-	go func() {
42
-		for range time.Tick(1 * time.Millisecond) {
43
-			if ctr.count() == 1 {
44
-				close(chWaiting)
45
-				break
46
-			}
47
-		}
48
-	}()
49
-
50
-	select {
51
-	case <-chWaiting:
52
-	case <-time.After(3 * time.Second):
53
-		t.Fatal("timed out waiting for lock waiters to be incremented")
54
-	}
55
-
56
-	select {
57
-	case <-chDone:
58
-		t.Fatal("lock should not have returned while it was still held")
59
-	default:
60
-	}
61
-
62
-	if err := l.Unlock("test"); err != nil {
63
-		t.Fatal(err)
64
-	}
65
-
66
-	select {
67
-	case <-chDone:
68
-	case <-time.After(3 * time.Second):
69
-		t.Fatalf("lock should have completed")
70
-	}
71
-
72
-	if ctr.count() != 0 {
73
-		t.Fatalf("expected waiters to be 0, got: %d", ctr.count())
74
-	}
75
-}
76
-
77
-func TestLockerUnlock(t *testing.T) {
78
-	l := New()
79
-
80
-	l.Lock("test")
81
-	l.Unlock("test")
82
-
83
-	chDone := make(chan struct{})
84
-	go func() {
85
-		l.Lock("test")
86
-		close(chDone)
87
-	}()
88
-
89
-	select {
90
-	case <-chDone:
91
-	case <-time.After(3 * time.Second):
92
-		t.Fatalf("lock should not be blocked")
93
-	}
94
-}
95
-
96
-func TestLockerConcurrency(t *testing.T) {
97
-	l := New()
98
-
99
-	var wg sync.WaitGroup
100
-	for i := 0; i <= 10000; i++ {
101
-		wg.Add(1)
102
-		go func() {
103
-			l.Lock("test")
104
-			// if there is a concurrency issue, will very likely panic here
105
-			l.Unlock("test")
106
-			wg.Done()
107
-		}()
108
-	}
109
-
110
-	chDone := make(chan struct{})
111
-	go func() {
112
-		wg.Wait()
113
-		close(chDone)
114
-	}()
115
-
116
-	select {
117
-	case <-chDone:
118
-	case <-time.After(10 * time.Second):
119
-		t.Fatal("timeout waiting for locks to complete")
120
-	}
121
-
122
-	// Since everything has unlocked this should not exist anymore
123
-	if ctr, exists := l.locks["test"]; exists {
124
-		t.Fatalf("lock should not exist: %v", ctr)
125
-	}
126
-}
127
-
128
-func BenchmarkLocker(b *testing.B) {
129
-	l := New()
130
-	for i := 0; i < b.N; i++ {
131
-		l.Lock("test")
132
-		l.Unlock("test")
133
-	}
134
-}
135
-
136
-func BenchmarkLockerParallel(b *testing.B) {
137
-	l := New()
138
-	b.SetParallelism(128)
139
-	b.RunParallel(func(pb *testing.PB) {
140
-		for pb.Next() {
141
-			l.Lock("test")
142
-			l.Unlock("test")
143
-		}
144
-	})
145
-}
146
-
147
-func BenchmarkLockerMoreKeys(b *testing.B) {
148
-	l := New()
149
-	var keys []string
150
-	for i := 0; i < 64; i++ {
151
-		keys = append(keys, strconv.Itoa(i))
152
-	}
153
-	b.SetParallelism(128)
154
-	b.RunParallel(func(pb *testing.PB) {
155
-		for pb.Next() {
156
-			k := keys[rand.Intn(len(keys))]
157
-			l.Lock(k)
158
-			l.Unlock(k)
159
-		}
160
-	})
161
-}
... ...
@@ -6,6 +6,7 @@ github.com/golang/gddo                              72a348e765d293ed6d1ded7b6995
6 6
 github.com/google/uuid                              0cd6bf5da1e1c83f8b45653022c74f71af0538a4 # v1.1.1
7 7
 github.com/gorilla/mux                              98cb6bf42e086f6af920b965c38cacc07402d51b # v1.8.0
8 8
 github.com/Microsoft/opengcs                        a10967154e143a36014584a6f664344e3bb0aa64
9
+github.com/moby/locker                              281af2d563954745bea9d1487c965f24d30742fe # v1.0.1
9 10
 github.com/moby/term                                73f35e472e8f0a3f91347164138ce6bd73b756a9
10 11
 
11 12
 github.com/creack/pty                               3a6a957789163cacdfe0e291617a1c8e80612c11 # v1.1.9
12 13
new file mode 100644
... ...
@@ -0,0 +1,190 @@
0
+                                 Apache License
1
+                           Version 2.0, January 2004
2
+                        https://www.apache.org/licenses/
3
+
4
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
5
+
6
+   1. Definitions.
7
+
8
+      "License" shall mean the terms and conditions for use, reproduction,
9
+      and distribution as defined by Sections 1 through 9 of this document.
10
+
11
+      "Licensor" shall mean the copyright owner or entity authorized by
12
+      the copyright owner that is granting the License.
13
+
14
+      "Legal Entity" shall mean the union of the acting entity and all
15
+      other entities that control, are controlled by, or are under common
16
+      control with that entity. For the purposes of this definition,
17
+      "control" means (i) the power, direct or indirect, to cause the
18
+      direction or management of such entity, whether by contract or
19
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
20
+      outstanding shares, or (iii) beneficial ownership of such entity.
21
+
22
+      "You" (or "Your") shall mean an individual or Legal Entity
23
+      exercising permissions granted by this License.
24
+
25
+      "Source" form shall mean the preferred form for making modifications,
26
+      including but not limited to software source code, documentation
27
+      source, and configuration files.
28
+
29
+      "Object" form shall mean any form resulting from mechanical
30
+      transformation or translation of a Source form, including but
31
+      not limited to compiled object code, generated documentation,
32
+      and conversions to other media types.
33
+
34
+      "Work" shall mean the work of authorship, whether in Source or
35
+      Object form, made available under the License, as indicated by a
36
+      copyright notice that is included in or attached to the work
37
+      (an example is provided in the Appendix below).
38
+
39
+      "Derivative Works" shall mean any work, whether in Source or Object
40
+      form, that is based on (or derived from) the Work and for which the
41
+      editorial revisions, annotations, elaborations, or other modifications
42
+      represent, as a whole, an original work of authorship. For the purposes
43
+      of this License, Derivative Works shall not include works that remain
44
+      separable from, or merely link (or bind by name) to the interfaces of,
45
+      the Work and Derivative Works thereof.
46
+
47
+      "Contribution" shall mean any work of authorship, including
48
+      the original version of the Work and any modifications or additions
49
+      to that Work or Derivative Works thereof, that is intentionally
50
+      submitted to Licensor for inclusion in the Work by the copyright owner
51
+      or by an individual or Legal Entity authorized to submit on behalf of
52
+      the copyright owner. For the purposes of this definition, "submitted"
53
+      means any form of electronic, verbal, or written communication sent
54
+      to the Licensor or its representatives, including but not limited to
55
+      communication on electronic mailing lists, source code control systems,
56
+      and issue tracking systems that are managed by, or on behalf of, the
57
+      Licensor for the purpose of discussing and improving the Work, but
58
+      excluding communication that is conspicuously marked or otherwise
59
+      designated in writing by the copyright owner as "Not a Contribution."
60
+
61
+      "Contributor" shall mean Licensor and any individual or Legal Entity
62
+      on behalf of whom a Contribution has been received by Licensor and
63
+      subsequently incorporated within the Work.
64
+
65
+   2. Grant of Copyright License. Subject to the terms and conditions of
66
+      this License, each Contributor hereby grants to You a perpetual,
67
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
68
+      copyright license to reproduce, prepare Derivative Works of,
69
+      publicly display, publicly perform, sublicense, and distribute the
70
+      Work and such Derivative Works in Source or Object form.
71
+
72
+   3. Grant of Patent License. Subject to the terms and conditions of
73
+      this License, each Contributor hereby grants to You a perpetual,
74
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
75
+      (except as stated in this section) patent license to make, have made,
76
+      use, offer to sell, sell, import, and otherwise transfer the Work,
77
+      where such license applies only to those patent claims licensable
78
+      by such Contributor that are necessarily infringed by their
79
+      Contribution(s) alone or by combination of their Contribution(s)
80
+      with the Work to which such Contribution(s) was submitted. If You
81
+      institute patent litigation against any entity (including a
82
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
83
+      or a Contribution incorporated within the Work constitutes direct
84
+      or contributory patent infringement, then any patent licenses
85
+      granted to You under this License for that Work shall terminate
86
+      as of the date such litigation is filed.
87
+
88
+   4. Redistribution. You may reproduce and distribute copies of the
89
+      Work or Derivative Works thereof in any medium, with or without
90
+      modifications, and in Source or Object form, provided that You
91
+      meet the following conditions:
92
+
93
+      (a) You must give any other recipients of the Work or
94
+          Derivative Works a copy of this License; and
95
+
96
+      (b) You must cause any modified files to carry prominent notices
97
+          stating that You changed the files; and
98
+
99
+      (c) You must retain, in the Source form of any Derivative Works
100
+          that You distribute, all copyright, patent, trademark, and
101
+          attribution notices from the Source form of the Work,
102
+          excluding those notices that do not pertain to any part of
103
+          the Derivative Works; and
104
+
105
+      (d) If the Work includes a "NOTICE" text file as part of its
106
+          distribution, then any Derivative Works that You distribute must
107
+          include a readable copy of the attribution notices contained
108
+          within such NOTICE file, excluding those notices that do not
109
+          pertain to any part of the Derivative Works, in at least one
110
+          of the following places: within a NOTICE text file distributed
111
+          as part of the Derivative Works; within the Source form or
112
+          documentation, if provided along with the Derivative Works; or,
113
+          within a display generated by the Derivative Works, if and
114
+          wherever such third-party notices normally appear. The contents
115
+          of the NOTICE file are for informational purposes only and
116
+          do not modify the License. You may add Your own attribution
117
+          notices within Derivative Works that You distribute, alongside
118
+          or as an addendum to the NOTICE text from the Work, provided
119
+          that such additional attribution notices cannot be construed
120
+          as modifying the License.
121
+
122
+      You may add Your own copyright statement to Your modifications and
123
+      may provide additional or different license terms and conditions
124
+      for use, reproduction, or distribution of Your modifications, or
125
+      for any such Derivative Works as a whole, provided Your use,
126
+      reproduction, and distribution of the Work otherwise complies with
127
+      the conditions stated in this License.
128
+
129
+   5. Submission of Contributions. Unless You explicitly state otherwise,
130
+      any Contribution intentionally submitted for inclusion in the Work
131
+      by You to the Licensor shall be under the terms and conditions of
132
+      this License, without any additional terms or conditions.
133
+      Notwithstanding the above, nothing herein shall supersede or modify
134
+      the terms of any separate license agreement you may have executed
135
+      with Licensor regarding such Contributions.
136
+
137
+   6. Trademarks. This License does not grant permission to use the trade
138
+      names, trademarks, service marks, or product names of the Licensor,
139
+      except as required for reasonable and customary use in describing the
140
+      origin of the Work and reproducing the content of the NOTICE file.
141
+
142
+   7. Disclaimer of Warranty. Unless required by applicable law or
143
+      agreed to in writing, Licensor provides the Work (and each
144
+      Contributor provides its Contributions) on an "AS IS" BASIS,
145
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
146
+      implied, including, without limitation, any warranties or conditions
147
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
148
+      PARTICULAR PURPOSE. You are solely responsible for determining the
149
+      appropriateness of using or redistributing the Work and assume any
150
+      risks associated with Your exercise of permissions under this License.
151
+
152
+   8. Limitation of Liability. In no event and under no legal theory,
153
+      whether in tort (including negligence), contract, or otherwise,
154
+      unless required by applicable law (such as deliberate and grossly
155
+      negligent acts) or agreed to in writing, shall any Contributor be
156
+      liable to You for damages, including any direct, indirect, special,
157
+      incidental, or consequential damages of any character arising as a
158
+      result of this License or out of the use or inability to use the
159
+      Work (including but not limited to damages for loss of goodwill,
160
+      work stoppage, computer failure or malfunction, or any and all
161
+      other commercial damages or losses), even if such Contributor
162
+      has been advised of the possibility of such damages.
163
+
164
+   9. Accepting Warranty or Additional Liability. While redistributing
165
+      the Work or Derivative Works thereof, You may choose to offer,
166
+      and charge a fee for, acceptance of support, warranty, indemnity,
167
+      or other liability obligations and/or rights consistent with this
168
+      License. However, in accepting such obligations, You may act only
169
+      on Your own behalf and on Your sole responsibility, not on behalf
170
+      of any other Contributor, and only if You agree to indemnify,
171
+      defend, and hold each Contributor harmless for any liability
172
+      incurred by, or claims asserted against, such Contributor by reason
173
+      of your accepting any such warranty or additional liability.
174
+
175
+   END OF TERMS AND CONDITIONS
176
+
177
+   Copyright 2013-2018 Docker, Inc.
178
+
179
+   Licensed under the Apache License, Version 2.0 (the "License");
180
+   you may not use this file except in compliance with the License.
181
+   You may obtain a copy of the License at
182
+
183
+       https://www.apache.org/licenses/LICENSE-2.0
184
+
185
+   Unless required by applicable law or agreed to in writing, software
186
+   distributed under the License is distributed on an "AS IS" BASIS,
187
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
188
+   See the License for the specific language governing permissions and
189
+   limitations under the License.
0 190
new file mode 100644
... ...
@@ -0,0 +1,65 @@
0
+Locker
1
+=====
2
+
3
+locker provides a mechanism for creating finer-grained locking to help
4
+free up more global locks to handle other tasks.
5
+
6
+The implementation looks close to a sync.Mutex, however, the user must provide a
7
+reference to use to refer to the underlying lock when locking and unlocking,
8
+and unlock may generate an error.
9
+
10
+If a lock with a given name does not exist when `Lock` is called, one is
11
+created.
12
+Lock references are automatically cleaned up on `Unlock` if nothing else is
13
+waiting for the lock.
14
+
15
+
16
+## Usage
17
+
18
+```go
19
+package important
20
+
21
+import (
22
+	"sync"
23
+	"time"
24
+
25
+	"github.com/moby/locker"
26
+)
27
+
28
+type important struct {
29
+	locks *locker.Locker
30
+	data  map[string]interface{}
31
+	mu    sync.Mutex
32
+}
33
+
34
+func (i *important) Get(name string) interface{} {
35
+	i.locks.Lock(name)
36
+	defer i.locks.Unlock(name)
37
+	return i.data[name]
38
+}
39
+
40
+func (i *important) Create(name string, data interface{}) {
41
+	i.locks.Lock(name)
42
+	defer i.locks.Unlock(name)
43
+
44
+	i.createImportant(data)
45
+
46
+	i.mu.Lock()
47
+	i.data[name] = data
48
+	i.mu.Unlock()
49
+}
50
+
51
+func (i *important) createImportant(data interface{}) {
52
+	time.Sleep(10 * time.Second)
53
+}
54
+```
55
+
56
+For functions dealing with a given name, always lock at the beginning of the
57
+function (or before doing anything with the underlying state), this ensures any
58
+other function that is dealing with the same name will block.
59
+
60
+When needing to modify the underlying data, use the global lock to ensure nothing
61
+else is modifying it at the same time.
62
+Since name lock is already in place, no reads will occur while the modification
63
+is being performed.
64
+
0 65
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+module github.com/moby/locker
1
+
2
+go 1.13
0 3
new file mode 100644
... ...
@@ -0,0 +1,112 @@
0
+/*
1
+Package locker provides a mechanism for creating finer-grained locking to help
2
+free up more global locks to handle other tasks.
3
+
4
+The implementation looks close to a sync.Mutex, however the user must provide a
5
+reference to use to refer to the underlying lock when locking and unlocking,
6
+and unlock may generate an error.
7
+
8
+If a lock with a given name does not exist when `Lock` is called, one is
9
+created.
10
+Lock references are automatically cleaned up on `Unlock` if nothing else is
11
+waiting for the lock.
12
+*/
13
+package locker
14
+
15
+import (
16
+	"errors"
17
+	"sync"
18
+	"sync/atomic"
19
+)
20
+
21
+// ErrNoSuchLock is returned when the requested lock does not exist
22
+var ErrNoSuchLock = errors.New("no such lock")
23
+
24
+// Locker provides a locking mechanism based on the passed in reference name
25
+type Locker struct {
26
+	mu    sync.Mutex
27
+	locks map[string]*lockCtr
28
+}
29
+
30
+// lockCtr is used by Locker to represent a lock with a given name.
31
+type lockCtr struct {
32
+	mu sync.Mutex
33
+	// waiters is the number of waiters waiting to acquire the lock
34
+	// this is int32 instead of uint32 so we can add `-1` in `dec()`
35
+	waiters int32
36
+}
37
+
38
+// inc increments the number of waiters waiting for the lock
39
+func (l *lockCtr) inc() {
40
+	atomic.AddInt32(&l.waiters, 1)
41
+}
42
+
43
+// dec decrements the number of waiters waiting on the lock
44
+func (l *lockCtr) dec() {
45
+	atomic.AddInt32(&l.waiters, -1)
46
+}
47
+
48
+// count gets the current number of waiters
49
+func (l *lockCtr) count() int32 {
50
+	return atomic.LoadInt32(&l.waiters)
51
+}
52
+
53
+// Lock locks the mutex
54
+func (l *lockCtr) Lock() {
55
+	l.mu.Lock()
56
+}
57
+
58
+// Unlock unlocks the mutex
59
+func (l *lockCtr) Unlock() {
60
+	l.mu.Unlock()
61
+}
62
+
63
+// New creates a new Locker
64
+func New() *Locker {
65
+	return &Locker{
66
+		locks: make(map[string]*lockCtr),
67
+	}
68
+}
69
+
70
+// Lock locks a mutex with the given name. If it doesn't exist, one is created
71
+func (l *Locker) Lock(name string) {
72
+	l.mu.Lock()
73
+	if l.locks == nil {
74
+		l.locks = make(map[string]*lockCtr)
75
+	}
76
+
77
+	nameLock, exists := l.locks[name]
78
+	if !exists {
79
+		nameLock = &lockCtr{}
80
+		l.locks[name] = nameLock
81
+	}
82
+
83
+	// increment the nameLock waiters while inside the main mutex
84
+	// this makes sure that the lock isn't deleted if `Lock` and `Unlock` are called concurrently
85
+	nameLock.inc()
86
+	l.mu.Unlock()
87
+
88
+	// Lock the nameLock outside the main mutex so we don't block other operations
89
+	// once locked then we can decrement the number of waiters for this lock
90
+	nameLock.Lock()
91
+	nameLock.dec()
92
+}
93
+
94
+// Unlock unlocks the mutex with the given name
95
+// If the given lock is not being waited on by any other callers, it is deleted
96
+func (l *Locker) Unlock(name string) error {
97
+	l.mu.Lock()
98
+	nameLock, exists := l.locks[name]
99
+	if !exists {
100
+		l.mu.Unlock()
101
+		return ErrNoSuchLock
102
+	}
103
+
104
+	if nameLock.count() == 0 {
105
+		delete(l.locks, name)
106
+	}
107
+	nameLock.Unlock()
108
+
109
+	l.mu.Unlock()
110
+	return nil
111
+}
... ...
@@ -8,10 +8,10 @@ import (
8 8
 	"sync"
9 9
 
10 10
 	"github.com/docker/docker/errdefs"
11
-	"github.com/docker/docker/pkg/locker"
12 11
 	getter "github.com/docker/docker/pkg/plugingetter"
13 12
 	"github.com/docker/docker/pkg/plugins"
14 13
 	"github.com/docker/docker/volume"
14
+	"github.com/moby/locker"
15 15
 	"github.com/pkg/errors"
16 16
 	"github.com/sirupsen/logrus"
17 17
 )
... ...
@@ -10,14 +10,13 @@ import (
10 10
 	"sync"
11 11
 	"time"
12 12
 
13
-	"github.com/pkg/errors"
14
-
15 13
 	"github.com/docker/docker/errdefs"
16
-	"github.com/docker/docker/pkg/locker"
17 14
 	"github.com/docker/docker/volume"
18 15
 	"github.com/docker/docker/volume/drivers"
19 16
 	volumemounts "github.com/docker/docker/volume/mounts"
20 17
 	"github.com/docker/docker/volume/service/opts"
18
+	"github.com/moby/locker"
19
+	"github.com/pkg/errors"
21 20
 	"github.com/sirupsen/logrus"
22 21
 	bolt "go.etcd.io/bbolt"
23 22
 )