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 
8 using System.Threading;
9 using Antmicro.Renode.Time;
10 using Antmicro.Migrant;
11 using Antmicro.Migrant.Hooks;
12 using System;
13 
14 namespace Antmicro.Renode.Utilities
15 {
16     public static class TimeSourceBaseExtensions
17     {
18         // note: this method currently implements
19         // the not-sooner-than behaviour which means
20         // that it's guaranteed the timeout event
21         // will not trigger earlier than current time
22         // plus `virtualMilliseconds` (except for the
23         // serialization, in which case the event will
24         // be triggered immediately);
25         // technically the event is triggered in the synchronization phase,
26         // so it might be delayed maximally by the size of the quantum
EnqueueTimeoutEvent(this TimeSourceBase timeSource, ulong virtualMilliseconds, Action callback = null)27         public static TimeoutEvent EnqueueTimeoutEvent(this TimeSourceBase timeSource, ulong virtualMilliseconds,
28             Action callback = null)
29         {
30             TimeoutEvent timeoutEvent = null;
31 
32             if(virtualMilliseconds == 0)
33             {
34                 timeoutEvent = new TimeoutEvent(timeSource);
35                 timeoutEvent.Trigger();
36             }
37             else
38             {
39                 var when = timeSource.ElapsedVirtualTime + TimeInterval.FromMilliseconds(virtualMilliseconds);
40                 var actionId = timeSource.ExecuteInSyncedState(_ =>
41                 {
42                     callback?.Invoke();
43                     timeoutEvent.Trigger();
44                 }, new TimeStamp(when, timeSource.Domain));
45                 timeoutEvent = new TimeoutEvent(timeSource, actionId);
46             }
47 
48             return timeoutEvent;
49         }
50     }
51 
52     public class TimeoutEvent
53     {
TimeoutEvent(TimeSourceBase timeSource, ulong? actionId = null)54         public TimeoutEvent(TimeSourceBase timeSource, ulong? actionId = null)
55         {
56             waitHandle = new AutoResetEvent(false);
57             this.timeSource = timeSource;
58             this.actionId = actionId;
59         }
60 
Trigger()61         public void Trigger()
62         {
63             IsTriggered = true;
64             waitHandle.Set();
65         }
66 
Cancel()67         public void Cancel()
68         {
69             if(actionId == null)
70             {
71                 return;
72             }
73             timeSource.CancelActionToExecuteInSyncedState(actionId.Value);
74         }
75 
76         public WaitHandle WaitHandle => waitHandle;
77 
78         public bool IsTriggered { get; private set; }
79 
80         [PreSerialization]
PreSerialization()81         private void PreSerialization()
82         {
83             // We cannot serialize `waitHandle` as
84             // it internally contains an IntPtr;
85             // that's why we use constructor attribute.
86             // This, however, causes the original wait handle
87             // to be lost - everything waiting for it
88             // would timeout anyway after deserialization.
89             Trigger();
90         }
91 
92         [Constructor(false)]
93         private AutoResetEvent waitHandle;
94         // We cannot serialize the timeSource but since we trigger the event after deserialization
95         // anyway, it doesn't matter since it can't be canceled at that point.
96         [Transient]
97         private readonly TimeSourceBase timeSource;
98         // We also clear out actionId to make Cancel a no-op after deserialization.
99         [Transient]
100         private readonly ulong? actionId;
101     }
102 }
103 
104