starpc 0.49.10 → 0.49.11
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/go.mod +2 -2
- package/go.sum +4 -6
- package/package.json +1 -1
- package/srpc/client-rpc.go +28 -28
- package/srpc/common-rpc.go +62 -66
- package/srpc/common-rpc_test.go +29 -1
- package/srpc/server-rpc.go +22 -20
package/go.mod
CHANGED
|
@@ -3,9 +3,9 @@ module github.com/aperturerobotics/starpc
|
|
|
3
3
|
go 1.25.0
|
|
4
4
|
|
|
5
5
|
require (
|
|
6
|
-
github.com/aperturerobotics/common v0.33.0 // latest
|
|
6
|
+
github.com/aperturerobotics/common v0.33.1-0.20260516193515-675cfc5a0c12 // latest
|
|
7
7
|
github.com/aperturerobotics/protobuf-go-lite v0.13.0 // latest
|
|
8
|
-
github.com/aperturerobotics/util v1.34.5
|
|
8
|
+
github.com/aperturerobotics/util v1.34.5 // latest
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
require (
|
package/go.sum
CHANGED
|
@@ -2,8 +2,8 @@ github.com/aperturerobotics/abseil-cpp v0.0.0-20260131110040-4bb56e2f9017 h1:3U7
|
|
|
2
2
|
github.com/aperturerobotics/abseil-cpp v0.0.0-20260131110040-4bb56e2f9017/go.mod h1:lNSJTKECIUFAnfeSqy01kXYTYe1BHubW7198jNX3nEw=
|
|
3
3
|
github.com/aperturerobotics/cli v1.1.0 h1:7a+YRC+EY3npAnTzhHV5gLCiw91KS0Ts3XwLILGOsT8=
|
|
4
4
|
github.com/aperturerobotics/cli v1.1.0/go.mod h1:M7BFP9wow5ytTzMyJQOOO991fGfsUqdTI7gGEsHfTQ8=
|
|
5
|
-
github.com/aperturerobotics/common v0.33.0 h1:
|
|
6
|
-
github.com/aperturerobotics/common v0.33.0/go.mod h1:
|
|
5
|
+
github.com/aperturerobotics/common v0.33.1-0.20260516193515-675cfc5a0c12 h1:KQiFEDyu5//G8JZ3yTN92kF2P3F+LHjSKsQikgROncU=
|
|
6
|
+
github.com/aperturerobotics/common v0.33.1-0.20260516193515-675cfc5a0c12/go.mod h1:pW7BhBpKzVrTxN8XIz6jb3MJERZ15GDxaGQ7jcIl0Xg=
|
|
7
7
|
github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260329113538-218ccd8f20e0 h1:6/3RSSlPEQ6LeidslB1ZCJkxW+MnfYDkvdWMDklDXw4=
|
|
8
8
|
github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260329113538-218ccd8f20e0/go.mod h1:OBb/beWmr/pDIZAUfi86j/4tBh2v5ctTxKMqSnh9c/4=
|
|
9
9
|
github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260329113540-600516012db3 h1:lp+V8RYcBwTX1p81swkpZn5fhw1wn2xLorzETIxRyZQ=
|
|
@@ -16,10 +16,8 @@ github.com/aperturerobotics/protobuf v0.0.0-20260203024654-8201686529c4 h1:4Dy3B
|
|
|
16
16
|
github.com/aperturerobotics/protobuf v0.0.0-20260203024654-8201686529c4/go.mod h1:tMgO7y6SJo/d9ZcvrpNqIQtdYT9de+QmYaHOZ4KnhOg=
|
|
17
17
|
github.com/aperturerobotics/protobuf-go-lite v0.13.0 h1:jEvCJhHaJEikDY/va2AUnS0DOb/0n82aISLAqxSh4Sk=
|
|
18
18
|
github.com/aperturerobotics/protobuf-go-lite v0.13.0/go.mod h1:lGH3s5ArCTXKI4wJdlNpaybUtwSjfAG0vdWjxOfMcF8=
|
|
19
|
-
github.com/aperturerobotics/util v1.34.5
|
|
20
|
-
github.com/aperturerobotics/util v1.34.5
|
|
21
|
-
github.com/aperturerobotics/util v1.34.5-0.20260516103104-cbfc6d6a0589 h1:8B9O13He1sz8Spr2pc+RL3hBzAMveLgUCXT7BpAfvEY=
|
|
22
|
-
github.com/aperturerobotics/util v1.34.5-0.20260516103104-cbfc6d6a0589/go.mod h1:mDe7WnncVuV7yjeeVSsagyfrw4xfncu7d+f0+d70niY=
|
|
19
|
+
github.com/aperturerobotics/util v1.34.5 h1:007MaOJrrsiGm5o1c8Tt7p8nVwUAxkM6pmGflrBww/U=
|
|
20
|
+
github.com/aperturerobotics/util v1.34.5/go.mod h1:mDe7WnncVuV7yjeeVSsagyfrw4xfncu7d+f0+d70niY=
|
|
23
21
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
24
22
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
25
23
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
package/package.json
CHANGED
package/srpc/client-rpc.go
CHANGED
|
@@ -38,22 +38,22 @@ func (r *ClientRPC) Start(writer PacketWriter, writeFirstMsg bool, firstMsg []by
|
|
|
38
38
|
|
|
39
39
|
var firstMsgEmpty bool
|
|
40
40
|
var err error
|
|
41
|
-
r.bcast.
|
|
42
|
-
|
|
41
|
+
locked := r.bcast.Lock()
|
|
42
|
+
r.writer = writer
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
if writeFirstMsg {
|
|
45
|
+
firstMsgEmpty = len(firstMsg) == 0
|
|
46
|
+
}
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
pkt := NewCallStartPacket(r.service, r.method, firstMsg, firstMsgEmpty)
|
|
49
|
+
err = writer.WritePacket(pkt)
|
|
50
|
+
if err != nil {
|
|
51
|
+
r.ctxCancel()
|
|
52
|
+
_ = writer.Close()
|
|
53
|
+
}
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
locked.Broadcast()
|
|
56
|
+
locked.Unlock()
|
|
57
57
|
|
|
58
58
|
return err
|
|
59
59
|
}
|
|
@@ -69,14 +69,14 @@ func (r *ClientRPC) HandlePacketData(data []byte) error {
|
|
|
69
69
|
|
|
70
70
|
// HandleStreamClose handles the stream closing optionally w/ an error.
|
|
71
71
|
func (r *ClientRPC) HandleStreamClose(closeErr error) {
|
|
72
|
-
r.bcast.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
locked := r.bcast.Lock()
|
|
73
|
+
if closeErr != nil && r.remoteErr == nil {
|
|
74
|
+
r.remoteErr = closeErr
|
|
75
|
+
}
|
|
76
|
+
r.dataClosed = true
|
|
77
|
+
r.ctxCancel()
|
|
78
|
+
locked.Broadcast()
|
|
79
|
+
locked.Unlock()
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// HandlePacket handles an incoming parsed message packet.
|
|
@@ -108,11 +108,11 @@ func (r *ClientRPC) HandleCallStart(pkt *CallStart) error {
|
|
|
108
108
|
|
|
109
109
|
// Close releases any resources held by the ClientRPC.
|
|
110
110
|
func (r *ClientRPC) Close() {
|
|
111
|
-
r.bcast.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
111
|
+
locked := r.bcast.Lock()
|
|
112
|
+
// call did not start yet if writer is nil.
|
|
113
|
+
if r.writer != nil {
|
|
114
|
+
_ = r.WriteCallCancel()
|
|
115
|
+
r.closeLocked(&locked)
|
|
116
|
+
}
|
|
117
|
+
locked.Unlock()
|
|
118
118
|
}
|
package/srpc/common-rpc.go
CHANGED
|
@@ -52,10 +52,12 @@ func (c *commonRPC) Wait(ctx context.Context) error {
|
|
|
52
52
|
var err error
|
|
53
53
|
var waitCh <-chan struct{}
|
|
54
54
|
var rpcCtx context.Context
|
|
55
|
-
c.bcast.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
locked := c.bcast.Lock()
|
|
56
|
+
rpcCtx, err = c.ctx, c.remoteErr
|
|
57
|
+
if err == nil && rpcCtx.Err() == nil {
|
|
58
|
+
waitCh = locked.WaitCh()
|
|
59
|
+
}
|
|
60
|
+
locked.Unlock()
|
|
59
61
|
|
|
60
62
|
if err != nil {
|
|
61
63
|
return err
|
|
@@ -78,43 +80,37 @@ func (c *commonRPC) Wait(ctx context.Context) error {
|
|
|
78
80
|
//
|
|
79
81
|
// returns io.EOF if the stream ended without a packet.
|
|
80
82
|
func (c *commonRPC) ReadOne() ([]byte, error) {
|
|
81
|
-
var hasMsg bool
|
|
82
|
-
var msg []byte
|
|
83
|
-
var err error
|
|
84
83
|
var ctxDone bool
|
|
85
84
|
for {
|
|
86
85
|
var waitCh <-chan struct{}
|
|
87
|
-
c.bcast.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if len(c.dataQueue) != 0 {
|
|
96
|
-
msg = c.dataQueue[0]
|
|
97
|
-
hasMsg = true
|
|
98
|
-
c.dataQueue[0] = nil
|
|
99
|
-
c.dataQueue = c.dataQueue[1:]
|
|
100
|
-
} else if c.dataClosed || c.remoteErr != nil {
|
|
101
|
-
err = c.remoteErr
|
|
102
|
-
if err == nil {
|
|
103
|
-
err = io.EOF
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
waitCh = getWaitCh()
|
|
108
|
-
})
|
|
86
|
+
locked := c.bcast.Lock()
|
|
87
|
+
if ctxDone && !c.dataClosed {
|
|
88
|
+
// context must have been canceled locally
|
|
89
|
+
c.closeLocked(&locked)
|
|
90
|
+
locked.Unlock()
|
|
91
|
+
return nil, context.Canceled
|
|
92
|
+
}
|
|
109
93
|
|
|
110
|
-
if
|
|
94
|
+
if len(c.dataQueue) != 0 {
|
|
95
|
+
msg := c.dataQueue[0]
|
|
96
|
+
c.dataQueue[0] = nil
|
|
97
|
+
c.dataQueue = c.dataQueue[1:]
|
|
98
|
+
locked.Unlock()
|
|
111
99
|
return msg, nil
|
|
112
100
|
}
|
|
113
101
|
|
|
114
|
-
if
|
|
102
|
+
if c.dataClosed || c.remoteErr != nil {
|
|
103
|
+
err := c.remoteErr
|
|
104
|
+
if err == nil {
|
|
105
|
+
err = io.EOF
|
|
106
|
+
}
|
|
107
|
+
locked.Unlock()
|
|
115
108
|
return nil, err
|
|
116
109
|
}
|
|
117
110
|
|
|
111
|
+
waitCh = locked.WaitCh()
|
|
112
|
+
locked.Unlock()
|
|
113
|
+
|
|
118
114
|
select {
|
|
119
115
|
case <-c.ctx.Done():
|
|
120
116
|
ctxDone = true
|
|
@@ -147,18 +143,19 @@ func (c *commonRPC) WriteCallData(data []byte, dataIsZero, complete bool, err er
|
|
|
147
143
|
// HandleStreamClose handles the incoming stream closing w/ optional error.
|
|
148
144
|
func (c *commonRPC) HandleStreamClose(closeErr error) {
|
|
149
145
|
var writer PacketWriter
|
|
150
|
-
c.bcast.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
146
|
+
locked := c.bcast.Lock()
|
|
147
|
+
if c.dataClosed {
|
|
148
|
+
locked.Unlock()
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
if closeErr != nil && c.remoteErr == nil {
|
|
152
|
+
c.remoteErr = closeErr
|
|
153
|
+
}
|
|
154
|
+
c.dataClosed = true
|
|
155
|
+
c.ctxCancel()
|
|
156
|
+
writer = c.writer
|
|
157
|
+
locked.Broadcast()
|
|
158
|
+
locked.Unlock()
|
|
162
159
|
if writer != nil {
|
|
163
160
|
_ = writer.Close()
|
|
164
161
|
}
|
|
@@ -173,34 +170,33 @@ func (c *commonRPC) HandleCallCancel() error {
|
|
|
173
170
|
// HandleCallData handles the call data packet.
|
|
174
171
|
func (c *commonRPC) HandleCallData(pkt *CallData) error {
|
|
175
172
|
var err error
|
|
176
|
-
c.bcast.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return
|
|
181
|
-
}
|
|
182
|
-
|
|
173
|
+
locked := c.bcast.Lock()
|
|
174
|
+
if c.dataClosed {
|
|
175
|
+
// If the packet is just indicating the call is complete, ignore it.
|
|
176
|
+
if !pkt.GetComplete() {
|
|
183
177
|
// Otherwise, return ErrCompleted (unexpected packet).
|
|
184
178
|
err = ErrCompleted
|
|
185
|
-
return
|
|
186
179
|
}
|
|
180
|
+
locked.Unlock()
|
|
181
|
+
return err
|
|
182
|
+
}
|
|
187
183
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
if data := pkt.GetData(); len(data) != 0 || pkt.GetDataIsZero() {
|
|
185
|
+
c.dataQueue = append(c.dataQueue, data)
|
|
186
|
+
}
|
|
191
187
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
188
|
+
complete := pkt.GetComplete()
|
|
189
|
+
if pktErr := pkt.GetError(); len(pktErr) != 0 {
|
|
190
|
+
complete = true
|
|
191
|
+
c.remoteErr = errors.New(pktErr)
|
|
192
|
+
}
|
|
197
193
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
194
|
+
if complete {
|
|
195
|
+
c.dataClosed = true
|
|
196
|
+
}
|
|
201
197
|
|
|
202
|
-
|
|
203
|
-
|
|
198
|
+
locked.Broadcast()
|
|
199
|
+
locked.Unlock()
|
|
204
200
|
|
|
205
201
|
return err
|
|
206
202
|
}
|
|
@@ -216,7 +212,7 @@ func (c *commonRPC) WriteCallCancel() error {
|
|
|
216
212
|
}
|
|
217
213
|
|
|
218
214
|
// closeLocked releases resources held by the RPC.
|
|
219
|
-
func (c *commonRPC) closeLocked(broadcast
|
|
215
|
+
func (c *commonRPC) closeLocked(locked *broadcast.Locked) {
|
|
220
216
|
if c.dataClosed {
|
|
221
217
|
return
|
|
222
218
|
}
|
|
@@ -226,6 +222,6 @@ func (c *commonRPC) closeLocked(broadcast func()) {
|
|
|
226
222
|
c.remoteErr = context.Canceled
|
|
227
223
|
}
|
|
228
224
|
_ = c.writer.Close()
|
|
229
|
-
|
|
225
|
+
locked.Broadcast()
|
|
230
226
|
c.ctxCancel()
|
|
231
227
|
}
|
package/srpc/common-rpc_test.go
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package srpc
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"bytes"
|
|
4
5
|
"context"
|
|
5
6
|
"io"
|
|
6
7
|
"sync/atomic"
|
|
@@ -52,7 +53,11 @@ func TestCommonRPCHandleStreamCloseClosesWriterOutsideBroadcastLock(t *testing.T
|
|
|
52
53
|
writerClosedOutsideLock := false
|
|
53
54
|
writer := &closeCallbackPacketWriter{
|
|
54
55
|
closeFn: func() {
|
|
55
|
-
|
|
56
|
+
locked, ok := rpc.bcast.TryLock()
|
|
57
|
+
if ok {
|
|
58
|
+
locked.Unlock()
|
|
59
|
+
}
|
|
60
|
+
writerClosedOutsideLock = ok
|
|
56
61
|
},
|
|
57
62
|
}
|
|
58
63
|
rpc = NewServerRPC(context.Background(), InvokerFunc(nil), writer)
|
|
@@ -63,3 +68,26 @@ func TestCommonRPCHandleStreamCloseClosesWriterOutsideBroadcastLock(t *testing.T
|
|
|
63
68
|
t.Fatal("expected writer close outside broadcast lock")
|
|
64
69
|
}
|
|
65
70
|
}
|
|
71
|
+
|
|
72
|
+
func TestCommonRPCReadOneQueuedDoesNotAllocate(t *testing.T) {
|
|
73
|
+
msg := []byte("message")
|
|
74
|
+
queue := make([][]byte, 1)
|
|
75
|
+
rpc := NewServerRPC(context.Background(), InvokerFunc(nil), &closeCountingPacketWriter{})
|
|
76
|
+
|
|
77
|
+
allocs := testing.AllocsPerRun(1000, func() {
|
|
78
|
+
queue[0] = msg
|
|
79
|
+
rpc.dataQueue = queue
|
|
80
|
+
|
|
81
|
+
got, err := rpc.ReadOne()
|
|
82
|
+
if err != nil {
|
|
83
|
+
t.Fatalf("read one: %v", err)
|
|
84
|
+
}
|
|
85
|
+
if !bytes.Equal(got, msg) {
|
|
86
|
+
t.Fatalf("expected %q, got %q", msg, got)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
if allocs != 0 {
|
|
91
|
+
t.Fatalf("expected queued ReadOne to avoid allocations, got %f", allocs)
|
|
92
|
+
}
|
|
93
|
+
}
|
package/srpc/server-rpc.go
CHANGED
|
@@ -59,29 +59,31 @@ func (r *ServerRPC) HandlePacket(msg *Packet) error {
|
|
|
59
59
|
func (r *ServerRPC) HandleCallStart(pkt *CallStart) error {
|
|
60
60
|
var err error
|
|
61
61
|
|
|
62
|
-
r.bcast.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
locked := r.bcast.Lock()
|
|
63
|
+
// process start: method and service
|
|
64
|
+
if r.method != "" || r.service != "" {
|
|
65
|
+
err = errors.New("call start must be sent only once")
|
|
66
|
+
locked.Unlock()
|
|
67
|
+
return err
|
|
68
|
+
}
|
|
69
|
+
if r.dataClosed {
|
|
70
|
+
err = ErrCompleted
|
|
71
|
+
locked.Unlock()
|
|
72
|
+
return err
|
|
73
|
+
}
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
service, method := pkt.GetRpcService(), pkt.GetRpcMethod()
|
|
76
|
+
r.service, r.method = service, method
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
// process first data packet, if included
|
|
79
|
+
if data := pkt.GetData(); len(data) != 0 || pkt.GetDataIsZero() {
|
|
80
|
+
r.dataQueue = append(r.dataQueue, data)
|
|
81
|
+
}
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
// invoke the rpc
|
|
84
|
+
locked.Broadcast()
|
|
85
|
+
go r.invokeRPC(service, method)
|
|
86
|
+
locked.Unlock()
|
|
85
87
|
|
|
86
88
|
return err
|
|
87
89
|
}
|