Assemble Remote speech before reader delivery
This commit is contained in:
1 parent
f23e89ed93
commit
205cb27483
9 files changed
+492
-33
No files matched your search
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.stormux.org/storm/cthulhu/internal/tentaclewire"
|
||||
@@ -19,7 +20,18 @@ import (
|
||||
type RemoteSpeechDelivery struct {
|
||||
Context context.Context
|
||||
Lease tentaclewire.RemoteLease
|
||||
Speech tentaclewire.RemoteSpeech
|
||||
// Speeches is one complete effect/utterance, including all IPC fragments.
|
||||
// The owner applies priority once and must not deliver a partial assembly.
|
||||
Speeches []tentaclewire.RemoteSpeech
|
||||
// Release returns the retained frame/byte budget after delivery or discard.
|
||||
// It is idempotent and safe after revocation. Dequeue alone frees no budget.
|
||||
Release func()
|
||||
}
|
||||
|
||||
type remoteSpeechBatch struct {
|
||||
id uint64
|
||||
speeches []tentaclewire.RemoteSpeech
|
||||
bytes int
|
||||
}
|
||||
|
||||
// EnableRemoteSpeech installs one bounded mailbox, explicitly from reader code.
|
||||
@@ -67,7 +79,7 @@ func (session *Session) PublishRemoteSpeech(lease tentaclewire.RemoteLease, orig
|
||||
|
||||
func (session *Session) remote_present_locked(params json.RawMessage) (any, *RPCError) {
|
||||
var request tentaclewire.RemotePresentation
|
||||
if decode_strict(params, &request) != nil || !tentaclewire.ValidRemoteSpeech(request.Speech) || len(request.Lease) != 32 || request.Epoch == 0 || request.Sequence == 0 {
|
||||
if decode_strict(params, &request) != nil {
|
||||
return nil, &RPCError{Code: -32602, Message: "Invalid session parameters"}
|
||||
}
|
||||
lease := tentaclewire.RemoteLease{Lease: request.Lease, Epoch: request.Epoch}
|
||||
@@ -78,9 +90,45 @@ func (session *Session) remote_present_locked(params json.RawMessage) (any, *RPC
|
||||
if session.remoteSpeech == nil {
|
||||
return nil, &RPCError{Code: -32601, Message: "Remote speech owner unavailable"}
|
||||
}
|
||||
batch := access.speechBatch
|
||||
if batch == nil {
|
||||
if request.Utterance != 0 && request.Utterance != request.Sequence {
|
||||
return nil, &RPCError{Code: -32602, Message: "Invalid utterance assembly"}
|
||||
}
|
||||
batch = &remoteSpeechBatch{id: request.Utterance}
|
||||
} else if request.Utterance != batch.id || request.Speech.Priority != batch.speeches[0].Priority {
|
||||
// No interleaved speech, cancel, pause, or changed priority may expose
|
||||
// an incomplete utterance. Heartbeats may still advance the sequence.
|
||||
return nil, &RPCError{Code: -32602, Message: "Invalid utterance assembly"}
|
||||
}
|
||||
data, _ := json.Marshal(request.Speech)
|
||||
if access.speechItems == tentaclewire.RemoteSpeechQueueSize || access.speechBytes+len(data) > tentaclewire.RemoteSpeechQueueBytes {
|
||||
session.revoke_remote_locked("local", true)
|
||||
return nil, &RPCError{Code: -32002, Message: "Remote speech queue full"}
|
||||
}
|
||||
access.speechItems++
|
||||
access.speechBytes += len(data)
|
||||
batch.speeches = append(batch.speeches, request.Speech)
|
||||
batch.bytes += len(data)
|
||||
access.sequence = request.Sequence
|
||||
if request.Utterance != 0 && !request.Final {
|
||||
access.speechBatch = batch
|
||||
return struct{}{}, nil
|
||||
}
|
||||
access.speechBatch = nil
|
||||
var released sync.Once
|
||||
delivery := RemoteSpeechDelivery{Context: access.ctx, Lease: lease, Speeches: batch.speeches, Release: func() {
|
||||
released.Do(func() {
|
||||
session.mutex.Lock()
|
||||
defer session.mutex.Unlock()
|
||||
if session.remote == access {
|
||||
access.speechItems -= len(batch.speeches)
|
||||
access.speechBytes -= batch.bytes
|
||||
}
|
||||
})
|
||||
}}
|
||||
select {
|
||||
case session.remoteSpeech <- RemoteSpeechDelivery{Context: access.ctx, Lease: lease, Speech: request.Speech}:
|
||||
access.sequence = request.Sequence
|
||||
case session.remoteSpeech <- delivery:
|
||||
return struct{}{}, nil
|
||||
default:
|
||||
// A dropped cancel would let stale speech outlive the intended stream.
|
||||
|
||||
Reference in new issue
Block a user