diff --git a/item.go b/item.go index 959c40a..0fd12d1 100644 --- a/item.go +++ b/item.go @@ -18,6 +18,10 @@ type Item[T any] struct { cfg any // align 64 + // id only take effect on issync = True + id int64 + // align 64 + val T } @@ -36,7 +40,9 @@ func (b *Item[T]) Trans() T { panic("use after destroy") } if b.pool.issync { - b.stat.setinsyncop(true) + if !b.stat.setinsyncop(true) { + panic("non-unique op") + } } val := b.val atomic.StoreUintptr( @@ -51,7 +57,10 @@ func (b *Item[T]) Trans() T { // and will be Reset on putting back. func (b *Item[T]) HasInvolved() bool { if b.pool.issync { - b.stat.setinsyncop(true) + 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() @@ -65,7 +74,10 @@ func (b *Item[T]) V(f func(T)) { panic("use after destroy") } if b.pool.issync { - b.stat.setinsyncop(true) + 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) @@ -80,7 +92,10 @@ func (b *Item[T]) P(f func(*T)) { panic("use after destroy") } if b.pool.issync { - b.stat.setinsyncop(true) + 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) @@ -93,7 +108,10 @@ func (b *Item[T]) Copy() (cb *Item[T]) { panic("use after destroy") } if b.pool.issync { - b.stat.setinsyncop(true) + 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) diff --git a/status.go b/status.go index f9f3d7f..bebd0f4 100644 --- a/status.go +++ b/status.go @@ -1,6 +1,11 @@ package orbyte -import "sync/atomic" +import ( + "runtime" + "strconv" + "strings" + "sync/atomic" +) const ( statusisbuffered = 1 << iota @@ -16,6 +21,16 @@ func init() { destroyedstatus.setdestroyed(true) } +func getGoroutineID() int64 { + var buf [64]byte + n := runtime.Stack(buf[:], false) + idField := strings.Fields(string(buf[:n]))[1] + id, err := strconv.ParseInt(idField, 10, 64) + if err != nil { + panic(err) + } + return id +} func (c status) mask(v bool, typ uintptr) (news status) { news = c if v { @@ -39,22 +54,23 @@ func (c *status) setbool(v bool, typ uintptr) { } } -// setboolunique panic on non-unique set -func (c *status) setboolunique(v bool, typ uintptr) { +// setboolunique return false on non-unique set +func (c *status) setboolunique(v bool, typ uintptr) bool { olds := atomic.LoadUintptr((*uintptr)(c)) oldv := olds&typ != 0 if oldv == v { - panic("non-unique operation") + return false } news := status(olds).mask(v, typ) for !atomic.CompareAndSwapUintptr((*uintptr)(c), olds, uintptr(news)) { olds = atomic.LoadUintptr((*uintptr)(c)) oldv = olds&typ != 0 if oldv == v { - panic("non-unique operation") + return false } news = status(olds).mask(v, typ) } + return true } func (c *status) loadbool(typ uintptr) bool { @@ -77,6 +93,6 @@ func (c *status) setdestroyed(v bool) { c.setbool(v, statusdestroyed) } -func (c *status) setinsyncop(v bool) { - c.setboolunique(v, statusinsyncop) +func (c *status) setinsyncop(v bool) bool { + return c.setboolunique(v, statusinsyncop) }