mirror of
https://github.com/logos-messaging/sds-go-bindings.git
synced 2026-07-23 16:33:16 +00:00
fix: async Cleanup for recycle ABI + guard rmRegistry
sds_destroy is now non-blocking and recycles the context, delivering the drain outcome via the callback. Cleanup blocks on that callback through sdsCall, which also backpressures NewReliabilityManager against recycle so the fixed context pool cannot be exhausted under create/destroy churn. Guard rmRegistry with a RWMutex: it is written by register/unregister (Create/Cleanup) and read by sdsGlobalEventCallback on the nim-ffi event thread, which otherwise triggered a fatal "concurrent map read and map write" once multiple managers ran concurrently. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
d815ea3462
commit
0b8b037adf
17
sds/sds.go
17
sds/sds.go
@ -81,8 +81,8 @@ package sds
|
||||
return sds_create((const uint8_t*) reqCbor, reqCborLen, (SdsCallBack) SdsGoCallback, resp);
|
||||
}
|
||||
|
||||
static int cGoSdsDestroy(void* ctx) {
|
||||
return sds_destroy(ctx);
|
||||
static int cGoSdsDestroy(void* ctx, void* resp) {
|
||||
return sds_destroy(ctx, (SdsCallBack) SdsGoCallback, resp);
|
||||
}
|
||||
|
||||
static unsigned long long cGoSdsAddEventListener(void* ctx, const char* eventName) {
|
||||
@ -269,7 +269,7 @@ func sdsGlobalEventCallback(callerRet C.int, msg *C.char, len C.size_t, userData
|
||||
eventCbor = C.GoBytes(unsafe.Pointer(msg), C.int(len))
|
||||
}
|
||||
|
||||
rm, ok := rmRegistry[userData] // userData carries the rm's ctx handle
|
||||
rm, ok := lookupReliabilityManager(userData) // userData carries the rm's ctx handle
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -288,9 +288,16 @@ func (rm *ReliabilityManager) Cleanup() error {
|
||||
|
||||
rm.logger.Debug("cleaning up reliability manager")
|
||||
|
||||
ret := int(C.cGoSdsDestroy(rm.rmCtx))
|
||||
// sds_destroy recycles the context (it does not stop the worker threads).
|
||||
// It returns immediately; the real outcome is delivered through the callback
|
||||
// once the FFI thread has drained and parked the context, so we block on it
|
||||
// via sdsCall. Blocking here also backpressures Create against recycle so the
|
||||
// fixed context pool can't be exhausted under churn.
|
||||
ret, data := sdsCall(func(resp unsafe.Pointer) {
|
||||
C.cGoSdsDestroy(rm.rmCtx, resp)
|
||||
})
|
||||
if ret != C.RET_OK {
|
||||
return errorspkg.Errorf("error CleanupReliabilityManager: ret code %d", ret)
|
||||
return respError("error CleanupReliabilityManager", ret, data)
|
||||
}
|
||||
|
||||
unregisterReliabilityManager(rm)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package sds
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
@ -29,24 +30,42 @@ type ReliabilityManager struct {
|
||||
// rm is the event being emited for. Since we only have a global
|
||||
// callback in the go side, We register all the rm's that we create
|
||||
// so we can later obtain which instance of `ReliabilityManager` it should
|
||||
// be invoked depending on the ctx received
|
||||
var rmRegistry map[unsafe.Pointer]*ReliabilityManager
|
||||
// be invoked depending on the ctx received.
|
||||
//
|
||||
// rmRegistryMu guards the map: it is written by register/unregister (on
|
||||
// Create/Cleanup) and read by sdsGlobalEventCallback on the nim-ffi event
|
||||
// thread, so concurrent managers would otherwise trigger a fatal
|
||||
// "concurrent map read and map write".
|
||||
var (
|
||||
rmRegistryMu sync.RWMutex
|
||||
rmRegistry map[unsafe.Pointer]*ReliabilityManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
rmRegistry = make(map[unsafe.Pointer]*ReliabilityManager)
|
||||
}
|
||||
|
||||
func registerReliabilityManager(rm *ReliabilityManager) {
|
||||
_, ok := rmRegistry[rm.rmCtx]
|
||||
if !ok {
|
||||
rmRegistryMu.Lock()
|
||||
defer rmRegistryMu.Unlock()
|
||||
if _, ok := rmRegistry[rm.rmCtx]; !ok {
|
||||
rmRegistry[rm.rmCtx] = rm
|
||||
}
|
||||
}
|
||||
|
||||
func unregisterReliabilityManager(rm *ReliabilityManager) {
|
||||
rmRegistryMu.Lock()
|
||||
defer rmRegistryMu.Unlock()
|
||||
delete(rmRegistry, rm.rmCtx)
|
||||
}
|
||||
|
||||
func lookupReliabilityManager(ctx unsafe.Pointer) (*ReliabilityManager, bool) {
|
||||
rmRegistryMu.RLock()
|
||||
defer rmRegistryMu.RUnlock()
|
||||
rm, ok := rmRegistry[ctx]
|
||||
return rm, ok
|
||||
}
|
||||
|
||||
// sdsEventEnvelope is the CBOR wrapper libsds emits for every event:
|
||||
// { eventType: <wire name>, payload: <event struct> }.
|
||||
type sdsEventEnvelope struct {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user