fix: sync MTProto startup and egress fixes

This commit is contained in:
A 2026-07-13 19:05:41 +08:00
parent 50803a604c
commit 305e8a0008
24 changed files with 2880 additions and 317 deletions

View file

@ -39,15 +39,34 @@ func Register(ctx context.Context, cb func()) bool {
}
func Run(ctx context.Context) {
run := Take(ctx)
if run != nil {
run()
}
}
// Take transfers ownership of every currently registered callback to the caller.
// The returned function is idempotent and may safely outlive the request context.
// MTProto uses this to release a business worker after admitting rpc_result while
// still delaying follow-up updates until the result reaches the reliable stream.
func Take(ctx context.Context) func() {
cbs, ok := ctx.Value(callbacksKey{}).(*callbacks)
if !ok || cbs == nil {
return
return nil
}
cbs.mu.Lock()
list := append([]callback(nil), cbs.list...)
cbs.list = nil
cbs.mu.Unlock()
for _, cb := range list {
cb()
if len(list) == 0 {
return nil
}
var once sync.Once
return func() {
once.Do(func() {
for _, cb := range list {
cb()
}
})
}
}

View file

@ -0,0 +1,28 @@
package postresponse
import (
"context"
"sync/atomic"
"testing"
)
func TestTakeTransfersCallbacksAndRunsOnce(t *testing.T) {
ctx := WithCallbacks(context.Background())
var calls atomic.Int32
if !Register(ctx, func() { calls.Add(1) }) || !Register(ctx, func() { calls.Add(1) }) {
t.Fatal("register callbacks")
}
run := Take(ctx)
if run == nil {
t.Fatal("Take returned no callback")
}
Run(ctx)
if got := calls.Load(); got != 0 {
t.Fatalf("callbacks remained attached after Take: %d", got)
}
run()
run()
if got := calls.Load(); got != 2 {
t.Fatalf("transferred callbacks ran %d times, want 2 total", got)
}
}