I have a scenario when I need to operate on large array inside some inner function (a service) but result of this operation is to be consumed (serialized to JSON and returned over HTTP) by parent function:
public IActionResult ParentFunction()
{
var returnedArray = InnerFunction(1000);
return Ok(returnedArray.Take(1000));
}
public int[] InnerFunction(int count)
{
var rentedArray = _defaultArrayPool.Rent(count);
// make operation on rentedArray
return rentedArray;
}
In the above code obviously array is not returned to _defaultArrayPool thus it is never reused.
I considered several options, but I am willing to know what's the best implementation?
Option 1 - Return by parent function
I don't like this option because Rent and Return are called in different parts of the code.
public IActionResult ParentFunction()
{
int[] returnedArray = null;
try
{
returnedArray = InnerFunction(1000);
return Ok(returnedArray.Take(1000));
}
finally
{
if (returnedArray != null)
{
_defaultArrayPool.Return(returnedArray);
}
}
}
public int[] InnerFunction(int count)
{
var rentedArray = _defaultArrayPool.Rent(count);
// make operation on rentedArray
return rentedArray;
}
Option 2 - Rent and Return by parent function, and pass as reference
It is better, but won't work if ParentFunction does not know the length/count upfront.
public IActionResult ParentFunction()
{
var rentedArray = _defaultArrayPool.Rent(1000); // will not work if 'Count' is unknown here, and is to be determined by InnerFunction
try
{
InnerFunction(rentedArray, 1000);
return Ok(rentedArray.Take(1000));
}
finally
{
if (rentedArray != null)
{
_defaultArrayPool.Return(rentedArray);
}
}
}
public void InnerFunction(int[] arr, int count)
{
// make operation on arr
}
Option 3 - Rent and Return by different functions
It will work when inner function is determining needed count/length
public IActionResult ParentFunction()
{
int[] rentedArray = null;
try
{
var count = InnerFunction(out rentedArray);
return Ok(rentedArray.Take(count));
}
finally
{
if (rentedArray != null)
{
_defaultArrayPool.Return(rentedArray);
}
}
}
public int InnerFunction(out int[] arr)
{
int count = 1000; // determin lenght of the array
arr = _defaultArrayPool.Rent(count);
// make operation on arr
return count;
}
Are there any other better options?
Rather than any of the above, I would use the basic dispose pattern to return some
IDisposablethat wraps the rented array and returns it when disposed.First define the following disposable wrapper:
And now you can write your parent and inner functions as follows:
Mock-up fiddle #1 here.
That being said, there is a fundamental problem with all of your implementations: you return the rented array back to the array pool before your
OkObjectResultis actually executed and the value serialized to the response stream. Thus what you return may well be random if the same array pool memory is subsequently rented elsewhere in the interim.What are your options to work around this? A couple options come to mind.
Firstly, you could consider returning some enumerable wrapper that disposes the
RentedArrayWrapperafter a single iteration, like so:While this works, it seems sketchy to me because my general feeling is that enumerators should not have side-effects. Mock-up fiddle #2 here.
Secondly, you might consider subclassing
OkObjectResultwhich is the type returned byControllerBase.Ok(object)and making it dispose its value after being executed, like so:And then returning your rented array wrapper like so:
Mock-up fiddle #3 here.