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()
})
}
|