starpc 0.49.16 → 0.49.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starpc",
|
|
3
|
-
"version": "0.49.
|
|
3
|
+
"version": "0.49.18",
|
|
4
4
|
"description": "Streaming protobuf RPC service protocol over any two-way channel.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -128,9 +128,9 @@
|
|
|
128
128
|
"vitest": "^4.1.5"
|
|
129
129
|
},
|
|
130
130
|
"dependencies": {
|
|
131
|
-
"@aptre/yamux": "^1.0.3",
|
|
132
131
|
"@aptre/it-ws": "^1.1.2",
|
|
133
|
-
"@aptre/protobuf-es-lite": "
|
|
132
|
+
"@aptre/protobuf-es-lite": "1.1.0",
|
|
133
|
+
"@aptre/yamux": "^1.0.3",
|
|
134
134
|
"event-iterator": "^2.0.0",
|
|
135
135
|
"isomorphic-ws": "^5.0.0",
|
|
136
136
|
"it-first": "^3.0.11",
|
|
@@ -139,5 +139,11 @@
|
|
|
139
139
|
"it-stream-types": "^2.0.4",
|
|
140
140
|
"uint8arraylist": "^3.0.0",
|
|
141
141
|
"ws": "^8.20.0"
|
|
142
|
+
},
|
|
143
|
+
"resolutions": {
|
|
144
|
+
"@aptre/protobuf-es-lite": "1.1.0"
|
|
145
|
+
},
|
|
146
|
+
"overrides": {
|
|
147
|
+
"@aptre/protobuf-es-lite": "1.1.0"
|
|
142
148
|
}
|
|
143
149
|
}
|
package/srpc/common-rpc.go
CHANGED
|
@@ -34,6 +34,10 @@ type commonRPC struct {
|
|
|
34
34
|
// localCompleting is set while the local handler is publishing its terminal
|
|
35
35
|
// packet and closing the writer.
|
|
36
36
|
localCompleting bool
|
|
37
|
+
// localActive is set while the local handler goroutine may still be inside
|
|
38
|
+
// user code. Resource owners use Wait as a lifetime barrier, so cancellation
|
|
39
|
+
// must not make Wait return while a handler can still touch mux-owned state.
|
|
40
|
+
localActive bool
|
|
37
41
|
// localDone is set after the local handler has completed normally.
|
|
38
42
|
localDone bool
|
|
39
43
|
// dataQueue contains incoming data packets.
|
|
@@ -73,6 +77,21 @@ func (c *commonRPC) Wait(ctx context.Context) error {
|
|
|
73
77
|
locked := c.bcast.Lock()
|
|
74
78
|
err = c.remoteErr
|
|
75
79
|
rpcCanceled = c.ctx.Err() != nil
|
|
80
|
+
// A canceled stream tells the handler to stop, but it is not proof that
|
|
81
|
+
// the handler has returned. Keep waiting while localActive is true so a
|
|
82
|
+
// caller that releases resources after Wait cannot race in-flight user
|
|
83
|
+
// code still running on the canceled Stream context.
|
|
84
|
+
if c.localActive {
|
|
85
|
+
waitCh = locked.WaitCh()
|
|
86
|
+
locked.Unlock()
|
|
87
|
+
|
|
88
|
+
select {
|
|
89
|
+
case <-ctx.Done():
|
|
90
|
+
return context.Canceled
|
|
91
|
+
case <-waitCh:
|
|
92
|
+
continue
|
|
93
|
+
}
|
|
94
|
+
}
|
|
76
95
|
localDone = c.localDone
|
|
77
96
|
if err == nil && !rpcCanceled && !localDone {
|
|
78
97
|
waitCh = locked.WaitCh()
|
|
@@ -276,6 +295,7 @@ func (c *commonRPC) finishLocalCompletion() {
|
|
|
276
295
|
}
|
|
277
296
|
locked = c.bcast.Lock()
|
|
278
297
|
c.localCompleting = false
|
|
298
|
+
c.localActive = false
|
|
279
299
|
c.localDone = true
|
|
280
300
|
locked.Broadcast()
|
|
281
301
|
locked.Unlock()
|
package/srpc/common-rpc_test.go
CHANGED
|
@@ -232,6 +232,58 @@ func TestServerRPCRemoteCloseAfterLocalCompletionDoesNotCancelStreamContext(t *t
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
func TestServerRPCWaitDoesNotReturnUntilCanceledInvokeExits(t *testing.T) {
|
|
236
|
+
writer := newPacketRecordingWriter()
|
|
237
|
+
invoked := make(chan struct{})
|
|
238
|
+
ctxCanceled := make(chan struct{})
|
|
239
|
+
releaseInvoke := make(chan struct{})
|
|
240
|
+
rpc := NewServerRPC(context.Background(), InvokerFunc(func(serviceID, methodID string, strm Stream) (bool, error) {
|
|
241
|
+
close(invoked)
|
|
242
|
+
<-strm.Context().Done()
|
|
243
|
+
close(ctxCanceled)
|
|
244
|
+
<-releaseInvoke
|
|
245
|
+
return true, nil
|
|
246
|
+
}), writer)
|
|
247
|
+
|
|
248
|
+
if err := rpc.HandleCallStart(NewCallStartPacket("service", "method", nil, false).GetCallStart()); err != nil {
|
|
249
|
+
t.Fatalf("handle call start: %v", err)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
select {
|
|
253
|
+
case <-invoked:
|
|
254
|
+
case <-time.After(time.Second):
|
|
255
|
+
t.Fatal("invoker did not start")
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
done := make(chan error, 1)
|
|
259
|
+
go func() {
|
|
260
|
+
done <- rpc.Wait(context.Background())
|
|
261
|
+
}()
|
|
262
|
+
|
|
263
|
+
rpc.cancelContext()
|
|
264
|
+
select {
|
|
265
|
+
case <-ctxCanceled:
|
|
266
|
+
case <-time.After(time.Second):
|
|
267
|
+
t.Fatal("invoke context was not canceled")
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
select {
|
|
271
|
+
case err := <-done:
|
|
272
|
+
t.Fatalf("wait returned before canceled invoke exited: %v", err)
|
|
273
|
+
default:
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
close(releaseInvoke)
|
|
277
|
+
select {
|
|
278
|
+
case err := <-done:
|
|
279
|
+
if err != nil {
|
|
280
|
+
t.Fatalf("wait after invoke exit: %v", err)
|
|
281
|
+
}
|
|
282
|
+
case <-time.After(time.Second):
|
|
283
|
+
t.Fatal("wait did not return after invoke exited")
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
235
287
|
func TestServerRPCRemoteCloseDuringLocalCompletionDoesNotCancelStreamContext(t *testing.T) {
|
|
236
288
|
writer := newBlockingPacketWriter()
|
|
237
289
|
streamCtxCh := make(chan context.Context, 1)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//go:build goscript
|
|
2
|
+
|
|
3
|
+
package srpc
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"testing"
|
|
7
|
+
"time"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
func TestStartServerRPCInvokeDefersGoscriptWork(t *testing.T) {
|
|
11
|
+
ran := make(chan struct{}, 1)
|
|
12
|
+
startServerRPCInvoke(func() {
|
|
13
|
+
ran <- struct{}{}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
select {
|
|
17
|
+
case <-ran:
|
|
18
|
+
t.Fatal("server rpc invoke ran inline")
|
|
19
|
+
default:
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
select {
|
|
23
|
+
case <-ran:
|
|
24
|
+
case <-time.After(time.Second):
|
|
25
|
+
t.Fatal("server rpc invoke did not run")
|
|
26
|
+
}
|
|
27
|
+
}
|
package/srpc/server-rpc.go
CHANGED
|
@@ -80,9 +80,16 @@ func (r *ServerRPC) HandleCallStart(pkt *CallStart) error {
|
|
|
80
80
|
r.dataQueue = append(r.dataQueue, data)
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
// Wait is used as a resource lifetime barrier by rpcstream components.
|
|
84
|
+
// Mark the method active before scheduling it so cancellation cannot make
|
|
85
|
+
// Wait return and release a mux while invokeRPC is still running user code.
|
|
86
|
+
r.localActive = true
|
|
87
|
+
|
|
83
88
|
// invoke the rpc
|
|
84
89
|
locked.Broadcast()
|
|
85
|
-
|
|
90
|
+
startServerRPCInvoke(func() {
|
|
91
|
+
r.invokeRPC(service, method)
|
|
92
|
+
})
|
|
86
93
|
locked.Unlock()
|
|
87
94
|
|
|
88
95
|
return err
|