01 How to set the object not to be copied
sync.Pool
cannot be copied.
1
2
3
4
5
6
7
8
9
10
11
|
// sync/pool.go
type Pool struct {
noCopy noCopy
...
}
type noCopy struct{}
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}
|
sync.Mutex
cannot be copied.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// sync/mutex.go
type Mutex struct {
state int32
sema uint32
}
// A Locker represents an object that can be locked and unlocked.
type Locker interface {
Lock()
Unlock()
}
func (s *Mutex) Lock() {
...
}
func (s *Mutex) Unlock() {
...
}
|
sync.Cond
cannot be copied.
1
2
3
4
5
6
7
|
// sync/cond.go
type Cond struct {
noCopy noCopy
...
}
|
There are many places in the sync
package where data races are involved, especially when concurrent, so golang supports setting up non-copyability via nocopy
. All you need is to implement the sync.Locker
interface.
1
2
3
4
|
type Locker interface {
Lock()
Unlock()
}
|
02 How the underlying implementation disables copying
The Locker-based implementation of the nocopy mechanism is recognized in the static check phase (go vet
), rather than being handled in the runtime.
1
2
3
4
5
6
7
|
// cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
if named, ok := typ.(*types.Named); ok &&
named.Obj().Name() == "noCopy" &&
named.Obj().Pkg().Path() == "sync" {
return []types.Type{typ}
}
|
Before go1.10, the way to judge is written dead, directly determine whether it is sync
package, and the name is nocopy
. Later, it was changed to determine whether the sync.Locker
interface is implemented.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
var lockerType *types.Interface
// Construct a sync.Locker interface type.
func init() {
nullary := types.NewSignature(nil, nil, nil, false) // func()
methods := []*types.Func{
types.NewFunc(token.NoPos, nil, "Lock", nullary),
types.NewFunc(token.NoPos, nil, "Unlock", nullary),
}
lockerType = types.NewInterface(methods, nil).Complete()
}
...
func lockPath(tpkg *types.Package, typ types.Type) typePath {
...
if types.Implements(types.NewPointer(typ), lockerType) && !types.Implements(typ, lockerType) {
return []types.Type{typ}
}
...
}
|
03 Summary
If you want to use the nocopy function in your real business, you just need to implement the sync.Locker
interface and embed it in the structure. The static check at the front-end stage of compilation determines whether there is a copied case.