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