Initial working fully go release
This commit is contained in:
+61
-57
@@ -53,19 +53,17 @@ type monState struct {
|
||||
// - sid, dbfType, count, access: valid only while readyC is closed.
|
||||
// - readyC: closed when CREATE_CHAN reply received; replaced on reconnect.
|
||||
// - monitors: append-only (except removeMonitor); never cleared on reconnect.
|
||||
// - pending: ioid → GET reply channel; drained (channels closed) on reconnect.
|
||||
type chanState struct {
|
||||
cid uint32
|
||||
pvName string
|
||||
|
||||
mu sync.RWMutex
|
||||
sid uint32 // server-assigned channel ID (0 until CREATE_CHAN reply)
|
||||
dbfType int // native DBF field type
|
||||
count uint32 // element count
|
||||
access uint32 // AccessRead | AccessWrite bitmask
|
||||
readyC chan struct{} // closed once CREATE_CHAN reply is received
|
||||
monitors []*monState // active subscriptions
|
||||
pending map[uint32]chan reply // ioid → one-shot GET callback
|
||||
sid uint32 // server-assigned channel ID (0 until CREATE_CHAN reply)
|
||||
dbfType int // native DBF field type
|
||||
count uint32 // element count
|
||||
access uint32 // AccessRead | AccessWrite bitmask
|
||||
readyC chan struct{} // closed once CREATE_CHAN reply is received
|
||||
monitors []*monState // active subscriptions
|
||||
}
|
||||
|
||||
func newChanState(cid uint32, pvName string) *chanState {
|
||||
@@ -73,7 +71,6 @@ func newChanState(cid uint32, pvName string) *chanState {
|
||||
cid: cid,
|
||||
pvName: pvName,
|
||||
readyC: make(chan struct{}),
|
||||
pending: make(map[uint32]chan reply),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,16 +78,12 @@ func newChanState(cid uint32, pvName string) *chanState {
|
||||
// It closes the current readyC (waking any waitReady callers so they can
|
||||
// re-wait on the fresh channel) and then replaces it.
|
||||
// Must be called before the circuit sends CREATE_CHAN on the new conn.
|
||||
// In-flight GET requests are tracked in circuit.byIOID and cleared there.
|
||||
func (cs *chanState) resetForReconnect() {
|
||||
cs.mu.Lock()
|
||||
cs.sid = 0
|
||||
old := cs.readyC
|
||||
cs.readyC = make(chan struct{})
|
||||
// Drain pending GETs — they will receive zero reply (ok=false).
|
||||
for id, ch := range cs.pending {
|
||||
close(ch)
|
||||
delete(cs.pending, id)
|
||||
}
|
||||
cs.mu.Unlock()
|
||||
// Close the old readyC outside the lock to avoid deadlock with waitReady.
|
||||
select {
|
||||
@@ -150,7 +143,9 @@ type circuit struct {
|
||||
mu sync.RWMutex
|
||||
channels []*chanState // append-only; survive reconnect
|
||||
byCID map[uint32]*chanState // cid → chanState (fast lookup)
|
||||
bySID map[uint32]*chanState // sid → chanState (fast lookup)
|
||||
bySubID map[uint32]*monState // subID → monState (fast event dispatch)
|
||||
byIOID map[uint32]chan reply // ioid → GET/PUT callback (circuit-wide)
|
||||
|
||||
writeQ chan []byte // serialised outbound message queue
|
||||
seq atomic.Uint32 // shared ID sequence (cid, ioid, subID)
|
||||
@@ -165,7 +160,9 @@ func newCircuit(ctx context.Context, addr, clientName, hostName string) *circuit
|
||||
ctx: cctx,
|
||||
cancel: cancel,
|
||||
byCID: make(map[uint32]*chanState),
|
||||
bySID: make(map[uint32]*chanState),
|
||||
bySubID: make(map[uint32]*monState),
|
||||
byIOID: make(map[uint32]chan reply),
|
||||
writeQ: make(chan []byte, writeQueueDepth),
|
||||
}
|
||||
go c.run()
|
||||
@@ -204,6 +201,12 @@ func (c *circuit) run() {
|
||||
|
||||
// Snapshot channels and reset their per-connection state.
|
||||
c.mu.Lock()
|
||||
c.bySID = make(map[uint32]*chanState)
|
||||
// Close all in-flight GET reply channels so waiters unblock with ok=false.
|
||||
for _, ch := range c.byIOID {
|
||||
close(ch)
|
||||
}
|
||||
c.byIOID = make(map[uint32]chan reply)
|
||||
for _, cs := range c.channels {
|
||||
cs.resetForReconnect()
|
||||
}
|
||||
@@ -361,9 +364,13 @@ func (c *circuit) dispatch(conn net.Conn, hdr proto.Header, payload []byte) {
|
||||
|
||||
case proto.CmdCreateChan:
|
||||
// Parameter1 = cid (echoed), Parameter2 = SID assigned by server.
|
||||
c.mu.RLock()
|
||||
c.mu.Lock()
|
||||
cs, ok := c.byCID[hdr.Parameter1]
|
||||
c.mu.RUnlock()
|
||||
if ok {
|
||||
c.bySID[hdr.Parameter2] = cs
|
||||
}
|
||||
c.mu.Unlock()
|
||||
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@@ -425,21 +432,23 @@ func (c *circuit) dispatch(conn net.Conn, hdr proto.Header, payload []byte) {
|
||||
}
|
||||
|
||||
case proto.CmdEventAdd:
|
||||
// Monitor update: Parameter1 = subscriptionID.
|
||||
// Monitor update: server puts subscription ID in Parameter2 (m_available).
|
||||
// Parameter1 carries ECA_NORMAL (1) as a status code.
|
||||
subID := hdr.Parameter2
|
||||
c.mu.RLock()
|
||||
ms, ok := c.bySubID[hdr.Parameter1]
|
||||
ms, ok := c.bySubID[subID]
|
||||
c.mu.RUnlock()
|
||||
if !ok {
|
||||
dbg("CA EVENT_ADD for unknown subID", "subID", hdr.Parameter1)
|
||||
dbg("CA EVENT_ADD for unknown subID", "subID", subID)
|
||||
return
|
||||
}
|
||||
tv, ok := proto.DecodeTimeValue(hdr.DataType, hdr.DataCount, payload)
|
||||
if !ok {
|
||||
dbg("CA EVENT_ADD decode failed", "subID", hdr.Parameter1,
|
||||
dbg("CA EVENT_ADD decode failed", "subID", subID,
|
||||
"dbrType", hdr.DataType, "count", hdr.DataCount, "payloadLen", len(payload))
|
||||
return
|
||||
}
|
||||
dbg("CA EVENT_ADD value", "subID", hdr.Parameter1, "dbrType", hdr.DataType,
|
||||
dbg("CA EVENT_ADD value", "subID", subID, "dbrType", hdr.DataType,
|
||||
"double", tv.Double, "severity", tv.Severity)
|
||||
select {
|
||||
case ms.ch <- tv:
|
||||
@@ -447,25 +456,20 @@ func (c *circuit) dispatch(conn net.Conn, hdr proto.Header, payload []byte) {
|
||||
}
|
||||
|
||||
case proto.CmdReadNotify:
|
||||
// GET reply: Parameter1 = ioid.
|
||||
ioid := hdr.Parameter1
|
||||
c.mu.RLock()
|
||||
var replyCh chan reply
|
||||
var found bool
|
||||
for _, cs := range c.channels {
|
||||
cs.mu.Lock()
|
||||
if ch, exists := cs.pending[ioid]; exists {
|
||||
delete(cs.pending, ioid)
|
||||
replyCh = ch
|
||||
found = true
|
||||
}
|
||||
cs.mu.Unlock()
|
||||
if found {
|
||||
break
|
||||
}
|
||||
// Parameter2 = ioid (our request ID echoed back by server).
|
||||
ioid := hdr.Parameter2
|
||||
|
||||
c.mu.Lock()
|
||||
replyCh, ok := c.byIOID[ioid]
|
||||
if ok {
|
||||
delete(c.byIOID, ioid)
|
||||
}
|
||||
c.mu.RUnlock()
|
||||
if found {
|
||||
c.mu.Unlock()
|
||||
|
||||
dbg("CA CmdReadNotify", "ioid", ioid, "found", ok, "dbrType", hdr.DataType,
|
||||
"count", hdr.DataCount, "payloadSize", hdr.PayloadSize)
|
||||
|
||||
if ok {
|
||||
select {
|
||||
case replyCh <- reply{hdr: hdr, payload: payload, ok: true}:
|
||||
default:
|
||||
@@ -615,9 +619,9 @@ func (c *circuit) getRaw(ctx context.Context, cs *chanState, dbrType uint16, cou
|
||||
ioid := c.nextID()
|
||||
replyCh := make(chan reply, 1)
|
||||
|
||||
cs.mu.Lock()
|
||||
cs.pending[ioid] = replyCh
|
||||
cs.mu.Unlock()
|
||||
c.mu.Lock()
|
||||
c.byIOID[ioid] = replyCh
|
||||
c.mu.Unlock()
|
||||
|
||||
msg := proto.BuildMessage(proto.Header{
|
||||
Command: proto.CmdReadNotify,
|
||||
@@ -630,9 +634,9 @@ func (c *circuit) getRaw(ctx context.Context, cs *chanState, dbrType uint16, cou
|
||||
select {
|
||||
case c.writeQ <- msg:
|
||||
case <-ctx.Done():
|
||||
cs.mu.Lock()
|
||||
delete(cs.pending, ioid)
|
||||
cs.mu.Unlock()
|
||||
c.mu.Lock()
|
||||
delete(c.byIOID, ioid)
|
||||
c.mu.Unlock()
|
||||
return proto.Header{}, nil, ctx.Err()
|
||||
}
|
||||
|
||||
@@ -643,9 +647,9 @@ func (c *circuit) getRaw(ctx context.Context, cs *chanState, dbrType uint16, cou
|
||||
}
|
||||
return r.hdr, r.payload, nil
|
||||
case <-ctx.Done():
|
||||
cs.mu.Lock()
|
||||
delete(cs.pending, ioid)
|
||||
cs.mu.Unlock()
|
||||
c.mu.Lock()
|
||||
delete(c.byIOID, ioid)
|
||||
c.mu.Unlock()
|
||||
return proto.Header{}, nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
@@ -667,9 +671,9 @@ func (c *circuit) get(ctx context.Context, cs *chanState, dbrType uint16, count
|
||||
ioid := c.nextID()
|
||||
replyCh := make(chan reply, 1)
|
||||
|
||||
cs.mu.Lock()
|
||||
cs.pending[ioid] = replyCh
|
||||
cs.mu.Unlock()
|
||||
c.mu.Lock()
|
||||
c.byIOID[ioid] = replyCh
|
||||
c.mu.Unlock()
|
||||
|
||||
msg := proto.BuildMessage(proto.Header{
|
||||
Command: proto.CmdReadNotify,
|
||||
@@ -682,9 +686,9 @@ func (c *circuit) get(ctx context.Context, cs *chanState, dbrType uint16, count
|
||||
select {
|
||||
case c.writeQ <- msg:
|
||||
case <-ctx.Done():
|
||||
cs.mu.Lock()
|
||||
delete(cs.pending, ioid)
|
||||
cs.mu.Unlock()
|
||||
c.mu.Lock()
|
||||
delete(c.byIOID, ioid)
|
||||
c.mu.Unlock()
|
||||
return proto.TimeValue{}, ctx.Err()
|
||||
}
|
||||
|
||||
@@ -699,9 +703,9 @@ func (c *circuit) get(ctx context.Context, cs *chanState, dbrType uint16, count
|
||||
}
|
||||
return tv, nil
|
||||
case <-ctx.Done():
|
||||
cs.mu.Lock()
|
||||
delete(cs.pending, ioid)
|
||||
cs.mu.Unlock()
|
||||
c.mu.Lock()
|
||||
delete(c.byIOID, ioid)
|
||||
c.mu.Unlock()
|
||||
return proto.TimeValue{}, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user