Wait quietly for an absent braille display
Helper retries BrlAPI open in-process until BRLTTY answers, supervisor waits with the parked helper instead of kill/respawn cycles, exponential backoff and warn-once for real helper exits, bounded give-up so permanent failures still surface.
This commit is contained in:
1 parent
6c10ed0517
commit
002e515edf
5 files changed
+279
-15
No files matched your search
@@ -17,16 +17,55 @@ import (
|
||||
"git.stormux.org/storm/cthulhu/internal/braille"
|
||||
)
|
||||
|
||||
// A helper connection attempt blocks inside BRLTTY until BRLTTY answers,
|
||||
// which is exactly when a display becomes available: BRLTTY stalls BrlAPI
|
||||
// while it looks for a switched-off (e.g. Bluetooth) display, and there is
|
||||
// no cheaper presence query. The session therefore waits with the single
|
||||
// helper instead of killing and respawning it. brailleReadyGiveUp only
|
||||
// recycles a permanently wedged helper (e.g. BRLTTY died mid-handshake).
|
||||
const (
|
||||
brailleReadyGiveUp = 5 * time.Minute
|
||||
brailleReadyNotice = 10 * time.Second
|
||||
)
|
||||
|
||||
const brailleMaxBackoff = 30 * time.Second
|
||||
|
||||
// nextBrailleBackoff grows the delay between helper connection attempts so
|
||||
// an absent display (switched off, BRLTTY not answering) is retried on a
|
||||
// slow cadence instead of respawning the helper every few seconds forever.
|
||||
func nextBrailleBackoff(current time.Duration) time.Duration {
|
||||
if current <= 0 {
|
||||
return time.Second
|
||||
}
|
||||
return min(current*2, brailleMaxBackoff)
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) run(ctx context.Context, done chan struct{}) {
|
||||
defer close(done)
|
||||
backoff := time.Duration(0)
|
||||
lastError := ""
|
||||
for ctx.Err() == nil {
|
||||
if err := worker.session(ctx); err != nil && ctx.Err() == nil {
|
||||
worker.logger.Warn("braille helper disconnected", "error", err)
|
||||
started := time.Now()
|
||||
err := worker.session(ctx)
|
||||
if err != nil && ctx.Err() == nil {
|
||||
if err.Error() != lastError {
|
||||
worker.logger.Warn("braille helper disconnected", "error", err)
|
||||
lastError = err.Error()
|
||||
} else {
|
||||
worker.logger.Debug("braille helper still unavailable", "error", err)
|
||||
}
|
||||
}
|
||||
// A session that stayed up a while was a real connection; do not
|
||||
// keep growing the delay after it ends.
|
||||
if time.Since(started) > 10*time.Second {
|
||||
backoff = 0
|
||||
lastError = ""
|
||||
}
|
||||
backoff = nextBrailleBackoff(backoff)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(time.Second):
|
||||
case <-time.After(backoff):
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,17 +141,29 @@ func (worker *brailleWorker) session(ctx context.Context) (result error) {
|
||||
}
|
||||
readErrors <- err
|
||||
}()
|
||||
timer := time.NewTimer(2 * time.Second)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case err := <-ready:
|
||||
if err != nil {
|
||||
return err
|
||||
giveUp := worker.readyGiveUp
|
||||
if giveUp <= 0 {
|
||||
giveUp = brailleReadyGiveUp
|
||||
}
|
||||
giveUpTimer := time.NewTimer(giveUp)
|
||||
defer giveUpTimer.Stop()
|
||||
notice := time.NewTimer(brailleReadyNotice)
|
||||
defer notice.Stop()
|
||||
readyWait:
|
||||
for {
|
||||
select {
|
||||
case err := <-ready:
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
break readyWait
|
||||
case <-notice.C:
|
||||
worker.logger.Info("waiting for braille display")
|
||||
case <-giveUpTimer.C:
|
||||
return errors.New("braille helper connection timed out")
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
case <-timer.C:
|
||||
return errors.New("braille helper connection timed out")
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
worker.logger.Info("braille helper connected")
|
||||
worker.mu.Lock()
|
||||
|
||||
Reference in new issue
Block a user