Skip to content

Commit bdb7d18

Browse files
authored
Merge pull request #1216 from iceljc/bugfix/refine-code-lock
Bugfix/refine code lock
2 parents 1d28e8e + cd6eb1c commit bdb7d18

File tree

10 files changed

+40
-93
lines changed

10 files changed

+40
-93
lines changed

src/Infrastructure/BotSharp.Abstraction/Coding/ICodeProcessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public interface ICodeProcessor
1717
/// <param name="cancellationToken">The cancellation token</param>
1818
/// <returns></returns>
1919
/// <exception cref="NotImplementedException"></exception>
20-
Task<CodeInterpretResponse> RunAsync(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
20+
CodeInterpretResponse Run(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
2121
=> throw new NotImplementedException();
2222

2323
/// <summary>

src/Infrastructure/BotSharp.Abstraction/Coding/Settings/CodingSettings.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,4 @@ public class CodeScriptExecutionSettings
1919
public bool UseLock { get; set; }
2020
public bool UseProcess { get; set; }
2121
public int TimeoutSeconds { get; set; } = 3;
22-
public int MaxConcurrency { get; set; } = 1;
2322
}

src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ private async Task<bool> TriggerCodeScript(Agent agent, string triggerName, Rule
140140

141141
var (useLock, useProcess, timeoutSeconds) = GetCodeExecutionConfig();
142142
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
143-
var response = await processor.RunAsync(codeScript.Content, options: new()
143+
var response = processor.Run(codeScript.Content, options: new()
144144
{
145145
ScriptName = scriptName,
146146
Arguments = arguments,

src/Infrastructure/BotSharp.Core/Coding/CodeScriptExecutor.cs

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/Infrastructure/BotSharp.Core/Coding/CodingPlugin.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,5 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)
1313
var coding = new CodingSettings();
1414
config.Bind("Coding", coding);
1515
services.AddSingleton(provider => coding);
16-
17-
services.AddSingleton<CodeScriptExecutor>();
1816
}
1917
}

src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)
6969
services.AddScoped<ITokenStatistics, TokenStatistics>();
7070

7171
services.AddScoped<IAgentUtilityHook, WebSearchUtilityHook>();
72-
services.AddSingleton<CodeScriptExecutor>();
7372
}
7473

