57 lines
1.5 KiB
Go
57 lines
1.5 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
)
|
|
|
|
// Bound protection even if the backend never reports completion. The deadline
|
|
// starts at admission, covering time spent in the bounded pending queue too.
|
|
const uninterruptibleSpeechTimeout = 30 * time.Second
|
|
|
|
func (worker *speechWorker) has_uninterruptible_locked() bool {
|
|
if worker.uninterruptibleContext != nil && worker.uninterruptibleContext.Err() == nil {
|
|
return true
|
|
}
|
|
for _, request := range worker.pending {
|
|
if request.Uninterruptible && request.context != nil && request.context.Err() == nil {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Preserve unrelated queued speech, just as ordinary presentation revocation
|
|
// does. The existing cancellation barrier prevents CANCEL overtaking it.
|
|
func (worker *speechWorker) stop_uninterruptible() {
|
|
worker.mutex.Lock()
|
|
defer worker.mutex.Unlock()
|
|
retained := worker.pending[:0]
|
|
for _, request := range worker.pending {
|
|
if request.Uninterruptible {
|
|
completeSpeechRequest(request, context.Canceled)
|
|
} else {
|
|
retained = append(retained, request)
|
|
}
|
|
}
|
|
clear(worker.pending[len(retained):])
|
|
worker.pending = retained
|
|
if worker.uninterruptibleCancel == nil || !worker.running {
|
|
return
|
|
}
|
|
worker.uninterruptibleCancel()
|
|
worker.presentationDelivery = nil
|
|
if worker.beepCancel != nil {
|
|
worker.beepCancel()
|
|
}
|
|
worker.generation++
|
|
worker.interrupting = true
|
|
select {
|
|
case worker.interrupt <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|