2021-12-01 00:58:31 +00:00
using Ryujinx.Common.Logging ;
using System ;
2021-11-28 17:41:57 +00:00
using System.Buffers ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Diagnostics.CodeAnalysis ;
using System.Text ;
using System.Threading ;
namespace Ryujinx.Common.Pools
{
/// <summary>
/// Represents a reusable pooled buffer of some type T that was rented from a <see cref="BufferPool{T}"/>.
2021-12-01 01:27:41 +00:00
/// This buffer should be disposed when you are done using it, after which it is returned to the pool to be reclaimed.
2021-11-28 17:41:57 +00:00
/// </summary>
/// <typeparam name="T">The type of data that this buffer holds.</typeparam>
public class PooledBuffer < T > : IDisposable
{
private int _disposed = 0 ;
internal PooledBuffer ( T [ ] data , int length )
{
2021-11-29 01:52:51 +00:00
_buffer = data ;
2021-11-28 17:41:57 +00:00
Length = length ;
}
2021-12-01 00:58:31 +00:00
#if TRACK_BUFFERPOOL_LEAKS
2021-11-28 17:41:57 +00:00
private string _lastRentalStackTrace = "Unknown" ;
#pragma warning disable CA1063 // suppress "improper" finalizer style
~ PooledBuffer ( )
{
if ( Length > 0 )
{
2021-12-01 00:58:31 +00:00
Logger . Warning ? . Print ( LogClass . Application , $"Buffer pool leak detected: Buffer was rented by {_lastRentalStackTrace}" ) ;
2021-11-28 17:41:57 +00:00
Dispose ( false ) ;
}
}
#pragma warning restore CA1063
internal void MarkRented ( )
{
string stackTrace = new StackTrace ( ) . ToString ( ) ;
string objectTypeName = nameof ( BufferPool < T > ) ;
string traceLine ;
int startIdx = 0 ;
while ( startIdx < stackTrace . Length )
{
startIdx = stackTrace . IndexOf ( '\n' , startIdx ) ;
if ( startIdx < 0 )
{
break ;
}
startIdx + = 7 ; // trim the " at " prefix
int endIdx = stackTrace . IndexOf ( '\n' , startIdx ) - 1 ;
if ( endIdx < 0 )
{
endIdx = stackTrace . Length ;
}
traceLine = stackTrace . Substring ( startIdx , endIdx - startIdx ) ;
if ( ! traceLine . Contains ( objectTypeName ) )
{
_lastRentalStackTrace = traceLine ;
return ;
}
}
}
2021-12-01 00:58:31 +00:00
#endif // TRACK_BUFFERPOOL_LEAKS
2021-11-28 17:41:57 +00:00
/// <summary>
2021-12-01 01:27:41 +00:00
/// The contiguous buffer to store data in. Intentionally not exposed to callers except as a <see cref="Span{T}"/>, to prevent
/// the pooled array handle from leaking. The actual array is usually larger than <see cref="Length"/> because of how ArrayPool behaves.
2021-11-28 17:41:57 +00:00
/// </summary>
2021-11-29 01:52:51 +00:00
private T [ ] _buffer ;
2021-11-28 17:41:57 +00:00
/// <summary>
2021-12-01 01:27:41 +00:00
/// The buffer's allocated length.
2021-11-28 17:41:57 +00:00
/// </summary>
public int Length { get ; private set ; }
/// <summary>
2021-11-29 01:52:51 +00:00
/// Returns a pointer to this buffer's data as a span.
2021-11-28 17:41:57 +00:00
/// </summary>
2021-11-30 21:53:49 +00:00
public Span < T > AsSpan = > _buffer . AsSpan ( 0 , Length ) ;
2021-11-29 01:52:51 +00:00
/// <summary>
/// Returns a pointer to this buffer's data as a read-only span.
/// </summary>
2021-11-30 21:53:49 +00:00
public ReadOnlySpan < T > AsReadOnlySpan = > _buffer . AsSpan ( 0 , Length ) ;
2021-11-28 17:41:57 +00:00
[SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize", Justification = "Disposal semantics are different here; we are not freeing memory but instead returning objects to a pool")]
public void Dispose ( )
{
Dispose ( true ) ;
2021-12-01 00:58:31 +00:00
#if TRACK_BUFFERPOOL_LEAKS
2021-11-28 17:41:57 +00:00
GC . SuppressFinalize ( this ) ;
#endif
}
protected virtual void Dispose ( bool disposing )
{
2021-12-01 01:27:41 +00:00
// prevent multiple disposal atomically
2021-11-28 17:41:57 +00:00
if ( Interlocked . CompareExchange ( ref _disposed , 1 , 0 ) ! = 0 )
{
return ;
}
if ( disposing & & Length > 0 )
{
// if the buffer is zero-length, just keep it zero (this prevents us from corrupting the state of the empty singleton buffer)
2021-12-01 01:27:41 +00:00
// Otherwise, nullify the reference so anyone who mistakenly still has a handle to this object should hopefully fail fast
2021-11-29 01:52:51 +00:00
ArrayPool < T > . Shared . Return ( _buffer ) ;
_buffer = null ;
2021-11-28 17:41:57 +00:00
Length = - 1 ;
}
}
}
}