Skip to content

Commit 88f45c1

Browse files
committed
optimize readjsonprotocol
1 parent 1672305 commit 88f45c1

File tree

9 files changed

+661
-95
lines changed

9 files changed

+661
-95
lines changed

.idea/.idea.LanguageServer.Framework/.idea/copilot.data.migration.ask2agent.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

LanguageServer.Framework/LSPCommunicationBase.cs

Lines changed: 138 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
using EmmyLua.LanguageServer.Framework.Server;
99
using EmmyLua.LanguageServer.Framework.Server.Handler;
1010
using EmmyLua.LanguageServer.Framework.Server.JsonProtocol;
11+
using EmmyLua.LanguageServer.Framework.Server.Metrics;
1112
using EmmyLua.LanguageServer.Framework.Server.RequestManager;
1213
using EmmyLua.LanguageServer.Framework.Server.Scheduler;
1314

1415
namespace EmmyLua.LanguageServer.Framework;
1516

16-
public abstract class LSPCommunicationBase
17+
public abstract class LSPCommunicationBase : IDisposable
1718
{
1819
public JsonSerializerOptions JsonSerializerOptions { get; } = new()
1920
{
@@ -43,6 +44,25 @@ public abstract class LSPCommunicationBase
4344

4445
public ClientCapabilities ClientCapabilities { get; set; } = null!;
4546

47+
/// <summary>
48+
/// 性能指标收集器
49+
/// </summary>
50+
public IPerformanceMetricsCollector? MetricsCollector
51+
{
52+
get => _metricsCollector;
53+
set
54+
{
55+
_metricsCollector = value;
56+
// 同步到Writer
57+
Writer.MetricsCollector = value;
58+
}
59+
}
60+
private IPerformanceMetricsCollector? _metricsCollector;
61+
62+
protected CancellationTokenSource? ExitTokenSource { get; private set; }
63+
private readonly SemaphoreSlim _exitTokenLock = new(1, 1);
64+
private bool _disposed;
65+
4666
protected LSPCommunicationBase(Stream input, Stream output)
4767
{
4868
Reader = new JsonProtocolReader(input, JsonSerializerOptions);
@@ -76,6 +96,14 @@ public LSPCommunicationBase AddHandler(IJsonHandler handler)
7696
return this;
7797
}
7898

99+
/// <summary>
100+
/// 获取当前的性能指标快照
101+
/// </summary>
102+
public PerformanceMetrics? GetMetrics()
103+
{
104+
return MetricsCollector?.GetMetrics();
105+
}
106+
79107
public Task SendNotification(NotificationMessage notification)
80108
{
81109
Writer.WriteNotification(notification);
@@ -104,13 +132,18 @@ private async Task OnDispatch(Message message)
104132
{
105133
if (RequestHandlers.TryGetValue(request.Method, out var handler))
106134
{
135+
MetricsCollector?.RecordRequestStart(request.Method);
136+
var startTime = DateTime.UtcNow;
137+
var success = false;
138+
107139
try
108140
{
109141
var token = ClientRequestTokenManager.Create(request.Id);
110142
var result = await handler(request, token).ConfigureAwait(false);
111143
if (!token.IsCancellationRequested)
112144
{
113145
Writer.WriteResponse(request.Id, result);
146+
success = true;
114147
}
115148
else
116149
{
@@ -146,6 +179,8 @@ private async Task OnDispatch(Message message)
146179
finally
147180
{
148181
ClientRequestTokenManager.ClearToken(request.Id);
182+
var duration = DateTime.UtcNow - startTime;
183+
MetricsCollector?.RecordRequestComplete(request.Method, duration, success);
149184
}
150185
}
151186
else
@@ -163,7 +198,23 @@ private async Task OnDispatch(Message message)
163198
{
164199
if (NotificationHandlers.TryGetValue(notification.Method, out var handler))
165200
{
166-
await handler(notification, CancellationToken.None);
201+
MetricsCollector?.RecordNotification(notification.Method);
202+
203+
try
204+
{
205+
// 创建取消令牌以支持通知处理的取消
206+
using var cts = CancellationTokenSource.CreateLinkedTokenSource(
207+
ExitTokenSource?.Token ?? CancellationToken.None);
208+
await handler(notification, cts.Token);
209+
}
210+
catch (OperationCanceledException)
211+
{
212+
// Notification 被取消,忽略
213+
}
214+
catch (Exception e)
215+
{
216+
Console.Error.WriteLine($"[Notification Handler] Error handling '{notification.Method}': {e}");
217+
}
167218
}
168219

169220
break;
@@ -183,52 +234,106 @@ protected virtual bool BaseHandle(Message message)
183234

184235
public Task Run()
185236
{
186-
_mutexForExitTokenSource.WaitOne();
187-
if (ExitTokenSource is not null)
188-
throw new InvalidOperationException("Already running.");
189-
ExitTokenSource = new CancellationTokenSource();
190-
var task = Task.Run(async () =>
237+
_exitTokenLock.Wait();
238+
try
191239
{
192-
try
240+
if (ExitTokenSource is not null)
241+
throw new InvalidOperationException("Already running.");
242+
243+
ExitTokenSource = new CancellationTokenSource();
244+
245+
var task = Task.Run(async () =>
193246
{
194-
while (ExitTokenSource.IsCancellationRequested is false)
247+
try
195248
{
196-
var message = await Reader.ReadAsync(ExitTokenSource.Token);
197-
if (BaseHandle(message))
249+
while (!ExitTokenSource.IsCancellationRequested)
198250
{
199-
continue;
200-
}
251+
var message = await Reader.ReadAsync(ExitTokenSource.Token);
252+
MetricsCollector?.RecordMessageReceived();
253+
254+
if (BaseHandle(message))
255+
{
256+
continue;
257+
}
201258

202-
Scheduler.Schedule(OnDispatch, message);
259+
Scheduler.Schedule(OnDispatch, message);
260+
}
261+
}
262+
catch (OperationCanceledException)
263+
{
264+
await Console.Error.WriteLineAsync("LSPCommunication exited");
203265
}
266+
catch (Exception e)
267+
{
268+
Console.Error.WriteLine(e);
269+
}
270+
finally
271+
{
272+
ExitTokenSource?.Dispose();
273+
ExitTokenSource = null;
274+
}
275+
});
276+
277+
return task;
278+
}
279+
finally
280+
{
281+
_exitTokenLock.Release();
282+
}
283+
}
284+
285+
public void Exit()
286+
{
287+
_exitTokenLock.Wait();
288+
try
289+
{
290+
if (ExitTokenSource is null)
291+
throw new InvalidOperationException("Run() must be called before exit");
292+
ExitTokenSource.Cancel();
293+
}
294+
finally
295+
{
296+
_exitTokenLock.Release();
297+
}
298+
}
299+
300+
protected virtual void Dispose(bool disposing)
301+
{
302+
if (_disposed) return;
303+
304+
if (disposing)
305+
{
306+
// 取消运行
307+
try
308+
{
309+
Exit();
204310
}
205-
catch (OperationCanceledException)
311+
catch
206312
{
207-
await Console.Error.WriteLineAsync("LSPCommunication exited");
313+
// Ignore if not running
208314
}
209-
catch (Exception e)
315+
316+
// 释放资源
317+
ExitTokenSource?.Dispose();
318+
_exitTokenLock.Dispose();
319+
320+
if (Scheduler is IDisposable disposableScheduler)
210321
{
211-
Console.Error.WriteLine(e);
322+
disposableScheduler.Dispose();
212323
}
213-
finally
324+
325+
if (Writer is IDisposable disposableWriter)
214326
{
215-
ExitTokenSource.Dispose();
216-
ExitTokenSource = null;
327+
disposableWriter.Dispose();
217328
}
218-
});
219-
_mutexForExitTokenSource.ReleaseMutex();
220-
return task;
221-
}
329+
}
222330

223-
protected CancellationTokenSource? ExitTokenSource { get; private set; }
224-
private readonly Mutex _mutexForExitTokenSource = new();
331+
_disposed = true;
332+
}
225333

226-
public void Exit()
334+
public void Dispose()
227335
{
228-
_mutexForExitTokenSource.WaitOne();
229-
if (ExitTokenSource is null)
230-
throw new InvalidOperationException("Run() must be called before exit");
231-
ExitTokenSource!.Cancel();
232-
_mutexForExitTokenSource.ReleaseMutex();
336+
Dispose(true);
337+
GC.SuppressFinalize(this);
233338
}
234339
}

0 commit comments

Comments
 (0)