More waterbox savestate stuff

This commit is contained in:
nattthebear 2017-05-20 20:55:55 -04:00
parent fa24f5fc9d
commit b1ff4fbff8
5 changed files with 106 additions and 56 deletions

View File

@ -16,7 +16,7 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Waterbox
{
public sealed class ElfRunner : Swappable, IImportResolver, IBinaryStateable, IDisposable
public sealed class ElfRunner : Swappable, IImportResolver, IBinaryStateable
{
// TODO: a lot of things only work with our elves and aren't fully generalized
@ -265,13 +265,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
_symlist = symbols.ToList();
}
public void Dispose()
{
// we don't need to activate to dispose
Dispose(true);
//GC.SuppressFinalize(this);
}
public void Seal()
{
Enter();
@ -285,13 +278,9 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
}
//~ElfRunner()
//{
// Dispose(false);
//}
private void Dispose(bool disposing)
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
foreach (var d in _disposeList)
@ -305,7 +294,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
}
#region clib monkeypatches
// our clib expects a few function pointers to be defined for it

View File

@ -34,6 +34,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
Memory = new MemoryBlock(start, size);
Used = 0;
Name = name;
Console.WriteLine("Created heap `{1}` at {0:x16}:{2:x16}", start, name, start + size);
}
private void EnsureAlignment(int align)
@ -118,7 +119,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
var hash = br.ReadBytes(_hash.Length);
if (!hash.SequenceEqual(_hash))
{
throw new InvalidOperationException(string.Format("Hash did not match for heap {0}. Is this the same rom?"));
throw new InvalidOperationException(string.Format("Hash did not match for heap {0}. Is this the same rom?", Name));
}
}
}

View File

