Fix Remote speech stalls after pause and early completion
This commit is contained in:
1 parent
6867d901b2
commit
19b789ef5c
6 files changed
+131
-30
No files matched your search
+26
-13
@@ -38,6 +38,29 @@ func (client *Client) speak_and_wait_locked(ctx context.Context, speak func() er
|
||||
}
|
||||
}
|
||||
}()
|
||||
// Notifications can precede the SPEAK queue acknowledgement. Handle them
|
||||
// while reading command replies too, or an early END/CANCEL is lost and
|
||||
// this owner waits forever while later remote utterances queue behind it.
|
||||
started, completed := false, false
|
||||
var completion error
|
||||
handle := func(event reply) {
|
||||
if completed {
|
||||
return
|
||||
}
|
||||
switch event.code {
|
||||
case 701:
|
||||
if !started && ctx.Err() == nil && began != nil {
|
||||
began()
|
||||
}
|
||||
started = true
|
||||
case 702:
|
||||
completed, completion = true, ctx.Err()
|
||||
case 703:
|
||||
completed, completion = true, context.Canceled
|
||||
}
|
||||
}
|
||||
client.notification = handle
|
||||
defer func() { client.notification = nil }()
|
||||
if err := speak(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -61,24 +84,14 @@ func (client *Client) speak_and_wait_locked(ctx context.Context, speak func() er
|
||||
<-done
|
||||
}
|
||||
}()
|
||||
started := false
|
||||
for {
|
||||
for !completed {
|
||||
event, err := readReply(client.reader)
|
||||
if err != nil {
|
||||
_ = conn.Close()
|
||||
client.conn, client.reader = nil, nil
|
||||
return errors.Join(ctx.Err(), err)
|
||||
}
|
||||
switch event.code {
|
||||
case 701:
|
||||
if !started && ctx.Err() == nil && began != nil {
|
||||
began()
|
||||
}
|
||||
started = true
|
||||
case 702:
|
||||
return ctx.Err()
|
||||
case 703:
|
||||
return context.Canceled
|
||||
}
|
||||
handle(event)
|
||||
}
|
||||
return errors.Join(ctx.Err(), completion)
|
||||
}
|
||||
@@ -82,3 +82,42 @@ func TestContinuousSpeechWaitsForEndAndSurvivesCancellation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContinuousSpeechCompletionBeforeQueueReply(t *testing.T) {
|
||||
for _, terminal := range []int{702, 703} {
|
||||
t.Run(fmt.Sprint(terminal), func(t *testing.T) {
|
||||
conn, server := net.Pipe()
|
||||
client := connectedClient(conn)
|
||||
t.Cleanup(func() { server.Close(); client.Close(context.Background()) })
|
||||
began := make(chan struct{}, 1)
|
||||
finished := make(chan error, 1)
|
||||
go func() {
|
||||
finished <- client.SpeakAndWait(t.Context(), "Short remote response", func() { began <- struct{}{} })
|
||||
}()
|
||||
server.SetDeadline(time.Now().Add(2 * time.Second))
|
||||
reader := bufio.NewReader(server)
|
||||
if err := exchangeSSIP(reader, server, "SET SELF NOTIFICATION ALL on\r\n", "200 OK\r\n"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for range 3 {
|
||||
if _, err := reader.ReadString('\n'); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
// Speech Dispatcher queues work before returning the SPEAK reply.
|
||||
// A short or preempted utterance can finish before that reply arrives.
|
||||
fmt.Fprintf(server, "230 OK\r\n701-1\r\n701-42\r\n701 BEGIN\r\n%d-1\r\n%d-42\r\n%d DONE\r\n225-1\r\n225 QUEUED\r\n", terminal, terminal, terminal)
|
||||
if err := exchangeSSIP(reader, server, "SET SELF NOTIFICATION ALL off\r\n", "200 OK\r\n"); err != nil {
|
||||
t.Fatal("completion before queue reply was lost:", err)
|
||||
}
|
||||
if err := <-finished; terminal == 703 && !errors.Is(err, context.Canceled) || terminal == 702 && err != nil {
|
||||
t.Fatalf("completion = %v", err)
|
||||
}
|
||||
select {
|
||||
case <-began:
|
||||
default:
|
||||
t.Fatal("early BEGIN was lost")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ type Client struct {
|
||||
mutex sync.Mutex
|
||||
cancelMutex sync.Mutex
|
||||
settingsMutex sync.Mutex
|
||||
notification func(reply) // Speaking mutex owns the active completion handler.
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
@@ -426,6 +427,9 @@ func (client *Client) readReply() (reply, error) {
|
||||
if err != nil || response.code < 700 || response.code >= 800 {
|
||||
return response, err
|
||||
}
|
||||
if client.notification != nil {
|
||||
client.notification(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user