Rewind - if memory allocation fails, back off the size until successful.

This commit is contained in:
jdpurcell 2015-01-18 00:20:49 +00:00
parent 9f6c0ca695
commit bcb6b20ec2
2 changed files with 59 additions and 26 deletions

View File

@ -10,13 +10,14 @@ namespace BizHawk.Client.Common
public class Rewinder public class Rewinder
{ {
private StreamBlobDatabase _rewindBuffer; private StreamBlobDatabase _rewindBuffer;
private byte[] _rewindBufferBacking;
private long? _overrideMemoryLimit;
private RewindThreader _rewindThread; private RewindThreader _rewindThread;
private byte[] _lastState; private byte[] _lastState;
private bool _rewindImpossible; private bool _rewindImpossible;
private int _rewindFrequency = 1; private int _rewindFrequency = 1;
private bool _rewindDeltaEnable; private bool _rewindDeltaEnable;
private byte[] _rewindFellationBuf; private byte[] _deltaBuffer = new byte[0];
private byte[] _tempBuf = new byte[0];
public Rewinder() public Rewinder()
{ {
@ -122,14 +123,14 @@ namespace BizHawk.Client.Common
if (rewind_enabled) if (rewind_enabled)
{ {
var cap = Global.Config.Rewind_BufferSize * (long)1024 * 1024; var capacity = Global.Config.Rewind_BufferSize * (long)1024 * 1024;
if (_rewindBuffer != null) if (_rewindBuffer != null)
{ {
_rewindBuffer.Dispose(); _rewindBuffer.Dispose();
} }
_rewindBuffer = new StreamBlobDatabase(Global.Config.Rewind_OnDisk, cap, BufferManage); _rewindBuffer = new StreamBlobDatabase(Global.Config.Rewind_OnDisk, capacity, BufferManage);
if (_rewindThread != null) if (_rewindThread != null)
{ {
@ -216,25 +217,54 @@ namespace BizHawk.Client.Common
} }
} }
private byte[] BufferManage(byte[] inbuf, long size, bool allocate) private byte[] BufferManage(byte[] inbuf, ref long size, bool allocate)
{ {
if (allocate) if (allocate)
{ {
// if we have an appropriate buffer free, return it if (size > Int32.MaxValue)
if (_rewindFellationBuf != null && _rewindFellationBuf.LongLength == size)
{ {
var ret = _rewindFellationBuf; // I think we need .NET 4.5+ on x64 to allocate > 2GB
_rewindFellationBuf = null; size = Int32.MaxValue;
return ret; }
if (_overrideMemoryLimit != null)
{
size = Math.Min(_overrideMemoryLimit.Value, size);
}
// if we have an appropriate buffer free, return it
if (_rewindBufferBacking != null)
{
var buf = _rewindBufferBacking;
_rewindBufferBacking = null;
if (buf.LongLength == size)
{
return buf;
}
} }
// otherwise, allocate it // otherwise, allocate it
do
{
try
{
return new byte[size]; return new byte[size];
} }
catch (OutOfMemoryException)
_rewindFellationBuf = inbuf; {
size /= 2;
_overrideMemoryLimit = size;
}
}
while (size > 1);
throw new OutOfMemoryException();
}
else
{
_rewindBufferBacking = inbuf;
return null; return null;
} }
}
private void CaptureRewindStateNonDelta(byte[] state) private void CaptureRewindStateNonDelta(byte[] state)
{ {
@ -277,12 +307,12 @@ namespace BizHawk.Client.Common
int changeSequenceStartOffset = 0; int changeSequenceStartOffset = 0;
int lastChangeSequenceStartOffset = 0; int lastChangeSequenceStartOffset = 0;
if (_tempBuf.Length < stateLength) if (_deltaBuffer.Length < stateLength)
{ {
_tempBuf = new byte[stateLength]; _deltaBuffer = new byte[stateLength];
} }
_tempBuf[index++] = 0; // Full state = false (i.e. delta) _deltaBuffer[index++] = 0; // Full state = false (i.e. delta)
fixed (byte* pCurrentState = &currentState[0]) fixed (byte* pCurrentState = &currentState[0])
fixed (byte* pLastState = &_lastState[0]) fixed (byte* pLastState = &_lastState[0])
@ -315,13 +345,13 @@ namespace BizHawk.Client.Common
} }
// Offset Delta // Offset Delta
VLInteger.WriteUnsigned((uint)(changeSequenceStartOffset - lastChangeSequenceStartOffset), _tempBuf, ref index); VLInteger.WriteUnsigned((uint)(changeSequenceStartOffset - lastChangeSequenceStartOffset), _deltaBuffer, ref index);
// Length // Length
VLInteger.WriteUnsigned((uint)length, _tempBuf, ref index); VLInteger.WriteUnsigned((uint)length, _deltaBuffer, ref index);
// Data // Data
Buffer.BlockCopy(_lastState, changeSequenceStartOffset, _tempBuf, index, length); Buffer.BlockCopy(_lastState, changeSequenceStartOffset, _deltaBuffer, index, length);
index += length; index += length;
inChangeSequence = false; inChangeSequence = false;
@ -329,7 +359,7 @@ namespace BizHawk.Client.Common
} }
} }
_rewindBuffer.Push(new ArraySegment<byte>(_tempBuf, 0, index)); _rewindBuffer.Push(new ArraySegment<byte>(_deltaBuffer, 0, index));
UpdateLastState(currentState); UpdateLastState(currentState);
} }

View File

@ -10,14 +10,14 @@ namespace BizHawk.Client.Common
/// </summary> /// </summary>
public class StreamBlobDatabase : IDisposable public class StreamBlobDatabase : IDisposable
{ {
private Func<byte[], long, bool, byte[]> _mBufferManage; private StreamBlobDatabaseBufferManager _mBufferManage;
private byte[] _mAllocatedBuffer; private byte[] _mAllocatedBuffer;
private Stream _mStream; private Stream _mStream;
private LinkedList<ListItem> _mBookmarks = new LinkedList<ListItem>(); private LinkedList<ListItem> _mBookmarks = new LinkedList<ListItem>();
private LinkedListNode<ListItem> _mHead, _mTail; private LinkedListNode<ListItem> _mHead, _mTail;
private long _mCapacity, _mSize; private long _mCapacity, _mSize;
public StreamBlobDatabase(bool onDisk, long capacity, Func<byte[], long, bool, byte[]> mBufferManage) public StreamBlobDatabase(bool onDisk, long capacity, StreamBlobDatabaseBufferManager mBufferManage)
{ {
_mBufferManage = mBufferManage; _mBufferManage = mBufferManage;
_mCapacity = capacity; _mCapacity = capacity;
@ -32,7 +32,7 @@ namespace BizHawk.Client.Common
} }
else else
{ {
_mAllocatedBuffer = _mBufferManage(null, capacity, true); _mAllocatedBuffer = _mBufferManage(null, ref capacity, true);
_mStream = new MemoryStream(_mAllocatedBuffer); _mStream = new MemoryStream(_mAllocatedBuffer);
} }
} }
@ -63,7 +63,8 @@ namespace BizHawk.Client.Common
_mStream = null; _mStream = null;
if (_mAllocatedBuffer != null) if (_mAllocatedBuffer != null)
{ {
_mBufferManage(_mAllocatedBuffer, 0, false); long capacity = 0;
_mBufferManage(_mAllocatedBuffer, ref capacity, false);
} }
} }
@ -264,7 +265,7 @@ namespace BizHawk.Client.Common
} }
} }
private static byte[] test_BufferManage(byte[] inbuf, long size, bool allocate) private static byte[] test_BufferManage(byte[] inbuf, ref long size, bool allocate)
{ {
if (allocate) if (allocate)
{ {
@ -305,4 +306,6 @@ namespace BizHawk.Client.Common
} }
} }
} }
public delegate byte[] StreamBlobDatabaseBufferManager(byte[] existingBuffer, ref long capacity, bool allocate);
} }