aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs')
-rw-r--r--Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs246
1 files changed, 246 insertions, 0 deletions
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs
new file mode 100644
index 00000000..62ee2738
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs
@@ -0,0 +1,246 @@
+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<Entry> Node { get; set; }
+
+ public Entry(int id)
+ {
+ Id = id;
+ }
+ }
+
+ private readonly LinkedList<Entry> _freeList;
+ private readonly Entry[] _entries;
+
+ public EntryManager(int count)
+ {
+ _freeList = new LinkedList<Entry>();
+ _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<EntryManager.Entry> _entries;
+
+ public Domain(ServerDomainManager manager)
+ {
+ _manager = manager;
+ _entries = new LinkedList<EntryManager.Entry>();
+ }
+
+ 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<int> 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<int> 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<Domain> _domains;
+ private int _maxDomains;
+
+ public ServerDomainManager(int entryCount, int maxDomains)
+ {
+ _entryManager = new EntryManager(entryCount);
+ _entryOwnerLock = new object();
+ _domains = new HashSet<Domain>();
+ _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);
+ }
+ }
+ }
+}