Skip to content

Commit e699201

Browse files
committed
Make sure we don't process code post message in parallel
1 parent 94bb707 commit e699201

File tree

1 file changed

+67
-45
lines changed

1 file changed

+67
-45
lines changed

shell/AIShell.Integration/Channel.cs

Lines changed: 67 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ public class Channel : IDisposable
2727
private Thread _serverThread;
2828
private CodePostData _pendingPostCodeData;
2929

30+
/// <summary>
31+
/// Indicates if a code-post message is being processed:
32+
/// - value is 1: yes;
33+
/// - value is 0: no.
34+
/// </summary>
35+
private int _processingCodePost;
36+
3037
private Channel(Runspace runspace, Type psConsoleReadLineType)
3138
{
3239
_runspace = runspace;
@@ -183,64 +190,79 @@ private void OnPostCode(PostCodeMessage postCodeMessage)
183190
{
184191
// Ignore 'code post' request when a posting operation is on-going.
185192
// This most likely would happen when user run 'code post' mutliple times to post the same code, which is safe to ignore.
186-
if (_pendingPostCodeData is not null || postCodeMessage.CodeBlocks.Count is 0)
193+
if (Interlocked.CompareExchange(ref _processingCodePost, 1, 0) is 1)
187194
{
188195
return;
189196
}
190197

191-
string codeToInsert;
192-
List<string> codeBlocks = postCodeMessage.CodeBlocks;
193-
List<PredictionCandidate> predictionCandidates = null;
194-
195-
if (codeBlocks.Count is 1)
196-
{
197-
codeToInsert = codeBlocks[0];
198-
}
199-
else if (Predictor.TryProcessForPrediction(codeBlocks, out predictionCandidates))
200-
{
201-
codeToInsert = predictionCandidates[0].Code;
202-
}
203-
else
198+
try
204199
{
205-
// Use LF as line ending to be consistent with the response from LLM.
206-
StringBuilder sb = new(capacity: 50);
207-
for (int i = 0; i < codeBlocks.Count; i++)
200+
// If '_pendingPostCodeData' is already set, then we are still in an on-going posting operation,
201+
// waiting the code to be posted on the 'OnIdle' event.
202+
if (_pendingPostCodeData is not null || postCodeMessage.CodeBlocks.Count is 0)
203+
{
204+
return;
205+
}
206+
207+
string codeToInsert;
208+
List<string> codeBlocks = postCodeMessage.CodeBlocks;
209+
List<PredictionCandidate> predictionCandidates = null;
210+
211+
if (codeBlocks.Count is 1)
212+
{
213+
codeToInsert = codeBlocks[0];
214+
}
215+
else if (Predictor.TryProcessForPrediction(codeBlocks, out predictionCandidates))
216+
{
217+
codeToInsert = predictionCandidates[0].Code;
218+
}
219+
else
208220
{
209-
if (i > 0)
221+
// Use LF as line ending to be consistent with the response from LLM.
222+
StringBuilder sb = new(capacity: 50);
223+
for (int i = 0; i < codeBlocks.Count; i++)
210224
{
211-
sb.Append('\n');
225+
if (i > 0)
226+
{
227+
sb.Append('\n');
228+
}
229+
230+
sb.Append(codeBlocks[i]).Append('\n');
212231
}
213232

214-
sb.Append(codeBlocks[i]).Append('\n');
233+
codeToInsert = sb.ToString();
215234
}
216235

217-
codeToInsert = sb.ToString();
218-
}
219-
220-
// When PSReadLine is actively running, 'TreatControlCAsInput' would be set to 'true' because
221-
// it handles 'Ctrl+c' as regular input.
222-
// When the value is 'false', it means PowerShell is still busy running scripts or commands.
223-
if (Console.TreatControlCAsInput)
224-
{
225-
PSRLRevertLine();
226-
PSRLInsert(codeToInsert);
227-
_predictor.SetCandidates(predictionCandidates);
236+
// When PSReadLine is actively running, 'TreatControlCAsInput' would be set to 'true' because
237+
// it handles 'Ctrl+c' as regular input.
238+
// When the value is 'false', it means PowerShell is still busy running scripts or commands.
239+
if (Console.TreatControlCAsInput)
240+
{
241+
PSRLRevertLine();
242+
PSRLInsert(codeToInsert);
243+
_predictor.SetCandidates(predictionCandidates);
244+
}
245+
else
246+
{
247+
_pendingPostCodeData = new CodePostData(codeToInsert, predictionCandidates);
248+
// We use script block handler instead of a delegate handler because the latter will run
249+
// in a background thread, while the former will run in the pipeline thread, which is way
250+
// more predictable.
251+
_runspace.Events.SubscribeEvent(
252+
source: null,
253+
eventName: null,
254+
sourceIdentifier: PSEngineEvent.OnIdle,
255+
data: null,
256+
action: _onIdleAction,
257+
supportEvent: true,
258+
forwardEvent: false,
259+
maxTriggerCount: 1);
260+
}
228261
}
229-
else
262+
finally
230263
{
231-
_pendingPostCodeData = new CodePostData(codeToInsert, predictionCandidates);
232-
// We use script block handler instead of a delegate handler because the latter will run
233-
// in a background thread, while the former will run in the pipeline thread, which is way
234-
// more predictable.
235-
_runspace.Events.SubscribeEvent(
236-
source: null,
237-
eventName: null,
238-
sourceIdentifier: PSEngineEvent.OnIdle,
239-
data: null,
240-
action: _onIdleAction,
241-
supportEvent: true,
242-
forwardEvent: false,
243-
maxTriggerCount: 1);
264+
// Reset the state.
265+
_processingCodePost = 0;
244266
}
245267
}
246268

0 commit comments

Comments
 (0)