7574
public bool AttachMenu(List<PluginMenuDef> menu)

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ await hook.OnResponseGenerated(new InstructResponseModel
258258
// Run code script
259259
var (useLock, useProcess, timeoutSeconds) = GetCodeExecutionConfig(codingSettings);
260260
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
261-
var codeResponse = await codeProcessor.RunAsync(context.CodeScript?.Content ?? string.Empty, options: new()
261+
var codeResponse = codeProcessor.Run(context.CodeScript?.Content ?? string.Empty, options: new()
262262
{
263263
ScriptName = context.CodeScript?.Name,
264264
Arguments = context.Arguments,

src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public async Task<bool> Execute(RoleDialogModel message)
7171

7272
try
7373
{
74-
var (isSuccess, result) = await InnerRunCode(ret.PythonCode);
74+
var (isSuccess, result) = InnerRunCode(ret.PythonCode);
7575
if (isSuccess)
7676
{
7777
message.Content = result;
@@ -107,7 +107,7 @@ public async Task<bool> Execute(RoleDialogModel message)
107107
/// </summary>
108108
/// <param name="codeScript"></param>
109109
/// <returns></returns>
110-
private async Task<(bool, string)> InnerRunCode(string codeScript)
110+
private (bool, string) InnerRunCode(string codeScript)
111111
{
112112
var codeProvider = _codingSettings.CodeExecution?.Processor;
113113
codeProvider = !string.IsNullOrEmpty(codeProvider) ? codeProvider : BuiltInCodeProcessor.PyInterpreter;
@@ -122,7 +122,7 @@ public async Task<bool> Execute(RoleDialogModel message)
122122
var (useLock, useProcess, timeoutSeconds) = GetCodeExecutionConfig();
123123
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
124124

125-
var response = await processor.RunAsync(codeScript, options: new()
125+
var response = processor.Run(codeScript, options: new()
126126
{
127127
UseLock = useLock,
128128
UseProcess = useProcess

src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyCodeInterpreter.cs

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,42 +12,29 @@ public class PyCodeInterpreter : ICodeProcessor
1212
{
1313
private readonly IServiceProvider _services;
1414
private readonly ILogger<PyCodeInterpreter> _logger;
15-
private readonly CodeScriptExecutor _executor;
1615
private readonly CodingSettings _settings;
16+
private static SemaphoreSlim _semLock = new(initialCount: 1, maxCount: 1);
1717

1818
public PyCodeInterpreter(
1919
IServiceProvider services,
2020
ILogger<PyCodeInterpreter> logger,
21-
CodeScriptExecutor executor,
2221
CodingSettings settings)
2322
{
2423
_services = services;
2524
_logger = logger;
26-
_executor = executor;
2725
_settings = settings;
2826
}
2927

3028
public string Provider => BuiltInCodeProcessor.PyInterpreter;
3129

32-
public async Task<CodeInterpretResponse> RunAsync(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
30+
public CodeInterpretResponse Run(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
3331
{
3432
if (options?.UseLock == true)
3533
{
36-
try
37-
{
38-
return await _executor.ExecuteAsync(async () =>
39-
{
40-
return await InnerRunCode(codeScript, options, cancellationToken);
41-
}, cancellationToken: cancellationToken);
42-
}
43-
catch (Exception ex)
44-
{
45-
_logger.LogError(ex, "Error when using code script executor.");
46-
return new() { ErrorMsg = ex.Message };
47-
}
34+
return InnerRunWithLock(codeScript, options, cancellationToken);
4835
}
4936

50-
return await InnerRunCode(codeScript, options, cancellationToken);
37+
return InnerRunCode(codeScript, options, cancellationToken);
5138
}
5239

5340
public async Task<CodeGenerationResult> GenerateCodeScriptAsync(string text, CodeGenerationOptions? options = null)
@@ -105,7 +92,30 @@ public async Task<CodeGenerationResult> GenerateCodeScriptAsync(string text, Cod
10592

10693

10794
#region Private methods
108-
private async Task<CodeInterpretResponse> InnerRunCode(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
95+
private CodeInterpretResponse InnerRunWithLock(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
96+
{
97+
var lockAcquired = false;
98+
try
99+
{
100+
_semLock.Wait(cancellationToken);
101+
lockAcquired = true;
102+
return InnerRunCode(codeScript, options, cancellationToken);
103+
}
104+
catch (Exception ex)
105+
{
106+
_logger.LogError(ex, $"Error in {nameof(InnerRunWithLock)}");
107+
return new() { ErrorMsg = ex.Message };
108+
}
109+
finally
110+
{
111+
if (lockAcquired)
112+
{
113+
_semLock.Release();
114+
}
115+
}
116+
}
117+
118+
private CodeInterpretResponse InnerRunCode(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
109119
{
110120
var response = new CodeInterpretResponse();
111121
var scriptName = options?.ScriptName ?? codeScript.SubstringMax(30);
@@ -116,11 +126,11 @@ private async Task<CodeInterpretResponse> InnerRunCode(string codeScript, CodeIn
116126

117127
if (options?.UseProcess == true)
118128
{
119-
response = await CoreRunProcess(codeScript, options, cancellationToken);
129+
response = CoreRunProcess(codeScript, options, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
120130
}
121131
else
122132
{
123-
response = await CoreRunScript(codeScript, options, cancellationToken);
133+
response = CoreRunScript(codeScript, options, cancellationToken);
124134
}
125135

126136
_logger.LogWarning($"End running python code script in {Provider}: {scriptName}");
@@ -141,7 +151,7 @@ private async Task<CodeInterpretResponse> InnerRunCode(string codeScript, CodeIn
141151
}
142152
}
143153

144-
private async Task<CodeInterpretResponse> CoreRunScript(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
154+
private CodeInterpretResponse CoreRunScript(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
145155
{
146156
cancellationToken.ThrowIfCancellationRequested();
147157

@@ -161,21 +171,6 @@ private async Task<CodeInterpretResponse> CoreRunScript(string codeScript, CodeI
161171

162172
try
163173
{
164-
// Capture the Python thread ID for the current thread executing under the GIL
165-
var pythonThreadId = PythonEngine.GetPythonThreadID();
166-
using var reg = cancellationToken.Register(() =>
167-
{
168-
try
169-
{
170-
PythonEngine.Interrupt(pythonThreadId);
171-
_logger.LogWarning($"Cancellation requested: issued PythonEngine.Interrupt for thread {pythonThreadId} (request {requestId})");
172-
}
173-
catch (Exception ex)
174-
{
175-
_logger.LogError(ex, "Failed to interrupt Python execution on cancellation.");
176-
}
177-
});
178-
179174
// Redirect standard output/error to capture it
180175
dynamic outIO = io.StringIO();
181176
dynamic errIO = io.StringIO();
@@ -206,8 +201,6 @@ private async Task<CodeInterpretResponse> CoreRunScript(string codeScript, CodeI
206201
}
207202
sys.argv = list;
208203

209-
cancellationToken.ThrowIfCancellationRequested();
210-
211204
// Execute Python script
212205
PythonEngine.Exec(codeScript, globals);
213206

@@ -217,8 +210,6 @@ private async Task<CodeInterpretResponse> CoreRunScript(string codeScript, CodeI
217210
var stdout = outIO.getvalue()?.ToString() as string;
218211
var stderr = errIO.getvalue()?.ToString() as string;
219212

220-
cancellationToken.ThrowIfCancellationRequested();
221-
222213
return new CodeInterpretResponse
223214
{
224215
Result = stdout?.TrimEnd('\r', '\n') ?? string.Empty,
@@ -237,10 +228,10 @@ private async Task<CodeInterpretResponse> CoreRunScript(string codeScript, CodeI
237228
sys.stderr = sys.__stderr__;
238229
sys.argv = new PyList();
239230
}
240-
};
231+
}
241232
}, cancellationToken);
242233

243-
return await execTask.WaitAsync(cancellationToken);
234+
return execTask.WaitAsync(cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
244235
}
245236

246237

src/WebStarter/appsettings.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,8 +527,7 @@
527527
"Processor": null,
528528
"UseLock": false,
529529
"UseProcess": false,
530-
"TimeoutSeconds": 3,
531-
"MaxConcurrency": 1
530+
"TimeoutSeconds": 3
532531
}
533532
},
534533

0 commit comments

Comments
 (0)