1 // 2 // Copyright (c) 2010-2022 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.Linq; 10 using System.Collections.Generic; 11 using Antmicro.Renode.Exceptions; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals; 14 using Antmicro.Renode.Core.Structure; 15 using Antmicro.Migrant; 16 using Antmicro.Migrant.Hooks; 17 using Antmicro.Renode.Utilities; 18 19 namespace Antmicro.Renode.Core 20 { 21 public sealed class ExternalsManager : IHasChildren<IExternal> 22 { ExternalsManager()23 public ExternalsManager() 24 { 25 externals = new Dictionary<string, IExternal>(); 26 registeredIHasOwnLifeObjects = new List<SerializableWeakReference<IHasOwnLife>>(); 27 paused = true; 28 } 29 AddExternal(IExternal external, string name)30 public void AddExternal(IExternal external, string name) 31 { 32 lock(externals) 33 { 34 if(externals.ContainsValue(external)) 35 { 36 throw new RecoverableException("External already registered"); 37 } 38 if(externals.ContainsKey(name)) 39 { 40 throw new RecoverableException("External's name already in use"); 41 } 42 externals.Add(name, external); 43 } 44 45 OnExternalsChanged(external, true); 46 } 47 48 public IEnumerable<T> GetExternalsOfType<T>() where T : IExternal 49 { 50 return externals.Values.OfType<T>(); 51 } 52 RemoveExternal(IExternal external)53 public void RemoveExternal(IExternal external) 54 { 55 lock(externals) 56 { 57 if(!externals.ContainsValue(external)) 58 { 59 throw new RecoverableException("External not registered"); 60 } 61 62 externals.Remove(externals.Single(x => x.Value == external).Key); 63 } 64 65 OnExternalsChanged(external, false); 66 } 67 Clear()68 public void Clear() 69 { 70 IDisposable[] toDispose; 71 lock(externals) 72 { 73 toDispose = externals.Select(x => x.Value as IDisposable).Where(x => x != null).ToArray(); 74 externals.Clear(); 75 } 76 foreach(var td in toDispose) 77 { 78 td.Dispose(); 79 } 80 } 81 82 public bool TryGetByName<T>(string name, out T result) where T : class 83 { 84 return TryGetByNameInner(this, name, out result); 85 } 86 TryGetName(IExternal external, out string name)87 public bool TryGetName(IExternal external, out string name) 88 { 89 lock(externals) 90 { 91 var result = externals.SingleOrDefault(x => x.Value == external); 92 if(result.Value != null) 93 { 94 name = result.Key; 95 return true; 96 } 97 98 name = null; 99 return false; 100 } 101 } 102 GetNames()103 public IEnumerable<string> GetNames() 104 { 105 lock(externals) 106 { 107 var result = new List<string>(); 108 var keys = externals.Keys.ToArray(); 109 GetNamesInner(result, string.Empty, keys.Select(x => externals[x]).ToArray(), externals.Keys); 110 return result; 111 } 112 } 113 114 public IEnumerable<IExternal> Externals 115 { 116 get 117 { 118 return externals.Values; 119 } 120 } 121 Start()122 public void Start() 123 { 124 lock(externals) 125 { 126 if(!paused) 127 { 128 return; 129 } 130 paused = false; 131 var ownLifeExternals = externals.Select(x => x.Value as IHasOwnLife).Where(x => x != null); 132 if(alreadyStarted) 133 { 134 foreach(var external in ownLifeExternals) 135 { 136 external.Resume(); 137 } 138 foreach(var iHasOwnLife in registeredIHasOwnLifeObjects) 139 { 140 var target = iHasOwnLife.Target; 141 if(target != null) 142 { 143 target.Resume(); 144 } 145 } 146 return; 147 } 148 149 foreach(var external in ownLifeExternals) 150 { 151 external.Start(); 152 } 153 154 foreach(var iHasOwnLife in registeredIHasOwnLifeObjects) 155 { 156 var target = iHasOwnLife.Target; 157 if(target != null) 158 { 159 target.Start(); 160 } 161 } 162 163 alreadyStarted = true; 164 } 165 } 166 Pause()167 public void Pause() 168 { 169 lock(externals) 170 { 171 if(paused) 172 { 173 return; 174 } 175 paused = true; 176 var ownLifeExternals = externals.Select(x => x.Value as IHasOwnLife).Where(x => x != null); 177 foreach(var external in ownLifeExternals) 178 { 179 external.Pause(); 180 } 181 foreach(var iHasOwnLife in registeredIHasOwnLifeObjects) 182 { 183 var target = iHasOwnLife.Target; 184 if(target != null) 185 { 186 target.Pause(); 187 } 188 } 189 } 190 } 191 TryGetByName(string name, out bool success)192 IExternal IHasChildren<IExternal>.TryGetByName(string name, out bool success) 193 { 194 lock(externals) 195 { 196 if(externals.ContainsKey(name)) 197 { 198 success = true; 199 return externals[name]; 200 } 201 success = false; 202 return null; 203 } 204 } 205 GetNamesInner(List<string> result, string prefix, IEnumerable<object> objects, IEnumerable<string> theirNames)206 private void GetNamesInner(List<string> result, string prefix, IEnumerable<object> objects, IEnumerable<string> theirNames) 207 { 208 bool notUsed; 209 var namesEnumerator = theirNames.GetEnumerator(); 210 namesEnumerator.MoveNext(); 211 foreach(var obj in objects) 212 { 213 var currentName = string.IsNullOrEmpty(prefix) ? namesEnumerator.Current : prefix + Machine.PathSeparator + namesEnumerator.Current; 214 result.Add(currentName); 215 var withChildren = obj as IHasChildren<object>; 216 if(withChildren != null) 217 { 218 var objectsAndNames = withChildren.GetNames().Select(x => new { Name = x, Value = withChildren.TryGetByName(x, out notUsed) }) 219 .Where(x => x.Value != null).ToArray(); 220 GetNamesInner(result, currentName, objectsAndNames.Select(x => x.Value).ToArray(), objectsAndNames.Select(x => x.Name).ToArray()); 221 } 222 namesEnumerator.MoveNext(); 223 } 224 } 225 226 private bool TryGetByNameInner<T>(IHasChildren<object> currentParent, string subname, out T result) where T : class 227 { 228 if(subname == null) 229 { 230 result = null; 231 return false; 232 } 233 var parts = subname.Split(new [] { Machine.PathSeparator } , 2); 234 object candidate; 235 if(!currentParent.TryGetByName(parts[0], out candidate)) 236 { 237 result = null; 238 return false; 239 } 240 result = candidate as T; 241 if(parts.Length == 1) 242 { 243 return result != null; 244 } 245 var parent = candidate as IHasChildren<object>; 246 if(parent != null) 247 { 248 return TryGetByNameInner(parent, parts[1], out result); 249 } 250 return false; 251 } 252 253 [field: Transient] 254 public event Action<ExternalsChangedEventArgs> ExternalsChanged; 255 256 private bool alreadyStarted; 257 private bool paused; 258 259 [Constructor] 260 private readonly Dictionary<string, IExternal> externals; 261 262 // those fields are used to serialize externals (and skip the transient ones) 263 private List<string> externalsKeys; 264 private List<IExternal> externalsValues; 265 266 [PreSerialization] SerializeExternals()267 private void SerializeExternals() 268 { 269 externalsKeys = new List<string>(); 270 externalsValues = new List<IExternal>(); 271 272 foreach(var item in externals) 273 { 274 if(item.Value.GetType().GetCustomAttributes(typeof(TransientAttribute), true).Any()) 275 { 276 Logger.Log(LogLevel.Info, "Skipping serialization of the '{0}' external as it's marked as transient", item.Key); 277 continue; 278 } 279 280 externalsKeys.Add(item.Key); 281 externalsValues.Add(item.Value); 282 } 283 } 284 285 [PostDeserialization] RestoreExternals()286 private void RestoreExternals() 287 { 288 for(var i = 0; i < externalsKeys.Count; i++) 289 { 290 externals.Add(externalsKeys[i], externalsValues[i]); 291 } 292 293 externalsKeys = null; 294 externalsValues = null; 295 } 296 RegisterIHasOwnLife(IHasOwnLife own)297 public void RegisterIHasOwnLife(IHasOwnLife own) 298 { 299 lock(externals) 300 { 301 registeredIHasOwnLifeObjects.Add(new SerializableWeakReference<IHasOwnLife>(own)); 302 if(!paused) 303 { 304 own.Start(); 305 } 306 } 307 } 308 UnregisterIHasOwnLife(IHasOwnLife own)309 public void UnregisterIHasOwnLife(IHasOwnLife own) 310 { 311 lock(externals) 312 { 313 registeredIHasOwnLifeObjects.RemoveAll(x => x.Target == own); 314 if(alreadyStarted) 315 { 316 own.Pause(); 317 } 318 } 319 } 320 OnExternalsChanged(IExternal external, bool added)321 private void OnExternalsChanged(IExternal external, bool added) 322 { 323 var ec = ExternalsChanged; 324 if(ec != null) 325 { 326 ec(new ExternalsChangedEventArgs(external, added ? ExternalsChangedEventArgs.ChangeType.Added : ExternalsChangedEventArgs.ChangeType.Removed)); 327 } 328 } 329 330 private readonly List<SerializableWeakReference<IHasOwnLife>> registeredIHasOwnLifeObjects; 331 332 public class ExternalsChangedEventArgs : EventArgs 333 { ExternalsChangedEventArgs(IExternal external, ChangeType change)334 public ExternalsChangedEventArgs(IExternal external, ChangeType change) 335 { 336 External = external; 337 Change = change; 338 } 339 340 public IExternal External { get; private set; } 341 public ChangeType Change { get; private set; } 342 343 public enum ChangeType 344 { 345 Added, 346 Removed 347 } 348 } 349 } 350 } 351 352