github.com/hsfzxjy/dgo/go@v0.2.0/pin/token.go (about) 1 package pin 2 3 import ( 4 "sync" 5 "unsafe" 6 7 "github.com/hsfzxjy/dgo/go/pin/pcop" 8 ) 9 10 type rawToken struct { 11 version uint16 12 lid uint8 13 _pad [1]byte 14 meta *Meta 15 } 16 17 type Token[T any] struct { 18 *rawToken 19 //lint:ingore U1000 unexportable marker 20 _ struct{} 21 } 22 23 var rawTokenPool = sync.Pool{ 24 New: func() any { return new(rawToken) }, 25 } 26 27 func newToken(meta *Meta, version uint16, lid uint8) untypedToken { 28 rt := rawTokenPool.Get().(*rawToken) 29 rt.version = version 30 rt.meta = meta 31 rt.lid = lid 32 return Token[struct{}]{rawToken: rt} 33 } 34 35 // t should be dropped after Dispose() invoked 36 func (t *Token[T]) Dispose() (success bool) { 37 if t.IsEmpty() { 38 return false 39 } 40 t.meta.decref(t.version, t.lid) 41 untypedTokenLeak(untypedToken(*t)) 42 return true 43 } 44 45 func (t Token[T]) Data() *T { 46 if t.IsEmpty() { 47 panic("dgo:go: Data() called on an empty Token") 48 } 49 return (*T)(unsafe.Pointer(t.meta)) 50 } 51 52 func (t *Token[T]) IsEmpty() bool { return t.rawToken == nil || t.rawToken.meta == nil } 53 54 type untypedToken = Token[struct{}] 55 56 //lint:ignore U1000 go:linkname 57 func untypedTokenFromRaw(version uint16, lid uint8, data uintptr) (ret untypedToken) { 58 meta, ok := pinTable.m.Load(data) 59 if !ok { 60 return 61 } 62 63 LOAD_FLAG: 64 flag := meta.flag.Load() 65 switch flag { 66 case accessing: 67 goto LOAD_FLAG 68 case attached_not_intable, attached_intable: 69 runtime_procPin() 70 if !meta.flag.CompareAndSwap(flag, accessing) { 71 runtime_procUnpin() 72 goto LOAD_FLAG 73 } 74 if meta.version == version && 75 meta.lids.Test(lid) { 76 ret = newToken(meta, version, lid) 77 } 78 // else: the version is mismatched or lid is invalid, we return an empty token 79 meta.flag.Store(flag) 80 runtime_procUnpin() 81 return 82 case detached: 83 return 84 } 85 86 return 87 } 88 89 //lint:ignore U1000 go:linkname 90 func untypedTokenLeak(token untypedToken) { 91 token.version = 0 92 token.lid = 0 93 token.meta = nil 94 rawTokenPool.Put(token.rawToken) 95 } 96 97 //lint:ignore U1000 go:linkname 98 func untypedTokenExtract(token untypedToken) (version uint16, lid uint8, data uintptr) { 99 version = token.version 100 lid = token.lid 101 data = uintptr(unsafe.Pointer(token.meta)) 102 return version, lid, data 103 } 104 105 //go:linkname pin_TokenDispose github.com/hsfzxjy/dgo/go.pin_TokenDispose 106 func pin_TokenDispose(version uint16, lid uint8, data uintptr) { 107 token := untypedTokenFromRaw(uint16(version), uint8(lid), uintptr(data)) 108 token.Dispose() 109 } 110 111 //go:linkname pin_ChanListen github.com/hsfzxjy/dgo/go.pin_ChanListen 112 func pin_ChanListen(version uint16, lid uint8, data uintptr, chid uint8, dcb uint32, port unsafe.Pointer) { 113 token := untypedTokenFromRaw(uint16(version), uint8(lid), uintptr(data)) 114 if token.IsEmpty() { 115 return 116 } 117 token.meta.ops <- pcop.Op{Kind: pcop.CHAN_LISTEN, Lid: lid, Chid: chid, Dcb: dcb, Port: port} 118 } 119 120 //go:linkname pin_ChanCancelListen github.com/hsfzxjy/dgo/go.pin_ChanCancelListen 121 func pin_ChanCancelListen(version uint16, lid uint8, data uintptr, chid uint8) { 122 token := untypedTokenFromRaw(uint16(version), uint8(lid), uintptr(data)) 123 if token.IsEmpty() { 124 return 125 } 126 token.meta.ops <- pcop.Op{Kind: pcop.CHAN_CANCEL_LISTEN, Lid: lid, Chid: chid} 127 } 128 129 var _ = pin_ChanCancelListen