@ -57,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
entryThunk();
}
public PeWrapper(string moduleName, byte[] fileData)
public PeWrapper(string moduleName, byte[] fileData, ulong destAddress)
{
ModuleName = moduleName;
_fileData = fileData;
@ -70,12 +70,13 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
_fileHash = WaterboxUtils.Hash(fileData);
Mount(destAddress);
}
/// <summary>
/// set memory protections, finishing the Mount process
/// set memory protections.
/// </summary>
public void FinishMount()
private void ProtectMemory()
{
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.R);
@ -103,7 +104,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// load the PE into memory
/// </summary>
/// <param name="org">start address</param>
public void Mount(ulong org)
private void Mount(ulong org)
{
Start = org;
LoadOffset = (long)Start - (long)_pe.ImageNtHeaders.OptionalHeader.ImageBase;
@ -159,6 +160,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
}
}
ProtectMemory();
// publish exports
EntryPoint = Z.US(Start + _pe.ImageNtHeaders.OptionalHeader.AddressOfEntryPoint);
@ -263,7 +265,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
WaterboxUtils.CopySome(br.BaseStream, ms, (long)length);
}
FinishMount();
ProtectMemory();
}
}
@ -507,6 +509,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
private Emu _emu;
private Syscalls _syscalls;
/// <summary>
/// timestamp of creation acts as a sort of "object id" in the savestate
/// </summary>
private readonly long _createstamp = WaterboxUtils.Timestamp();
public PeRunner(string directory, string filename, ulong heapsize, ulong sealedheapsize, ulong invisibleheapsize)
{
Initialize(_nextStart);
@ -530,8 +537,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
var moduleName = todoModules.Dequeue();
if (!_exports.ContainsKey(moduleName))
{
var module = new PeWrapper(moduleName, File.ReadAllBytes(Path.Combine(directory, moduleName)));
module.Mount(_nextStart);
var module = new PeWrapper(moduleName, File.ReadAllBytes(Path.Combine(directory, moduleName)), _nextStart);
ComputeNextStart(module.Size);
AddMemoryBlock(module.Memory);
_savestateComponents.Add(module);
@ -546,17 +552,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
}
foreach (var module in _modules)
{
foreach (var name in module.ImportsByModule.Keys)
{
module.ConnectImports(name, _exports[name]);
}
}
foreach (var module in _modules)
{
module.FinishMount();
}
ConnectAllImports();
// load all heaps
_heap = new Heap(_nextStart, heapsize, "brk-heap");
@ -618,11 +614,23 @@ namespace BizHawk.Emulation.Cores.Waterbox
}
}
private void ConnectAllImports()
{
foreach (var module in _modules)
{
foreach (var name in module.ImportsByModule.Keys)
{
module.ConnectImports(name, _exports[name]);
}
}
}
public void SaveStateBinary(BinaryWriter bw)
{
bw.Write(_createstamp);
bw.Write(_savestateComponents.Count);
using (this.EnterExit())
{
bw.Write(_savestateComponents.Count);
foreach (var c in _savestateComponents)
{
c.SaveStateBinary(bw);
@ -632,6 +640,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
public void LoadStateBinary(BinaryReader br)
{
var differentCore = br.ReadInt64() != _createstamp; // true if a different core instance created the state
if (br.ReadInt32() != _savestateComponents.Count)
throw new InvalidOperationException("Internal savestate error");
using (this.EnterExit())
@ -640,14 +649,20 @@ namespace BizHawk.Emulation.Cores.Waterbox
{
c.LoadStateBinary(br);
}
if (differentCore)
{
// if a different runtime instance than this one saved the state,
// Exvoker imports need to be reconnected
Console.WriteLine("Restoring PeRunner state from a different core...");
ConnectAllImports();
}
}
}
private bool _disposed = false;
public void Dispose()
protected override void Dispose(bool disposing)
{
if (!_disposed)
base.Dispose(disposing);
if (disposing)
{
foreach (var d in _disposeList)
d.Dispose();
@ -658,7 +673,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
_heap = null;
_sealedheap = null;
_invisibleheap = null;
_disposed = true;
}
}
}

View File

@ -12,13 +12,18 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// represents an object that can be swapped in and out of memory to compete with other objects in the same memory
/// not suited for general purpose stuff
/// </summary>
public abstract class Swappable : IMonitor
public abstract class Swappable : IMonitor, IDisposable
{
/// <summary>
/// start address, or 0 if we don't need to be swapped
/// </summary>
private ulong _lockkey = 0;
/// <summary>
/// the the relevant lockinfo for this core
/// </summary>
private LockInfo _currentLockInfo;
/// <summary>
/// everything to swap in for context switches
/// </summary>
@ -37,6 +42,10 @@ namespace BizHawk.Emulation.Cores.Waterbox
protected void Initialize(ulong lockkey)
{
_lockkey = lockkey;
if (lockkey != 0)
{
_currentLockInfo = LockInfos.GetOrAdd(_lockkey, new LockInfo { Sync = new object() });
}
}
/// <summary>
@ -50,30 +59,38 @@ namespace BizHawk.Emulation.Cores.Waterbox
private class LockInfo
{
public object Sync;
public Swappable Loaded;
private WeakReference LoadedRef = new WeakReference(null);
public Swappable Loaded
{
get
{
// if somehow an object died without being disposed,
// the MemoryBlock finalizer will have unloaded the memory
// and so we can treat it as if no Swappable was attached
return (Swappable)LoadedRef.Target;
}
set
{
LoadedRef.Target = value;
}
}
}
private static readonly ConcurrentDictionary<ulong, LockInfo> LockInfos = new ConcurrentDictionary<ulong, LockInfo>();
static Swappable()
{
LockInfos.GetOrAdd(0, new LockInfo()); // any errant attempt to lock when ShouldMonitor == false will result in NRE
}
/// <summary>
/// acquire lock and swap this into memory
/// </summary>
public void Enter()
{
var li = LockInfos.GetOrAdd(_lockkey, new LockInfo { Sync = new object() });
Monitor.Enter(li.Sync);
if (li.Loaded != this)
Monitor.Enter(_currentLockInfo.Sync);
if (_currentLockInfo.Loaded != this)
{
if (li.Loaded != null)
li.Loaded.DeactivateInternal();
li.Loaded = null;
if (_currentLockInfo.Loaded != null)
_currentLockInfo.Loaded.DeactivateInternal();
_currentLockInfo.Loaded = null;
ActivateInternal();
li.Loaded = this;
_currentLockInfo.Loaded = this;
}
}
@ -82,8 +99,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// </summary>
public void Exit()
{
var li = LockInfos.GetOrAdd(_lockkey, new LockInfo { Sync = new object() });
Monitor.Exit(li.Sync);
Monitor.Exit(_currentLockInfo.Sync);
}
private void DeactivateInternal()
@ -100,5 +116,31 @@ namespace BizHawk.Emulation.Cores.Waterbox
m.Activate();
}
private bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
lock (_currentLockInfo.Sync)
{
if (_currentLockInfo.Loaded == this)
{
DeactivateInternal();
_currentLockInfo.Loaded = null;
}
_currentLockInfo = null;
}
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
}

View File

@ -51,5 +51,10 @@ namespace BizHawk.Emulation.Cores.Waterbox
*p++ = 0;
}
}
public static long Timestamp()
{
return DateTime.UtcNow.Ticks;
}
}
}