public static IntPtr Get(int size, int referenceCount) { const int maxSize = 16 * 1024 * 1024; if (size < 0 || size > maxSize) { throw new ArgumentOutOfRangeException(nameof(size)); } if (referenceCount <= 0) { throw new ArgumentOutOfRangeException(nameof(referenceCount)); } AllocateBuffer(size, out var ptr, out var actualSize); var counter = AllocateReferenceCounter(ptr, actualSize, referenceCount); #if DEBUG SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Verbose, $"{nameof( BufferManager )} allocated {ptr.ToInt64():X8} (size={size}, actualSize={actualSize}) with {referenceCount} references"); #endif lock ( ReferenceCounters ) { ReferenceCounters.Add(ptr, counter); } return(ptr); }
public void Set(IntPtr ptr, int size, int referenceCount) { if (ptr == IntPtr.Zero) { throw new ArgumentNullException(nameof(ptr)); } if (size <= 0) { throw new ArgumentOutOfRangeException(nameof(size)); } if (referenceCount <= 0) { throw new ArgumentOutOfRangeException(nameof(referenceCount)); } Pointer = ptr; Size = size; var prevCount = Interlocked.Exchange(ref _count, referenceCount); if (prevCount != 0) { #if DEBUG SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Warning, $"{nameof( BufferManager )} set reference count when current count was not 0"); #endif } }
public bool Decrement() { var newCount = Interlocked.Decrement(ref _count); if (newCount < 0) { SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Bug, $"Prevented double free of {nameof(BufferManager)} pointer"); return(false); } return(newCount == 0); }
private static void FreeBuffer(IntPtr ptr, int size) { var bucketSize = GetBucketSize(size); var bucketLimit = GetBucketLimit(size); if (bucketSize <= 0 || bucketLimit <= 0) { // not bucketed, no pooling for this size Marshal.FreeHGlobal(ptr); #if DEBUG SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Verbose, $"{nameof( BufferManager )} freed unpooled pointer {ptr.ToInt64():X8} (size={size})"); #endif return; } lock ( BufferPools ) { if (!BufferPools.TryGetValue(bucketSize, out var bucketPool)) { bucketPool = new Stack <IntPtr>(bucketLimit); BufferPools.Add(bucketSize, bucketPool); } if (bucketPool.Count >= bucketLimit) { // pool overflow, get rid Marshal.FreeHGlobal(ptr); #if DEBUG SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Verbose, $"{nameof( BufferManager )} pool overflow, freed pooled pointer {ptr.ToInt64():X8} (size={size})"); #endif return; } bucketPool.Push(ptr); #if DEBUG SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Verbose, $"{nameof( BufferManager )} returned pointer to pool {ptr.ToInt64():X8} (size={size})"); #endif } }
private static void Free(NetMsg *msg) { var ptr = msg->DataPtr; lock ( ReferenceCounters ) { if (!ReferenceCounters.TryGetValue(ptr, out var counter)) { SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Bug, $"Attempt to free pointer not tracked by {nameof(BufferManager)}: {ptr.ToInt64():X8}"); return; } #if DEBUG SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Verbose, $"{nameof( BufferManager )} decrementing reference count of {ptr.ToInt64():X8}"); #endif if (counter.Decrement()) { #if DEBUG SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Verbose, $"{nameof( BufferManager )} freeing {ptr.ToInt64():X8} as it is now unreferenced"); if (ptr != counter.Pointer) { SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Bug, $"{nameof( BufferManager )} freed pointer ({ptr.ToInt64():X8}) does not match counter pointer ({counter.Pointer.ToInt64():X8})"); } var bucketSize = GetBucketSize(counter.Size); if (counter.Size != bucketSize) { SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Bug, $"{nameof( BufferManager )} freed pointer size ({counter.Size}) does not match bucket size ({bucketSize})"); } #endif ReferenceCounters.Remove(ptr); FreeBuffer(ptr, counter.Size); FreeReferenceCounter(counter); } } }
private static void AllocateBuffer(int minimumSize, out IntPtr ptr, out int size) { var bucketSize = GetBucketSize(minimumSize); if (bucketSize <= 0) { // not bucketed, no pooling for this size ptr = Marshal.AllocHGlobal(minimumSize); size = minimumSize; #if DEBUG SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Verbose, $"{nameof( BufferManager )} allocated unpooled pointer {ptr.ToInt64():X8} (size={size})"); #endif return; } lock ( BufferPools ) { if (!BufferPools.TryGetValue(bucketSize, out var bucketPool) || bucketPool.Count == 0) { // nothing pooled yet, but we can pool this size ptr = Marshal.AllocHGlobal(bucketSize); size = bucketSize; #if DEBUG SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Verbose, $"{nameof( BufferManager )} allocated new poolable pointer {ptr.ToInt64():X8} (size={size})"); #endif return; } ptr = bucketPool.Pop(); size = bucketSize; #if DEBUG SteamNetworkingUtils.LogDebugMessage(NetDebugOutput.Verbose, $"{nameof( BufferManager )} allocated pointer from pool {ptr.ToInt64():X8} (size={size})"); #endif } }