Close memory holes, adjust tests

This commit is contained in:
2026-10-19 00:42:19 +00:00
parent 466290d316
commit a81044322b
4 changed files with 380 additions and 19 deletions
+80 -2
View File
@@ -546,6 +546,10 @@ func ensureSlotConnected(s *UserMasterSession, slotIdx int, background bool, cli
IsBound: isBound,
MaxBufferSize: maxBuf,
}
if GlobalConfig != nil {
slot.AutoHoldDiscardLimit = GlobalConfig.Server.AutoHoldDiscardLimit
slot.AutoHoldBurstLimit = GlobalConfig.Server.AutoHoldBurstLimit
}
s.Slots[slotIdx] = slot
go upstreamReader(s, slot, slotIdx)
return slot
@@ -575,8 +579,7 @@ func connectOrResumeSlot(s *UserMasterSession, slotIdx int, clientConn net.Conn)
}()
slot.Mu.Lock()
slot.ScreenChanged = false
slot.IsHeld = false
slot.resetHoldAndLoopCountersLocked()
if len(slot.LastScreen) > 0 {
_, _ = clientConn.Write(slot.LastScreen)
}
@@ -781,6 +784,21 @@ func upstreamReader(s *UserMasterSession, slot *SessionSlot, slotIdx int) {
// Cap buffer at 64KB to avoid unbounded memory use from constant ticks
if len(recBuf) > 65536 {
recBuf = recBuf[len(recBuf)-16384:]
if !isActive {
slot.Mu.Lock()
slot.DiscardCount++
discardLimit := slot.AutoHoldDiscardLimit
if discardLimit == 0 {
discardLimit = defaultAutoHoldDiscardLimit
}
if discardLimit > 0 && slot.DiscardCount >= discardLimit {
slot.IsHeld = true
slot.ScreenChanged = false
slot.DiscardCount = 0
log.Printf("[%s] '%s' System [%d] %s placed on automatic HOLD (raw receive buffer discard loop detected)", displayTag, username, slotIdx+1, slot.System.Name)
}
slot.Mu.Unlock()
}
}
recBuf = append(recBuf, data...)
for {
@@ -870,8 +888,41 @@ func upstreamReader(s *UserMasterSession, slot *SessionSlot, slotIdx int) {
continue
}
now := time.Now()
// Auto-hold loop detection for background sessions:
// 1. High frequency write updates (excessive update rate loop)
if !isActive {
burstLimit := slot.AutoHoldBurstLimit
if burstLimit == 0 {
burstLimit = defaultAutoHoldBurstLimit
}
if burstLimit > 0 {
if slot.WriteBurstWindow.IsZero() || now.Sub(slot.WriteBurstWindow) > autoHoldBurstWindow {
slot.WriteBurstCount = 1
slot.WriteBurstWindow = now
} else {
slot.WriteBurstCount++
}
if slot.WriteBurstCount >= burstLimit {
slot.IsHeld = true
slot.ScreenChanged = false
slot.WriteBurstCount = 0
slot.DiscardCount = 0
log.Printf("[%s] '%s' System [%d] %s placed on automatic HOLD (excessive update rate detected)", displayTag, username, slotIdx+1, slot.System.Name)
if isEraseWrite || len(slot.LastScreen) == 0 {
slot.LastScreen = bytes.Clone(screenData)
}
slot.Mu.Unlock()
continue
}
}
}
if isEraseWrite || len(slot.LastScreen) == 0 {
slot.LastScreen = bytes.Clone(screenData)
slot.DiscardCount = 0
} else if len(slot.LastScreen)+len(screenData) <= maxBuf {
slot.LastScreen = append(slot.LastScreen, screenData...)
} else {
@@ -886,6 +937,33 @@ func upstreamReader(s *UserMasterSession, slot *SessionSlot, slotIdx int) {
newBuf = append(newBuf, slot.LastScreen[:baseLen]...)
newBuf = append(newBuf, screenData...)
slot.LastScreen = newBuf
// Auto-hold loop detection for background sessions:
// 2. Buffer overflow discard loop (repeated discards of older intermediate writes)
if !isActive {
discardLimit := slot.AutoHoldDiscardLimit
if discardLimit == 0 {
discardLimit = defaultAutoHoldDiscardLimit
}
if discardLimit > 0 {
if slot.DiscardWindow.IsZero() || now.Sub(slot.DiscardWindow) > autoHoldDiscardWindow {
slot.DiscardCount = 1
slot.DiscardWindow = now
} else {
slot.DiscardCount++
}
if slot.DiscardCount >= discardLimit {
slot.IsHeld = true
slot.ScreenChanged = false
slot.DiscardCount = 0
slot.WriteBurstCount = 0
log.Printf("[%s] '%s' System [%d] %s placed on automatic HOLD (discard loop detected)", displayTag, username, slotIdx+1, slot.System.Name)
slot.Mu.Unlock()
continue
}
}
}
}
if !isActive {
slot.ScreenChanged = true