using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; namespace Ryujinx.Horizon.Sdk.Sf.Cmif { class ServerDomainManager { private class EntryManager { public class Entry { public int Id { get; } public Domain Owner { get; set; } public ServiceObjectHolder Obj { get; set; } public LinkedListNode Node { get; set; } public Entry(int id) { Id = id; } } private readonly LinkedList _freeList; private readonly Entry[] _entries; public EntryManager(int count) { _freeList = new LinkedList(); _entries = new Entry[count]; for (int i = 0; i < count; i++) { _freeList.AddLast(_entries[i] = new Entry(i + 1)); } } public Entry AllocateEntry() { lock (_freeList) { if (_freeList.Count == 0) { return null; } var entry = _freeList.First.Value; _freeList.RemoveFirst(); return entry; } } public void FreeEntry(Entry entry) { lock (_freeList) { DebugUtil.Assert(entry.Owner == null); DebugUtil.Assert(entry.Obj == null); _freeList.AddFirst(entry); } } public Entry GetEntry(int id) { if (id == 0) { return null; } int index = id - 1; if ((uint)index >= (uint)_entries.Length) { return null; } return _entries[index]; } } private class Domain : DomainServiceObject, IDisposable { private readonly ServerDomainManager _manager; private readonly LinkedList _entries; public Domain(ServerDomainManager manager) { _manager = manager; _entries = new LinkedList(); } public override ServiceObjectHolder GetObject(int id) { var entry = _manager._entryManager.GetEntry(id); if (entry == null) { return null; } lock (_manager._entryOwnerLock) { if (entry.Owner != this) { return null; } } return entry.Obj.Clone(); } public override ServerDomainBase GetServerDomain() { return this; } public override void RegisterObject(int id, ServiceObjectHolder obj) { var entry = _manager._entryManager.GetEntry(id); DebugUtil.Assert(entry != null); lock (_manager._entryOwnerLock) { DebugUtil.Assert(entry.Owner == null); entry.Owner = this; entry.Node = _entries.AddLast(entry); } entry.Obj = obj; } public override Result ReserveIds(Span outIds) { for (int i = 0; i < outIds.Length; i++) { var entry = _manager._entryManager.AllocateEntry(); if (entry == null) { return SfResult.OutOfDomainEntries; } DebugUtil.Assert(entry.Owner == null); outIds[i] = entry.Id; } return Result.Success; } public override ServiceObjectHolder UnregisterObject(int id) { var entry = _manager._entryManager.GetEntry(id); if (entry == null) { return null; } ServiceObjectHolder obj; lock (_manager._entryOwnerLock) { if (entry.Owner != this) { return null; } entry.Owner = null; obj = entry.Obj; entry.Obj = null; _entries.Remove(entry.Node); entry.Node = null; } _manager._entryManager.FreeEntry(entry); return obj; } public override void UnreserveIds(ReadOnlySpan ids) { for (int i = 0; i < ids.Length; i++) { var entry = _manager._entryManager.GetEntry(ids[i]); DebugUtil.Assert(entry != null); DebugUtil.Assert(entry.Owner == null); _manager._entryManager.FreeEntry(entry); } } public void Dispose() { foreach (var entry in _entries) { if (entry.Obj.ServiceObject is IDisposable disposableObj) { disposableObj.Dispose(); } } _manager.FreeDomain(this); } } private readonly EntryManager _entryManager; private readonly object _entryOwnerLock; private readonly HashSet _domains; private int _maxDomains; public ServerDomainManager(int entryCount, int maxDomains) { _entryManager = new EntryManager(entryCount); _entryOwnerLock = new object(); _domains = new HashSet(); _maxDomains = maxDomains; } public DomainServiceObject AllocateDomainServiceObject() { lock (_domains) { if (_domains.Count == _maxDomains) { return null; } var domain = new Domain(this); _domains.Add(domain); return domain; } } public static void DestroyDomainServiceObject(DomainServiceObject obj) { ((Domain)obj).Dispose(); } private void FreeDomain(Domain domain) { lock (_domains) { _domains.Remove(domain); } } } }