1
0
mirror of https://github.com/fumiama/orbyte.git synced 2026-06-05 02:00:30 +08:00
Files
orbyte/item.go
2025-05-13 00:52:42 +09:00

168 lines
3.6 KiB
Go

package orbyte
import (
"runtime"
"sync/atomic"
)
// Item represents a thread-safe user-defined value.
//
// Only the item that has ownership can be a pointer.
// Do not copy neither Item nor *Item by yourself.
// You must always use the given methods.
type Item[T any] struct {
pool *Pool[T]
stat status
// align 64
cfg any
// align 64
// id only take effect on issync = True
id int64
// align 64
val T
}
// Ignore marks Item to be independent and will not be
// put back.
func (b *Item[T]) Ignore() *Item[T] {
b.stat.setignored(true)
return b
}
// Trans disable inner val being reset by
// destroy and return a safe copy of val.
//
// This method is not thread-safe.
// Only call once on one item.
// The item will be destroyed after calling Trans().
//
// Use it to drop your ownership
// before passing val (not its pointer)
// to another function that is not controlled by you.
func (b *Item[T]) Trans() T {
if b.stat.hasdestroyed() {
panic("use after destroy")
}
if b.pool.issync {
if !b.stat.setinsyncop(true) {
panic("non-unique op")
}
}
val := b.val
atomic.StoreUintptr(
(*uintptr)(&b.stat), uintptr(destroyedstatus),
)
runtime.KeepAlive(b)
b.destroybystat(0)
return val
}
// HasInvolved whether this item is buffered
// and will be Reset on putting back.
func (b *Item[T]) HasInvolved() bool {
if b.pool.issync {
if !b.stat.setinsyncop(true) && getGoroutineID() != b.id {
panic("non-unique op")
}
atomic.StoreInt64(&b.id, getGoroutineID())
defer b.stat.setinsyncop(false)
}
return b.stat.isbuffered()
}
// V use value of the item.
//
// This operation is safe in function f.
func (b *Item[T]) V(f func(T)) *Item[T] {
if b.stat.hasdestroyed() {
panic("use after destroy")
}
if b.pool.issync {
if !b.stat.setinsyncop(true) && getGoroutineID() != b.id {
panic("non-unique op")
}
atomic.StoreInt64(&b.id, getGoroutineID())
defer b.stat.setinsyncop(false)
}
f(b.val)
runtime.KeepAlive(b)
return b
}
// P use pointer value of the item.
//
// This operation is safe in function f.
func (b *Item[T]) P(f func(*T)) *Item[T] {
if b.stat.hasdestroyed() {
panic("use after destroy")
}
if b.pool.issync {
if !b.stat.setinsyncop(true) && getGoroutineID() != b.id {
panic("non-unique op")
}
atomic.StoreInt64(&b.id, getGoroutineID())
defer b.stat.setinsyncop(false)
}
f(&b.val)
runtime.KeepAlive(b)
return b
}
// Copy data completely with separated ownership.
func (b *Item[T]) Copy() (cb *Item[T]) {
if b.stat.hasdestroyed() {
panic("use after destroy")
}
if b.pool.issync {
if !b.stat.setinsyncop(true) && getGoroutineID() != b.id {
panic("non-unique op")
}
atomic.StoreInt64(&b.id, getGoroutineID())
defer b.stat.setinsyncop(false)
}
cb = b.pool.New(b.cfg)
b.pool.pooler.Copy(&cb.val, &b.val)
return
}
func (b *Item[T]) destroybystat(stat status) {
switch {
case stat.hasdestroyed():
panic("destroy after destroy")
case stat.isbuffered():
b.pool.pooler.Reset(&b.val)
default:
var v T
b.val = v
}
b.pool.put(b, stat.hasignored())
}
// ManualDestroy item and put it back to pool.
//
// Calling this method only when you're sure that
// no one will use it, or it will cause a panic.
func (b *Item[T]) ManualDestroy() {
if b.pool.issync {
b.stat.setinsyncop(true)
}
runtime.SetFinalizer(b, nil)
b.destroybystat(status(atomic.SwapUintptr(
(*uintptr)(&b.stat), uintptr(destroyedstatus),
)))
}
// setautodestroy item on GC.
//
// Only can call once.
func (b *Item[T]) setautodestroy() *Item[T] {
runtime.SetFinalizer(b, func(item *Item[T]) {
// no one is using, no concurrency issue.
item.destroybystat(item.stat)
})
return b
}