1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Threading;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Time;
13 
14 namespace Antmicro.Renode.Network.ExternalControl
15 {
16     public class RunFor : BaseCommand, IDisposable
17     {
RunFor(ExternalControlServer parent)18         public RunFor(ExternalControlServer parent)
19             : base(parent)
20         {
21         }
22 
Dispose()23         public void Dispose()
24         {
25             if(cancellationToken != null)
26             {
27                 parent.Log(LogLevel.Warning, "RunFor disposed while running");
28             }
29             cancellationToken?.Cancel();
30             cancellationToken = null;
31             disposed = true;
32         }
33 
Invoke(List<byte> data)34         public override Response Invoke(List<byte> data)
35         {
36             if(disposed)
37             {
38                 return Response.CommandFailed(Identifier, "Command is unavailable");
39             }
40 
41             if(data.Count != 8)
42             {
43                 return Response.CommandFailed(Identifier, "Expected 8 bytes of payload");
44             }
45 
46             if(cancellationToken != null)
47             {
48                 return Response.CommandFailed(Identifier, "One RunFor command can be running at any given time");
49             }
50 
51             cancellationToken = new CancellationTokenSource();
52             exception = null;
53             success = false;
54 
55             var microseconds = BitConverter.ToUInt64(data.ToArray(), 0);
56             var interval = TimeInterval.FromMicroseconds(microseconds);
57 
58             var thread = new Thread(() =>
59             {
60                 try
61                 {
62                     parent.Log(LogLevel.Info, "Executing RunFor({0}) command", interval);
63                     EmulationManager.Instance.CurrentEmulation.RunFor(interval);
64 
65                     if(cancellationToken.IsCancellationRequested)
66                     {
67                         return;
68                     }
69 
70                     success = true;
71                 }
72                 catch(Exception e)
73                 {
74                     exception = e;
75                 }
76                 cancellationToken?.Cancel();
77             })
78             {
79                 IsBackground = true,
80                 Name = GetType().Name
81             };
82 
83             thread.Start();
84             cancellationToken.Token.WaitHandle.WaitOne();
85             cancellationToken = null;
86 
87             if(exception != null)
88             {
89                 throw exception;
90             }
91 
92             if(success)
93             {
94                 return Response.Success(Identifier);
95             }
96             return Response.CommandFailed(Identifier, "RunFor was interrupted");
97         }
98 
99         public override Command Identifier => Command.RunFor;
100         public override byte Version => 0x0;
101 
102         private bool success;
103         private Exception exception;
104         private CancellationTokenSource cancellationToken;
105         private bool disposed;
106     }
107 }
108