File: cstruct.go

package info (click to toggle)
golang-github-zenhack-go.notmuch 0.0~git20190821.5a19619-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, sid, trixie
  • size: 240 kB
  • sloc: makefile: 22
file content (101 lines) | stat: -rw-r--r-- 2,666 bytes parent folder | download
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()
	})
}