Skip to content

Commit 72938b1

Browse files
committed
fix(all): impl. new apis to make sure safety
1 parent 1902817 commit 72938b1

File tree

8 files changed

+125
-295
lines changed

8 files changed

+125
-295
lines changed

item.go

Lines changed: 30 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package orbyte
22

33
import (
44
"runtime"
5-
"strconv"
65
"sync/atomic"
76
)
87

@@ -16,96 +15,59 @@ type Item[T any] struct {
1615
cfg any
1716

1817
stat status
19-
ref *Item[T]
20-
refc int32 // refc -1 means transferred / destroyed
2118

2219
val T
2320
}
2421

25-
func (b *Item[T]) incref() {
26-
atomic.AddInt32(&b.refc, 1)
27-
}
28-
29-
func (b *Item[T]) decref() {
30-
atomic.AddInt32(&b.refc, -1)
31-
}
32-
33-
// Trans ownership to a new item and
34-
// destroy original item immediately.
35-
//
36-
// The value in new item will not be Reset().
22+
// Trans disable inner val being reset by
23+
// destroy and return a safe copy of val.
3724
//
38-
// Call this function to drop your ownership
39-
// before passing it to another function
40-
// that is not controlled by you.
25+
// This method is not thread-safe.
26+
// Only call once on one item.
27+
// The item will be destroyed after calling Trans().
4128
//
42-
// Avoid to call this function after calling Ref().
43-
func (b *Item[T]) Trans() (tb *Item[T]) {
29+
// Use it to drop your ownership
30+
// before passing val (not its pointer)
31+
// to another function that is not controlled by you.
32+
func (b *Item[T]) Trans() T {
4433
if b.stat.hasdestroyed() {
4534
panic("use after destroy")
4635
}
47-
if b.ref != nil {
48-
panic("cannot trans ref")
49-
}
50-
tb = b.pool.newempty()
51-
*tb = *b
52-
tb.stat = status(atomic.SwapUintptr(
36+
val := b.val
37+
atomic.StoreUintptr(
5338
(*uintptr)(&b.stat), uintptr(destroyedstatus),
54-
))
55-
tb.refc = 0
56-
tb.stat.setintrans(true)
57-
b.destroybystat(status(0))
58-
return tb
39+
)
40+
runtime.KeepAlive(b)
41+
b.destroybystat(0)
42+
return val
5943
}
6044

61-
// HasInvolved whether this item is buffered.
45+
// HasInvolved whether this item is buffered
46+
// and will be Reset on putting back.
6247
func (b *Item[T]) HasInvolved() bool {
6348
return b.stat.isbuffered()
6449
}
6550

66-
// IsTrans whether this item has been marked as trans.
67-
func (b *Item[T]) IsTrans() bool {
68-
return b.stat.isintrans()
69-
}
70-
71-
// IsRef whether this item is a reference.
72-
func (b *Item[T]) IsRef() bool {
73-
return b.ref != nil
74-
}
75-
76-
// Unwrap use value of the item
77-
func (b *Item[T]) Unwrap() T {
78-
if b.stat.hasdestroyed() {
79-
panic("use after destroy")
80-
}
81-
return b.val
82-
}
83-
84-
// Pointer use pointer value of the item
85-
func (b *Item[T]) Pointer() *T {
51+
// V use value of the item.
52+
//
53+
// This operation is safe in function f.
54+
func (b *Item[T]) V(f func(T)) {
8655
if b.stat.hasdestroyed() {
8756
panic("use after destroy")
8857
}
89-
return &b.val
58+
f(b.val)
59+
runtime.KeepAlive(b)
9060
}
9161

92-
// Ref gens new item without ownership.
62+
// P use pointer value of the item.
9363
//
94-
// It's a safe reference, thus calling this
95-
// will not destroy the original item
96-
// comparing with Trans().
97-
func (b *Item[T]) Ref() (rb *Item[T]) {
64+
// This operation is safe in function f.
65+
func (b *Item[T]) P(f func(*T)) {
9866
if b.stat.hasdestroyed() {
9967
panic("use after destroy")
10068
}
101-
rb = b.pool.newempty()
102-
*rb = *b
103-
rb.ref = b
104-
rb.refc = 0
105-
b.incref()
106-
rb.stat.setbuffered(false)
107-
rb.stat.setintrans(false)
108-
return
69+
f(&b.val)
70+
runtime.KeepAlive(b)
10971
}
11072

11173
// Copy data completely with separated ownership.
@@ -119,21 +81,9 @@ func (b *Item[T]) Copy() (cb *Item[T]) {
11981
}
12082

12183
func (b *Item[T]) destroybystat(stat status) {
122-
if !atomic.CompareAndSwapInt32(&b.refc, 0, -1) {
123-
if b.refc < 0 {
124-
panic("use imm. after destroy")
125-
}
126-
panic("cannot destroy: " + strconv.Itoa(int(b.refc)) + " refs remained")
127-
}
128-
if b.ref != nil {
129-
defer b.ref.decref()
130-
}
13184
switch {
13285
case stat.hasdestroyed():
133-
panic("use after put back to pool")
134-
case stat.isintrans():
135-
var v T
136-
b.val = v
86+
panic("destroy after destroy")
13787
case stat.isbuffered():
13888
b.pool.pooler.Reset(&b.val)
13989
default:
@@ -158,10 +108,10 @@ func (b *Item[T]) ManualDestroy() {
158108
// Only can call once.
159109
func (b *Item[T]) setautodestroy() *Item[T] {
160110
runtime.SetFinalizer(b, func(item *Item[T]) {
161-
// no one is using, no concurrency issue.
162111
if item.stat.hasdestroyed() {
163112
panic("unexpected hasdestroyed")
164113
}
114+
// no one is using, no concurrency issue.
165115
item.destroybystat(item.stat)
166116
})
167117
return b

pbuf/buffer.go

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package pbuf
22

33
import (
44
"bytes"
5-
"unsafe"
65

76
"github.com/fumiama/orbyte"
87
)
@@ -21,54 +20,3 @@ func (bufferPool BufferPool) InvolveBuffer(buf *bytes.Buffer) *orbyte.Item[bytes
2120
func (bufferPool BufferPool) ParseBuffer(buf *bytes.Buffer) *orbyte.Item[bytes.Buffer] {
2221
return bufferPool.p.Parse(buf.Len(), buf)
2322
}
24-
25-
// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
26-
// The zero value for Buffer is an empty buffer ready to use.
27-
type buffer struct {
28-
buf []byte // contents are the bytes buf[off : len(buf)]
29-
off int // read at &buf[off], write at &buf[len(buf)]
30-
lastRead readOp // last read operation, so that Unread* can work correctly.
31-
}
32-
33-
func skip(w *bytes.Buffer, n int) {
34-
if n == 0 {
35-
return
36-
}
37-
b := (*buffer)(unsafe.Pointer(w))
38-
b.lastRead = opInvalid
39-
if len(b.buf) <= b.off {
40-
// Buffer is empty, reset to recover space.
41-
w.Reset()
42-
return
43-
}
44-
n = minnum(n, len(b.buf[b.off:]))
45-
b.off += n
46-
if n > 0 {
47-
b.lastRead = opRead
48-
}
49-
}
50-
51-
// The readOp constants describe the last action performed on
52-
// the buffer, so that UnreadRune and UnreadByte can check for
53-
// invalid usage. opReadRuneX constants are chosen such that
54-
// converted to int they correspond to the rune size that was read.
55-
type readOp int8
56-
57-
// Don't use iota for these, as the values need to correspond with the
58-
// names and comments, which is easier to see when being explicit.
59-
const (
60-
opRead readOp = -1 // Any other read operation.
61-
opInvalid readOp = 0 // Non-read operation.
62-
opReadRune1 readOp = 1 // Read rune of size 1.
63-
opReadRune2 readOp = 2 // Read rune of size 2.
64-
opReadRune3 readOp = 3 // Read rune of size 3.
65-
opReadRune4 readOp = 4 // Read rune of size 4.
66-
)
67-
68-
// minnum 返回两数最小值,该函数将被内联
69-
func minnum[T int | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64](a, b T) T {
70-
if a > b {
71-
return b
72-
}
73-
return a
74-
}

pbuf/buffer_test.go

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,24 @@ func TestBuffer(t *testing.T) {
2020
}
2121

2222
func testBuffer(buf *orbyte.Item[bytes.Buffer], t *testing.T) {
23-
if buf.Pointer().Len() != 4096 {
24-
io.CopyN(buf.Pointer(), rand.Reader, 4096)
25-
if buf.Pointer().Len() != 4096 {
26-
t.Fatal("got", buf.Pointer().Len())
23+
buf.P(func(buf *bytes.Buffer) {
24+
if buf.Len() != 4096 {
25+
io.CopyN(buf, rand.Reader, 4096)
26+
if buf.Len() != 4096 {
27+
t.Fatal("got", buf.Len())
28+
}
2729
}
28-
}
29-
30+
})
3031
bufcp := buf.Copy()
31-
if bufcp.Pointer().Len() != 4096 {
32-
t.Fatal("got", bufcp.Pointer().Len())
33-
}
34-
if !bytes.Equal(bufcp.Pointer().Bytes(), buf.Pointer().Bytes()) {
35-
t.Fatal("unexpected")
36-
}
37-
38-
bufr := buf.Ref()
39-
if bufr.Pointer().Len() != 4096 {
40-
t.Fatal("got", bufr.Pointer().Len())
41-
}
42-
if !bytes.Equal(bufr.Pointer().Bytes(), buf.Pointer().Bytes()) {
43-
t.Fatal("unexpected")
44-
}
45-
bufr.ManualDestroy()
46-
47-
bufcp = bufcp.Trans()
48-
if bufcp.Pointer().Len() != 4096 {
49-
t.Fatal("got", bufcp.Pointer().Len())
50-
}
51-
if !bytes.Equal(bufcp.Pointer().Bytes(), buf.Pointer().Bytes()) {
52-
t.Fatal("unexpected")
53-
}
54-
bufcp.ManualDestroy()
32+
dat := buf.Trans()
33+
bufcp.P(func(bufcp *bytes.Buffer) {
34+
if bufcp.Len() != 4096 {
35+
t.Fatal("got", bufcp.Len())
36+
}
37+
if !bytes.Equal(bufcp.Bytes(), dat.Bytes()) {
38+
t.Fatal("unexpected")
39+
}
40+
})
5541

5642
runtime.GC()
5743
runtime.Gosched()

0 commit comments

Comments
 (0)