[#59550] ExternalControlServer: Fix deadlocking in RunFor
diff --git a/src/Renode/Network/ExternalControl/RunFor.cs b/src/Renode/Network/ExternalControl/RunFor.cs
index 8e80afc..c10a7fe 100644
--- a/src/Renode/Network/ExternalControl/RunFor.cs
+++ b/src/Renode/Network/ExternalControl/RunFor.cs
@@ -6,36 +6,102 @@
//
using System;
using System.Collections.Generic;
+using System.Threading;
using Antmicro.Renode.Core;
using Antmicro.Renode.Logging;
using Antmicro.Renode.Time;
namespace Antmicro.Renode.Network.ExternalControl
{
- public class RunFor : BaseCommand
+ public class RunFor : BaseCommand, IDisposable
{
public RunFor(IEmulationElement parent)
: base(parent)
{
}
+ public void Dispose()
+ {
+ if(cancellationToken != null)
+ {
+ parent.Log(LogLevel.Warning, "RunFor disposed while running");
+ }
+ cancellationToken?.Cancel();
+ cancellationToken = null;
+ disposed = true;
+ }
+
public override Response Invoke(List<byte> data)
{
+ if(disposed)
+ {
+ return Response.CommandFailed(Identifier, "Command is unavailable");
+ }
+
if(data.Count != 8)
{
- return Response.CommandFailed(Command.RunFor, "Expected 8 bytes of payload");
+ return Response.CommandFailed(Identifier, "Expected 8 bytes of payload");
}
+ if(cancellationToken != null)
+ {
+ return Response.CommandFailed(Identifier, "One RunFor command can be running at any given time");
+ }
+
+ cancellationToken = new CancellationTokenSource();
+ exception = null;
+ success = false;
+
var microseconds = BitConverter.ToUInt64(data.ToArray(), 0);
var interval = TimeInterval.FromMicroseconds(microseconds);
- parent.Log(LogLevel.Info, "Executing RunFor({0}) command", interval);
- EmulationManager.Instance.CurrentEmulation.RunFor(interval);
+ var thread = new Thread(() =>
+ {
+ try
+ {
+ parent.Log(LogLevel.Info, "Executing RunFor({0}) command", interval);
+ EmulationManager.Instance.CurrentEmulation.RunFor(interval);
- return Response.Success(Command.RunFor);
+ if(cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ success = true;
+ }
+ catch(Exception e)
+ {
+ exception = e;
+ }
+ cancellationToken?.Cancel();
+ })
+ {
+ IsBackground = true,
+ Name = GetType().Name
+ };
+
+ thread.Start();
+ cancellationToken.Token.WaitHandle.WaitOne();
+ cancellationToken = null;
+
+ if(exception != null)
+ {
+ throw exception;
+ }
+
+ if(success)
+ {
+ return Response.Success(Identifier);
+ }
+ return Response.CommandFailed(Identifier, "RunFor was interrupted");
}
public override Command Identifier => Command.RunFor;
public override byte Version => 0x0;
+
+ private bool success;
+ private Exception exception;
+ private CancellationTokenSource cancellationToken;
+ private bool disposed;
}
}