Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
neo
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
Kirill Smelkov
neo
Commits
e39e6682
Commit
e39e6682
authored
Oct 26, 2020
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
.
parent
dea433be
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
131 additions
and
8 deletions
+131
-8
go/zodb/internal/weak/weak.go
go/zodb/internal/weak/weak.go
+26
-2
go/zodb/internal/weak/weak_test.go
go/zodb/internal/weak/weak_test.go
+39
-6
go/zodb/internal/weak/ztrace.go
go/zodb/internal/weak/ztrace.go
+66
-0
No files found.
go/zodb/internal/weak/weak.go
View file @
e39e6682
// Copyright (C) 2018 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
// Copyright (C) 2018
-2020
Nexedi SA and Contributors.
//
Kirill Smelkov <kirr@nexedi.com>
//
// based on:
// https://groups.google.com/d/msg/golang-nuts/PYWxjT2v6ps/dL71oJk1mXEJ
...
...
@@ -24,15 +24,20 @@
// Package weak provides weak references for Go.
package
weak
//go:generate gotrace gen .
import
(
"fmt"
"runtime"
"sync"
// "time"
"unsafe"
)
// iface is how Go runtime represents an interface.
//
// NOTE layout must be synchronized to Go runtime representation.
// NOTE correctness depends on non-moving property of Go GC.
type
iface
struct
{
typ
uintptr
// type
data
uintptr
// data
...
...
@@ -63,6 +68,8 @@ type Ref struct {
state
weakRefState
}
//trace:event traceRelease(w *Ref, released bool)
// NewRef creates new weak reference pointing to obj.
//
// TODO + onrelease callback?
...
...
@@ -77,6 +84,12 @@ func NewRef(obj interface{}) *Ref {
var
release
func
(
interface
{})
release
=
func
(
obj
interface
{})
{
// assert that the object was not moved
iobj
:=
*
(
*
iface
)(
unsafe
.
Pointer
(
&
obj
))
if
w
.
iface
!=
iobj
{
panic
(
fmt
.
Sprintf
(
"weak: release: object moved: w.iface=%x obj=%x"
,
w
.
iface
,
iobj
))
}
// GC decided that the object is no longer reachable and
// scheduled us to run as finalizer. During the time till we
// actually run, Ref.Get might have been come to run and
...
...
@@ -87,16 +100,21 @@ func NewRef(obj interface{}) *Ref {
if
w
.
state
==
objGot
{
w
.
state
=
objLive
runtime
.
SetFinalizer
(
obj
,
release
)
traceRelease
(
w
,
false
)
}
else
{
w
.
state
=
objReleased
traceRelease
(
w
,
true
)
}
w
.
mu
.
Unlock
()
}
runtime
.
SetFinalizer
(
obj
,
release
)
return
w
}
//trace:event traceGotPre(w *Ref)
// Get returns object pointed to by this weak reference.
//
// If original object is still alive - it is returned.
...
...
@@ -106,6 +124,12 @@ func (w *Ref) Get() (obj interface{}) {
if
w
.
state
!=
objReleased
{
w
.
state
=
objGot
traceGotPre
(
w
)
//time.Sleep(100*time.Nanosecond)
//time.Sleep(10*time.Millisecond)
//runtime.GC()
//runtime.GC()
// recreate interface{} from saved words.
// XXX do writes as pointers so that compiler emits write barriers to notify GC?
i
:=
(
*
iface
)(
unsafe
.
Pointer
(
&
obj
))
...
...
go/zodb/internal/weak/weak_test.go
View file @
e39e6682
...
...
@@ -24,6 +24,8 @@ import (
"testing"
"time"
"unsafe"
"lab.nexedi.com/kirr/go123/tracing"
)
// verify that interface <-> iface works ok.
...
...
@@ -60,6 +62,22 @@ func TestWeakRef(t *testing.T) {
w
:=
NewRef
(
p
)
pptr
:=
uintptr
(
unsafe
.
Pointer
(
p
))
wrelease
:=
make
(
chan
bool
)
// events from traceRelease(w)
tpg
:=
&
tracing
.
ProbeGroup
{}
tracing
.
Lock
()
traceRelease_Attach
(
tpg
,
func
(
w_
*
Ref
,
released
bool
)
{
if
w_
!=
w
{
panic
(
"release: w != w_"
)
}
wrelease
<-
released
})
traceGotPre_Attach
(
tpg
,
func
(
w
*
Ref
)
{
// nop for now
//panic("TODO GotPre")
})
tracing
.
Unlock
()
defer
tpg
.
Done
()
assertEq
:=
func
(
a
,
b
interface
{})
{
t
.
Helper
()
if
a
!=
b
{
...
...
@@ -68,24 +86,39 @@ func TestWeakRef(t *testing.T) {
}
// perform GC + give finalizers a chance to run.
GC
:=
func
()
{
GC
:=
func
(
expectRelease
bool
)
{
t
.
Helper
()
runtime
.
GC
()
// GC only queues finalizers, not runs them directly. Give it
// some time so that finalizers could have been run.
time
.
Sleep
(
10
*
time
.
Millisecond
)
// XXX hack
if
expectRelease
{
select
{
case
<-
wrelease
:
// ok
case
<-
time
.
After
(
100
*
time
.
Millisecond
)
:
t
.
Fatal
(
"no release event"
)
}
}
else
{
select
{
case
<-
time
.
After
(
10
*
time
.
Millisecond
)
:
// ok
case
<-
wrelease
:
t
.
Fatal
(
"unexpected release event"
)
}
}
}
assertEq
(
w
.
state
,
objLive
)
assertEq
(
w
.
Get
(),
p
)
assertEq
(
w
.
state
,
objGot
)
GC
()
GC
(
false
)
assertEq
(
w
.
state
,
objGot
)
// fin has not been run at all (p is live)
assertEq
(
w
.
Get
(),
p
)
assertEq
(
w
.
state
,
objGot
)
p
=
nil
GC
()
GC
(
true
)
assertEq
(
w
.
state
,
objLive
)
// fin ran and downgraded got -> live
switch
p_
:=
w
.
Get
()
.
(
type
)
{
default
:
...
...
@@ -97,10 +130,10 @@ func TestWeakRef(t *testing.T) {
}
assertEq
(
w
.
state
,
objGot
)
GC
()
GC
(
true
)
assertEq
(
w
.
state
,
objLive
)
// fin ran again and again downgraded got -> live
GC
()
GC
(
true
)
assertEq
(
w
.
state
,
objReleased
)
// fin ran again and released the object
assertEq
(
w
.
Get
(),
nil
)
}
go/zodb/internal/weak/ztrace.go
0 → 100644
View file @
e39e6682
// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.
package
weak
// code generated for tracepoints
import
(
"lab.nexedi.com/kirr/go123/tracing"
"unsafe"
)
// traceevent: traceGotPre(w *Ref)
type
_t_traceGotPre
struct
{
tracing
.
Probe
probefunc
func
(
w
*
Ref
)
}
var
_traceGotPre
*
_t_traceGotPre
func
traceGotPre
(
w
*
Ref
)
{
if
_traceGotPre
!=
nil
{
_traceGotPre_run
(
w
)
}
}
func
_traceGotPre_run
(
w
*
Ref
)
{
for
p
:=
_traceGotPre
;
p
!=
nil
;
p
=
(
*
_t_traceGotPre
)(
unsafe
.
Pointer
(
p
.
Next
()))
{
p
.
probefunc
(
w
)
}
}
func
traceGotPre_Attach
(
pg
*
tracing
.
ProbeGroup
,
probe
func
(
w
*
Ref
))
*
tracing
.
Probe
{
p
:=
_t_traceGotPre
{
probefunc
:
probe
}
tracing
.
AttachProbe
(
pg
,
(
**
tracing
.
Probe
)(
unsafe
.
Pointer
(
&
_traceGotPre
)),
&
p
.
Probe
)
return
&
p
.
Probe
}
// traceevent: traceRelease(w *Ref, released bool)
type
_t_traceRelease
struct
{
tracing
.
Probe
probefunc
func
(
w
*
Ref
,
released
bool
)
}
var
_traceRelease
*
_t_traceRelease
func
traceRelease
(
w
*
Ref
,
released
bool
)
{
if
_traceRelease
!=
nil
{
_traceRelease_run
(
w
,
released
)
}
}
func
_traceRelease_run
(
w
*
Ref
,
released
bool
)
{
for
p
:=
_traceRelease
;
p
!=
nil
;
p
=
(
*
_t_traceRelease
)(
unsafe
.
Pointer
(
p
.
Next
()))
{
p
.
probefunc
(
w
,
released
)
}
}
func
traceRelease_Attach
(
pg
*
tracing
.
ProbeGroup
,
probe
func
(
w
*
Ref
,
released
bool
))
*
tracing
.
Probe
{
p
:=
_t_traceRelease
{
probefunc
:
probe
}
tracing
.
AttachProbe
(
pg
,
(
**
tracing
.
Probe
)(
unsafe
.
Pointer
(
&
_traceRelease
)),
&
p
.
Probe
)
return
&
p
.
Probe
}
// trace export signature
func
_trace_exporthash_c65df3d81975319429a288a09095370dace6a87b
()
{}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment