ryujinx/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs
gdkchan 08831eecf7
IPC refactor part 3+4: New server HIPC message processor (#4188)
* IPC refactor part 3 + 4: New server HIPC message processor with source generator based serialization

* Make types match on calls to AlignUp/AlignDown

* Formatting

* Address some PR feedback

* Move BitfieldExtensions to Ryujinx.Common.Utilities and consolidate implementations

* Rename Reader/Writer to SpanReader/SpanWriter and move to Ryujinx.Common.Memory

* Implement EventType

* Address more PR feedback

* Log request processing errors since they are not normal

* Rename waitable to multiwait and add missing lock

* PR feedback

* Ac_K PR feedback
2023-01-04 23:15:45 +01:00

247 lines
6.8 KiB
C#

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);
}
}
}
}