Rewind - if memory allocation fails, back off the size until successful.
This commit is contained in:
parent
9f6c0ca695
commit
bcb6b20ec2
|
@ -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 = ¤tState[0])
|
fixed (byte* pCurrentState = ¤tState[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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue