/// <summary>
/// Requests a page from the buffered pool.
/// If there is not a free one available, method will block
/// and request a collection of unused pages by raising
/// <see cref="RequestCollection"/> event.
/// </summary>
/// <param name="index">the index id of the page that was allocated</param>
/// <param name="addressPointer"> outputs a address that can be used
/// to access this memory address. You cannot call release with this parameter.
/// Use the returned index to release pages.</param>
/// <remarks>
/// IMPORTANT NOTICE: Be careful when calling this method as the calling thread
/// will block if no memory is available to have a background collection to occur.
///
/// There is a possiblity for a deadlock if calling this method from within a lock.
///
/// The page allocated will not be initialized,
/// so assume that the data is garbage.</remarks>
public void AllocatePage(out int index, out IntPtr addressPointer)
{
if (m_pageList.TryGetNextPage(out index, out addressPointer))
{
return;
}
lock (m_syncAllocate)
{
//m_releasePageVersion is approximately the number of times that a release page function has been called.
// due to race conditions, the number may not be exact, but it will have at least changed.
while (true)
{
int releasePageVersion = m_releasePageVersion;
if (m_pageList.TryGetNextPage(out index, out addressPointer))
{
return;
}
RequestMoreFreeBlocks();
if (releasePageVersion == m_releasePageVersion)
{
Log.Publish(MessageLevel.Critical, MessageFlags.PerformanceIssue, "Out Of Memory", string.Format("Memory pool has run out of memory: Current Usage: {0}MB", CurrentCapacity / 1024 / 1024));
throw new OutOfMemoryException("Memory pool is full");
}
//Due to a race condition, it is possible that someone else get the freed block
//and we must request freeing again.
}
}
}