-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathcstruct.go
101 lines (91 loc) · 2.6 KB
/
cstruct.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package notmuch
// Copyright © 2015 The go.notmuch Authors. Authors can be found in the AUTHORS file.
// Licensed under the GPLv3 or later.
// See COPYING at the root of the repository for details.
import (
"io"
"runtime"
"sync"
"unsafe"
)
// cStruct is the common representation of almost all of our wrapper types.
//
// It does the heavy lifting of interacting with the garbage
// collector/*_destroy functions.
type cStruct struct {
// A pointer to the underlying c object
cptr unsafe.Pointer
// Parent object. Holding a pointer to this in Go-land makes the reference
// visible to the garbage collector, and thus prevents it from being
// reclaimed prematurely.
parent *cStruct
// readers-writer lock for dealing with mixing manual calls to Close() with
// GC.
lock sync.RWMutex
}
// Recursively acquire read locks on this object and all parent objects.
func (c *cStruct) rLock() {
c.lock.RLock()
if c.parent != nil {
c.parent.rLock()
}
}
// Recursively release read locks this object and all parent objects.
func (c *cStruct) rUnlock() {
if c.parent != nil {
c.parent.rUnlock()
}
c.lock.RUnlock()
}
// Call f in a context in which it is safe to destroy the underlying C object.
// `f` will only be invoked if the underlying object is still live. When `f`
// is invoked, The calling goroutine will hold the necessary locks to make
// destroying the underlying object safe.
//
// Typically, wrapper types will use this to implement their Close() methods;
// it handles all of the synchronization bits.
func (c *cStruct) doClose(f func() error) error {
// Briefly:
// 1. Acquire a write lock on ourselves.
// 2. Acquire read locks for all of our ancestors in pre-order (the ordering
// is important to avoid deadlocks).
// 3. Check if we're live, and call f if so.
// 4. Clear all of our references to other objects, and release the locks
var err error
c.lock.Lock()
if c.parent != nil {
c.parent.rLock()
}
defer func() {
if c.parent != nil {
c.parent.rUnlock()
}
c.cptr = nil
c.parent = nil
c.lock.Unlock()
}()
if c.live() {
err = f()
}
return err
}
// Returns true if and only if c's underlying object is live, i.e. neither it
// nor any of its ancestor objects have been finalized.
//
// Note that this method does no synchronization; the caller must separately
// acquire the necessary locks.
func (c *cStruct) live() bool {
if c.cptr == nil {
return false
}
if c.parent == nil {
return true
}
return c.parent.live()
}
// Set a finalizer to invoke c.Close() when c is garbage collected.
func setGcClose(c io.Closer) {
runtime.SetFinalizer(c, func(c io.Closer) {
c.Close()
})
}