-
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathruntime_modes.go
More file actions
224 lines (198 loc) · 5.72 KB
/
Copy pathruntime_modes.go
File metadata and controls
224 lines (198 loc) · 5.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// Copyright 2026 The Go Language Server Authors. All rights reserved.
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2
import "context"
// SingleClient is the single-flight, caller-owned-read-loop client mode.
//
// It is an alias of [SyncClient] so the current low-latency synchronous client
// becomes the named baseline for the mode split without adding another runtime
// layer.
type SingleClient = SyncClient
// NewSingleClient creates a [SingleClient] over stream.
func NewSingleClient(stream Stream, opts ...Option) (*SingleClient, error) {
return NewSyncClient(stream, opts...)
}
// Peer is the bidirectional JSON-RPC endpoint mode.
//
// It names the existing [Conn] capability set explicitly: both sides may send
// calls, notifications, and responses, with [Async] required for handlers that
// wait on server-initiated calls.
type Peer = Conn
// NewPeer creates a bidirectional peer endpoint over stream.
func NewPeer(stream Stream, opts ...Option) Peer {
return NewConn(stream, opts...)
}
// Server is the server-oriented runtime mode.
//
// It currently uses [Conn] because the existing connection already provides the
// server read loop, preemption, async handler release, and batch dispatch. The
// separate name keeps server-only call sites from depending on client-mode
// constructors.
type Server = Conn
// NewServer creates a server endpoint over stream.
func NewServer(stream Stream, opts ...Option) Server {
return NewConn(stream, opts...)
}
// BatchClient is the raw-frame batch client mode.
//
// It exposes only frame primitives so batch-mode callers can write a complete
// JSON-RPC batch array and read the response frame without passing through the
// single-message [Conn.Call] API. The supplied Stream must be frame-capable; the
// built-in framers satisfy this requirement.
type BatchClient struct {
stream Stream
frames frameStream
}
// NewBatchClient creates a raw-frame batch client over stream.
func NewBatchClient(stream Stream) (*BatchClient, error) {
frames, ok := stream.(frameStream)
if !ok {
return nil, errFrameStreamRequired
}
return &BatchClient{stream: stream, frames: frames}, nil
}
// WriteFrame writes one already-encoded JSON frame.
func (c *BatchClient) WriteFrame(ctx context.Context, data []byte) (int64, error) {
return c.frames.WriteFrame(ctx, data)
}
// ReadFrame reads one raw JSON response frame.
func (c *BatchClient) ReadFrame(ctx context.Context) (data []byte, n int64, err error) {
return c.frames.ReadFrame(ctx)
}
// Close closes the underlying stream.
func (c *BatchClient) Close() error {
return c.stream.Close()
}
// BatchServer is the batch-capable server endpoint mode.
//
// It names the existing Conn batch dispatch path explicitly. New batch-only
// server APIs can grow behind this type without changing single-client or peer
// constructors.
type BatchServer struct {
Conn
}
// NewBatchServer creates a batch-capable server endpoint over stream.
func NewBatchServer(stream Stream, opts ...Option) *BatchServer {
return &BatchServer{Conn: NewConn(stream, opts...)}
}
const errFrameStreamRequired = constError("jsonrpc2: batch mode requires a frame-capable stream")
// denseCallSlots stores waiters for generated, monotonically increasing numeric
// call IDs.
//
// Unlike outgoingCallSlots, it does not need tombstones or probing for generated
// dense IDs. It keeps a power-of-two ring window from base through base+len-1
// and advances base as low IDs are retired.
type denseCallSlots struct {
slots []denseCallSlot
base int64
live int
}
type denseCallSlot struct {
waiter *waiter
id int64
}
func (s *denseCallSlots) Len() int { return s.live }
func (s *denseCallSlots) Add(id ID, w *waiter) {
n, ok := id.Number()
if !ok {
panic("jsonrpc2: dense call id is not numeric")
}
if n <= 0 {
panic("jsonrpc2: dense call id must be positive")
}
if len(s.slots) == 0 {
s.base = n
s.slots = make([]denseCallSlot, initialOutgoingCallSlots)
} else if s.live == 0 {
s.base = n
}
if n < s.base {
s.rebase(n)
}
if need := int(n - s.base + 1); need > len(s.slots) {
s.grow(need)
}
idx := s.index(n)
if s.slots[idx].waiter != nil {
panic("jsonrpc2: duplicate dense call id")
}
s.slots[idx] = denseCallSlot{id: n, waiter: w}
s.live++
}
func (s *denseCallSlots) Take(id ID) (*waiter, bool) {
n, ok := id.Number()
if !ok || s.live == 0 || len(s.slots) == 0 || n < s.base || int(n-s.base) >= len(s.slots) {
return nil, false
}
idx := s.index(n)
slot := &s.slots[idx]
if slot.waiter == nil || slot.id != n {
return nil, false
}
w := slot.waiter
*slot = denseCallSlot{}
s.live--
if s.live == 0 {
clear(s.slots)
s.base = 0
return w, true
}
if n == s.base {
s.advanceBase()
}
return w, true
}
func (s *denseCallSlots) Drain(f func(ID, *waiter)) {
if s.live == 0 {
return
}
for i := range s.slots {
if w := s.slots[i].waiter; w != nil {
f(NewNumberID(s.slots[i].id), w)
s.slots[i] = denseCallSlot{}
}
}
s.live = 0
s.base = 0
}
func (s *denseCallSlots) index(id int64) int {
return int(uint64(id) & uint64(len(s.slots)-1))
}
func (s *denseCallSlots) advanceBase() {
for s.live > 0 {
idx := s.index(s.base)
if s.slots[idx].waiter != nil && s.slots[idx].id == s.base {
return
}
s.base++
}
}
func (s *denseCallSlots) rebase(base int64) {
if s.live == 0 {
s.base = base
return
}
maxID := base
for i := range s.slots {
if s.slots[i].waiter != nil && s.slots[i].id > maxID {
maxID = s.slots[i].id
}
}
if need := int(maxID - base + 1); need > len(s.slots) {
s.grow(need)
}
s.base = base
}
func (s *denseCallSlots) grow(need int) {
size := len(s.slots)
for size < need {
size *= 2
}
old := s.slots
s.slots = make([]denseCallSlot, size)
for i := range old {
if old[i].waiter != nil {
s.slots[s.index(old[i].id)] = old[i]
}
}
}