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.Linq;
8 using System.Collections;
9 using System.Collections.Generic;
10 using System.Threading;
11 
12 namespace Antmicro.Renode.Time
13 {
14     /// <summary>
15     /// Represents a collection of time handles and allows to iterate over it in an optimal way.
16     /// </summary>
17     /// <remarks>
18     /// It keeps track of handles not ready for a new time grant in a separate list so they can be accessed faster.
19     /// If there are any not ready time handles it will iterate only over them.
20     /// </remarks>
21     public sealed class HandlesCollection : IEnumerable<TimeHandle>
22     {
23         /// <summary>
24         /// Creates new empty collection.
25         /// </summary>
HandlesCollection()26         public HandlesCollection()
27         {
28             ready = new LinkedList<TimeHandle>();
29             notReady = new LinkedList<TimeHandle>();
30             locker = new object();
31         }
32 
33         /// <summary>
34         /// Executes `Latch` method on all handles and removes the disposed ones from the collection.
35         /// </summary>
LatchAllAndCollectGarbage()36         public void LatchAllAndCollectGarbage()
37         {
38             var wasLocked = false;
39             try
40             {
41                 InnerLatchAndCollectGarbage(ref wasLocked, notReady);
42                 InnerLatchAndCollectGarbage(ref wasLocked, ready);
43             }
44             finally
45             {
46                 if(wasLocked)
47                 {
48                     Monitor.Exit(locker);
49                 }
50             }
51         }
52 
53         /// <summary>
54         /// Executes `Unlatch` method on all handles.
55         /// </summary>
UnlatchAll()56         public void UnlatchAll()
57         {
58             foreach(var h in All)
59             {
60                 h.Unlatch();
61             }
62 
63             // After unlatch some handles might be disabled.
64             // Disabling not ready handles make them ready.
65             // We need to update list to reflect changes.
66             var currentNode = notReady.First;
67             while(currentNode != null)
68             {
69                 var next = currentNode.Next;
70                 UpdateHandle(currentNode);
71                 currentNode = next;
72             }
73         }
74 
75         /// <summary>
76         /// Adds new handle to the collection.
77         /// </summary>
78         /// <remarks>
79         /// Depending of the value of <see cref="IsReadyForNewTimeGrant">, the handle is put either in <see cref="ready"> or <see cref="notReady"> queue.
80         /// </remarks>
Add(TimeHandle handle)81         public void Add(TimeHandle handle)
82         {
83             lock(locker)
84             {
85                 (handle.IsReadyForNewTimeGrant ? ready : notReady).AddLast(handle);
86                 handle.IsDone = handle.IsReadyForNewTimeGrant;
87             }
88         }
89 
90         /// <summary>
91         /// Updates state of a the handle by moving it to a proper queue.
92         /// </summary>
UpdateHandle(LinkedListNode<TimeHandle> handle)93         public void UpdateHandle(LinkedListNode<TimeHandle> handle)
94         {
95             var isOnReadyList = handle.List == ready;
96             if((handle.Value.IsReadyForNewTimeGrant && isOnReadyList)
97                || (!handle.Value.IsReadyForNewTimeGrant && !isOnReadyList))
98             {
99                 return;
100             }
101 
102             lock(locker)
103             {
104                 if(isOnReadyList)
105                 {
106                     ready.Remove(handle);
107                     notReady.AddLast(handle);
108                     handle.Value.IsDone = false;
109                 }
110                 else
111                 {
112                     notReady.Remove(handle);
113                     ready.AddLast(handle);
114                     handle.Value.IsDone = true;
115                 }
116             }
117         }
118 
119         /// <summary>
120         /// Returns an enumerator over the part of handles collection - either not-ready-for-a-new-time-grant handles (if any) or ready ones (otherwise).
121         /// </summary>
GetEnumerator()122         public IEnumerator<TimeHandle> GetEnumerator()
123         {
124             return WithLinkedListNode.Select(x => x.Value).GetEnumerator();
125         }
126 
127         /// <summary>
128         /// Calculates a base elapsed virtual time (minimal) for all handles.
129         /// Returns `false` if the handles collection is empty.
130         /// </summary>
TryGetCommonElapsedTime(out TimeInterval commonElapsedTime)131         public bool TryGetCommonElapsedTime(out TimeInterval commonElapsedTime)
132         {
133             lock(locker)
134             {
135                 if(notReady.Count == 0 && ready.Count == 0)
136                 {
137                     commonElapsedTime = TimeInterval.Empty;
138                     return false;
139                 }
140                 commonElapsedTime = All.Min(x => x.TotalElapsedTime);
141                 return true;
142             }
143         }
144 
145         /// <summary>
146         /// Gets an enumerator over the part of handles collection - either not-ready-for-a-new-time-grant handles (if any) or ready ones (otherwise) returning objects of <see cref="LinkedListNode{TimeHandle}"/>.
147         /// </summary>
148         public IEnumerable<LinkedListNode<TimeHandle>> WithLinkedListNode
149         {
150             get
151             {
152                 var currentNode = (AreAllReadyForNewGrant ? ready : notReady).First;
153                 while(currentNode != null)
154                 {
155                     var next = currentNode.Next;
156                     yield return currentNode;
157                     currentNode = next;
158                 }
159             }
160         }
161 
162         /// <summary>
163         /// Returns true if all handles in collection are in ready-for-a-new-grant state.
164         /// </summary>
165         public bool AreAllReadyForNewGrant { get { return notReady.Count == 0; } }
166 
167         /// <summary>
168         /// Returns the number of handles that would be enumerated by <see cref="GetEnumerator">, i.e., either not-ready-for-a-new-grant (if any) or ready-for-a-new-grant (otherwise).
169         /// </summary>
170         public int ActiveCount { get { return AreAllReadyForNewGrant ? ready.Count : notReady.Count; } }
171 
172         /// <summary>
173         /// Returns an enumeration of all handles in the collection - not-ready queue concatenated with ready one.
174         /// </summary>
175         public IEnumerable<TimeHandle> All { get { return notReady.Concat(ready); } }
176 
177         /// <see cref="GetEnumerator">
IEnumerable.GetEnumerator()178         IEnumerator IEnumerable.GetEnumerator()
179         {
180             return this.GetEnumerator();
181         }
182 
InnerLatchAndCollectGarbage(ref bool wasLocked, LinkedList<TimeHandle> list)183         private void InnerLatchAndCollectGarbage(ref bool wasLocked, LinkedList<TimeHandle> list)
184         {
185             var current = list.First;
186             while(current != null)
187             {
188                 var next = current.Next;
189                 current.Value.Latch();
190 
191                 if(current.Value.DetachRequested)
192                 {
193                     if(!wasLocked)
194                     {
195                         Monitor.Enter(locker, ref wasLocked);
196                     }
197                     list.Remove(current);
198                     current.Value.Unlatch();
199                 }
200 
201                 current = next;
202             }
203         }
204 
205         private readonly LinkedList<TimeHandle> ready;
206         private readonly LinkedList<TimeHandle> notReady;
207         private readonly object locker;
208     }
209 }
210