-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdoc.go
More file actions
109 lines (93 loc) · 2.16 KB
/
doc.go
File metadata and controls
109 lines (93 loc) · 2.16 KB
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
102
103
104
105
106
107
108
109
package purejson
import (
"fmt"
"os"
"runtime"
"sync"
"github.com/amikos-tech/pure-simdjson/internal/ffi"
)
// Doc wraps one live native document handle plus its cached root view.
type Doc struct {
mu sync.Mutex
parser *Parser
handle ffi.DocHandle
root ffi.ValueView
closed bool
}
// Root returns the cached root element view for the live document.
func (d *Doc) Root() Element {
return Element{doc: d, view: d.root}
}
// Close releases the native document and clears the owning parser's busy
// state. It is idempotent.
func (d *Doc) Close() error {
d.mu.Lock()
if d.closed {
d.mu.Unlock()
return nil
}
handle := d.handle
parser := d.parser
library := parser.library
clearDocFinalizer(d)
rc := library.bindings.DocFree(handle)
runtime.KeepAlive(d)
runtime.KeepAlive(d.parser)
if err := wrapStatus(rc); err != nil {
attachDocFinalizer(d)
d.mu.Unlock()
if leakWarningsEnabled() {
fmt.Fprintf(os.Stderr, "purejson close-failed: doc %v\n", err)
}
return err
}
d.closed = true
d.handle = 0
d.mu.Unlock()
parser.clearLiveDoc(handle)
return nil
}
func (d *Doc) isClosed() bool {
// Use TryLock rather than Lock: callers on the fast path may hold d.mu
// already; blocking here would deadlock. Contention therefore reports
// "not closed" -- the outer caller's own guard surfaces ErrParserBusy.
if d.mu.TryLock() {
closed := d.closed
d.mu.Unlock()
return closed
}
return false
}
func (d *Doc) hasLeakedState() bool {
d.mu.Lock()
defer d.mu.Unlock()
return !d.closed && d.handle != 0
}
// finalizeLeaked frees the native document from the GC finalizer. It releases
// the mutex around the FFI call so a stuck native side cannot hold the runtime,
// then re-acquires it to commit state only when the free succeeded.
func (d *Doc) finalizeLeaked() {
d.mu.Lock()
if d.closed {
d.mu.Unlock()
return
}
handle := d.handle
parser := d.parser
library := parser.library
d.mu.Unlock()
rc := library.bindings.DocFree(handle)
docFreed := rc == int32(ffi.OK)
if docFreed {
docFinalizerCount.Add(1)
}
d.mu.Lock()
if docFreed {
d.closed = true
d.handle = 0
}
d.mu.Unlock()
if docFreed {
parser.clearLiveDoc(handle)
}
}