ServerManager.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using Ryujinx.Horizon.Sdk.OsTypes;
  2. using Ryujinx.Horizon.Sdk.Sf.Cmif;
  3. using Ryujinx.Horizon.Sdk.Sm;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Numerics;
  7. namespace Ryujinx.Horizon.Sdk.Sf.Hipc
  8. {
  9. class ServerManager : ServerManagerBase, IDisposable
  10. {
  11. private readonly SmApi _sm;
  12. private readonly int _pointerBufferSize;
  13. private readonly bool _canDeferInvokeRequest;
  14. private readonly int _maxSessions;
  15. private ulong _pointerBuffersBaseAddress;
  16. private ulong _savedMessagesBaseAddress;
  17. private readonly object _resourceLock;
  18. private readonly ulong[] _sessionAllocationBitmap;
  19. private readonly HashSet<ServerSession> _sessions;
  20. private readonly HashSet<Server> _servers;
  21. public ServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(sm, options)
  22. {
  23. _sm = sm;
  24. _pointerBufferSize = options.PointerBufferSize;
  25. _canDeferInvokeRequest = options.CanDeferInvokeRequest;
  26. _maxSessions = maxSessions;
  27. if (allocator != null)
  28. {
  29. _pointerBuffersBaseAddress = allocator.Allocate((ulong)maxSessions * (ulong)options.PointerBufferSize);
  30. if (options.CanDeferInvokeRequest)
  31. {
  32. _savedMessagesBaseAddress = allocator.Allocate((ulong)maxSessions * (ulong)Api.TlsMessageBufferSize);
  33. }
  34. }
  35. _resourceLock = new object();
  36. _sessionAllocationBitmap = new ulong[(maxSessions + 63) / 64];
  37. _sessions = new HashSet<ServerSession>();
  38. _servers = new HashSet<Server>();
  39. }
  40. private PointerAndSize GetObjectBySessionIndex(ServerSession session, ulong baseAddress, ulong size)
  41. {
  42. return new PointerAndSize(baseAddress + (ulong)session.SessionIndex * size, size);
  43. }
  44. protected override ServerSession AllocateSession(int sessionHandle, ServiceObjectHolder obj)
  45. {
  46. int sessionIndex = -1;
  47. lock (_resourceLock)
  48. {
  49. if (_sessions.Count >= _maxSessions)
  50. {
  51. return null;
  52. }
  53. for (int i = 0; i <_sessionAllocationBitmap.Length; i++)
  54. {
  55. ref ulong mask = ref _sessionAllocationBitmap[i];
  56. if (mask != ulong.MaxValue)
  57. {
  58. int bit = BitOperations.TrailingZeroCount(~mask);
  59. sessionIndex = i * 64 + bit;
  60. mask |= 1UL << bit;
  61. break;
  62. }
  63. }
  64. if (sessionIndex == -1)
  65. {
  66. return null;
  67. }
  68. ServerSession session = new(sessionIndex, sessionHandle, obj);
  69. _sessions.Add(session);
  70. return session;
  71. }
  72. }
  73. protected override void FreeSession(ServerSession session)
  74. {
  75. if (session.ServiceObjectHolder.ServiceObject is IDisposable disposableObj)
  76. {
  77. disposableObj.Dispose();
  78. }
  79. lock (_resourceLock)
  80. {
  81. _sessionAllocationBitmap[session.SessionIndex / 64] &= ~(1UL << (session.SessionIndex & 63));
  82. _sessions.Remove(session);
  83. }
  84. }
  85. protected override Server AllocateServer(
  86. int portIndex,
  87. int portHandle,
  88. ServiceName name,
  89. bool managed,
  90. ServiceObjectHolder staticHoder)
  91. {
  92. lock (_resourceLock)
  93. {
  94. Server server = new(portIndex, portHandle, name, managed, staticHoder);
  95. _servers.Add(server);
  96. return server;
  97. }
  98. }
  99. protected override void DestroyServer(Server server)
  100. {
  101. lock (_resourceLock)
  102. {
  103. server.UnlinkFromMultiWaitHolder();
  104. Os.FinalizeMultiWaitHolder(server);
  105. if (server.Managed)
  106. {
  107. // We should AbortOnFailure, but sometimes SM is already gone when this is called,
  108. // so let's just ignore potential errors.
  109. _sm.UnregisterService(server.Name);
  110. HorizonStatic.Syscall.CloseHandle(server.PortHandle);
  111. }
  112. _servers.Remove(server);
  113. }
  114. }
  115. protected override PointerAndSize GetSessionPointerBuffer(ServerSession session)
  116. {
  117. if (_pointerBufferSize > 0)
  118. {
  119. return GetObjectBySessionIndex(session, _pointerBuffersBaseAddress, (ulong)_pointerBufferSize);
  120. }
  121. else
  122. {
  123. return PointerAndSize.Empty;
  124. }
  125. }
  126. protected override PointerAndSize GetSessionSavedMessageBuffer(ServerSession session)
  127. {
  128. if (_canDeferInvokeRequest)
  129. {
  130. return GetObjectBySessionIndex(session, _savedMessagesBaseAddress, Api.TlsMessageBufferSize);
  131. }
  132. else
  133. {
  134. return PointerAndSize.Empty;
  135. }
  136. }
  137. protected virtual void Dispose(bool disposing)
  138. {
  139. if (disposing)
  140. {
  141. lock (_resourceLock)
  142. {
  143. ServerSession[] sessionsToClose = new ServerSession[_sessions.Count];
  144. _sessions.CopyTo(sessionsToClose);
  145. foreach (ServerSession session in sessionsToClose)
  146. {
  147. CloseSessionImpl(session);
  148. }
  149. Server[] serversToClose = new Server[_servers.Count];
  150. _servers.CopyTo(serversToClose);
  151. foreach (Server server in serversToClose)
  152. {
  153. DestroyServer(server);
  154. }
  155. }
  156. }
  157. }
  158. public void Dispose()
  159. {
  160. Dispose(true);
  161. }
  162. }
  163. }