diff --git a/BizHawk.Emulation.Cores/ElfRunner.cs b/BizHawk.Emulation.Cores/ElfRunner.cs
index 21305db42b..0f6f1dfaa8 100644
--- a/BizHawk.Emulation.Cores/ElfRunner.cs
+++ b/BizHawk.Emulation.Cores/ElfRunner.cs
@@ -10,10 +10,12 @@ using System.Reflection;
using BizHawk.Common;
using System.Security.Cryptography;
using System.IO;
+using System.Collections.Concurrent;
+using System.Threading;
namespace BizHawk.Emulation.Cores
{
- public sealed class ElfRunner : IImportResolver, IDisposable
+ public sealed class ElfRunner : IImportResolver, IDisposable, IMonitor
{
// TODO: a lot of things only work with our elves and aren't fully generalized
@@ -39,12 +41,25 @@ namespace BizHawk.Emulation.Cores
///
private Heap _invisibleheap;
+ ///
+ /// _base.Start, or 0 if we were relocated and so don't need to be swapped
+ ///
+ private ulong _lockkey;
+
private long _loadoffset;
private Dictionary> _symdict;
private List> _symlist;
+ ///
+ /// everything to clean up at dispose time
+ ///
private List _disposeList = new List();
+ ///
+ /// everything to swap in for context switches
+ ///
+ private List _memoryBlocks = new List();
+
private ulong GetHeapStart(ulong prevend)
{
// if relocatable, we won't have constant pointers, so put the heap anywhere
@@ -72,16 +87,21 @@ namespace BizHawk.Emulation.Cores
{
_base = new MemoryBlock((ulong)(orig_end - orig_start));
_loadoffset = (long)_base.Start - orig_start;
+ _lockkey = 0;
}
else
{
- _base = new MemoryBlock((ulong)orig_start, (ulong)(orig_end - orig_start));
+ _lockkey = (ulong)orig_start;
+ _base = new MemoryBlock(_lockkey, (ulong)(orig_end - orig_start));
_loadoffset = 0;
+ Enter();
}
try
{
_disposeList.Add(_base);
+ _memoryBlocks.Add(_base);
+ _base.Activate();
_base.Protect(_base.Start, _base.Size, MemoryBlock.Protection.RW);
foreach (var seg in loadsegs)
@@ -92,7 +112,6 @@ namespace BizHawk.Emulation.Cores
RegisterSymbols();
ProcessRelocations();
-
_base.Protect(_base.Start, _base.Size, MemoryBlock.Protection.R);
foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Allocatable) != 0))
@@ -108,25 +127,30 @@ namespace BizHawk.Emulation.Cores
if (heapsize > 0)
{
_heap = new Heap(GetHeapStart(end), (ulong)heapsize, "sbrk-heap");
+ _heap.Memory.Activate();
end = _heap.Memory.End;
_disposeList.Add(_heap);
+ _memoryBlocks.Add(_heap.Memory);
}
if (sealedheapsize > 0)
{
_sealedheap = new Heap(GetHeapStart(end), (ulong)sealedheapsize, "sealed-heap");
+ _sealedheap.Memory.Activate();
end = _sealedheap.Memory.End;
_disposeList.Add(_sealedheap);
+ _memoryBlocks.Add(_sealedheap.Memory);
}
if (invisibleheapsize > 0)
{
_invisibleheap = new Heap(GetHeapStart(end), (ulong)invisibleheapsize, "invisible-heap");
+ _invisibleheap.Memory.Activate();
end = _invisibleheap.Memory.End;
_disposeList.Add(_invisibleheap);
+ _memoryBlocks.Add(_invisibleheap.Memory);
}
- //FixupGOT();
ConnectAllClibPatches();
Console.WriteLine("Loaded {0}@{1:X16}", filename, _base.Start);
foreach (var sec in _elf.Sections.Where(s => s.LoadAddress != 0))
@@ -134,16 +158,20 @@ namespace BizHawk.Emulation.Cores
Console.WriteLine(" {0}@{1:X16}, size {2}", sec.Name.PadLeft(20), sec.LoadAddress + _loadoffset, sec.Size.ToString().PadLeft(12));
}
- TopSavableSymbols();
+ PrintTopSavableSymbols();
}
catch
{
Dispose();
throw;
}
+ finally
+ {
+ Exit();
+ }
}
- private void TopSavableSymbols()
+ private void PrintTopSavableSymbols()
{
Console.WriteLine("Top savestate symbols:");
foreach (var text in _symlist
@@ -248,13 +276,22 @@ namespace BizHawk.Emulation.Cores
public void Dispose()
{
+ // we don't need to activate to dispose
Dispose(true);
//GC.SuppressFinalize(this);
}
public void Seal()
{
- _sealedheap.Seal();
+ Enter();
+ try
+ {
+ _sealedheap.Seal();
+ }
+ finally
+ {
+ Exit();
+ }
}
//~ElfRunner()
@@ -269,6 +306,11 @@ namespace BizHawk.Emulation.Cores
foreach (var d in _disposeList)
d.Dispose();
_disposeList.Clear();
+ _memoryBlocks.Clear();
+ _base = null;
+ _heap = null;
+ _sealedheap = null;
+ _invisibleheap = null;
}
}
@@ -383,57 +425,136 @@ namespace BizHawk.Emulation.Cores
}
}
+ ///
+ /// true if the IMonitor should be used for native calls
+ ///
+ public bool ShouldMonitor { get { return _lockkey != 0; } }
+
+ // any ElfRunner is assumed to conflict with any other ElfRunner at the same base address,
+ // but not any other starting address. so don't put them too close together!
+
+ private class LockInfo
+ {
+ public object Sync;
+ public ElfRunner Loaded;
+ }
+
+ private static readonly ConcurrentDictionary LockInfos = new ConcurrentDictionary();
+
+ static ElfRunner()
+ {
+ LockInfos.GetOrAdd(0, new LockInfo()); // any errant attempt to lock when ShouldMonitor == false will result in NRE
+ }
+
+ ///
+ /// acquire lock and swap this into memory
+ ///
+ public void Enter()
+ {
+ var li = LockInfos.GetOrAdd(_lockkey, new LockInfo { Sync = new object() });
+ Monitor.Enter(li.Sync);
+ if (li.Loaded != this)
+ {
+ if (li.Loaded != null)
+ li.Loaded.DeactivateInternal();
+ li.Loaded = null;
+ ActivateInternal();
+ li.Loaded = this;
+ }
+ }
+
+ ///
+ /// release lock
+ ///
+ public void Exit()
+ {
+ var li = LockInfos.GetOrAdd(_lockkey, new LockInfo { Sync = new object() });
+ Monitor.Exit(li.Sync);
+ }
+
+ private void DeactivateInternal()
+ {
+ Console.WriteLine("ElfRunner DeactivateInternal {0}", GetHashCode());
+ foreach (var m in _memoryBlocks)
+ m.Deactivate();
+ }
+
+ private void ActivateInternal()
+ {
+ Console.WriteLine("ElfRunner ActivateInternal {0}", GetHashCode());
+ foreach (var m in _memoryBlocks)
+ m.Activate();
+ }
+
#region state
const ulong MAGIC = 0xb00b1e5b00b1e569;
public void SaveStateBinary(BinaryWriter bw)
{
- bw.Write(MAGIC);
- bw.Write(_elfhash);
- bw.Write(_loadoffset);
- foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Writable) != 0))
+ Enter();
+ try
{
- var ms = _base.GetStream((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, false);
- bw.Write(sec.Size);
- ms.CopyTo(bw.BaseStream);
- }
+ bw.Write(MAGIC);
+ bw.Write(_elfhash);
+ bw.Write(_loadoffset);
+ foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Writable) != 0))
+ {
+ var ms = _base.GetStream((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, false);
+ bw.Write(sec.Size);
+ ms.CopyTo(bw.BaseStream);
+ }
- if (_heap != null) _heap.SaveStateBinary(bw);
- if (_sealedheap != null) _sealedheap.SaveStateBinary(bw);
- bw.Write(MAGIC);
+ if (_heap != null) _heap.SaveStateBinary(bw);
+ if (_sealedheap != null) _sealedheap.SaveStateBinary(bw);
+ bw.Write(MAGIC);
+ }
+ finally
+ {
+ Exit();
+ }
}
public void LoadStateBinary(BinaryReader br)
{
- if (br.ReadUInt64() != MAGIC)
- throw new InvalidOperationException("Magic not magic enough!");
- if (!br.ReadBytes(_elfhash.Length).SequenceEqual(_elfhash))
- throw new InvalidOperationException("Elf changed disguise!");
- if (br.ReadInt64() != _loadoffset)
- throw new InvalidOperationException("Trickys elves moved on you!");
-
- foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Writable) != 0))
+ Enter();
+ try
{
- var len = br.ReadInt64();
- if (sec.Size != len)
- throw new InvalidOperationException("Unexpected section size for " + sec.Name);
- var ms = _base.GetStream((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, true);
- CopySome(br.BaseStream, ms, len);
+ if (br.ReadUInt64() != MAGIC)
+ throw new InvalidOperationException("Magic not magic enough!");
+ if (!br.ReadBytes(_elfhash.Length).SequenceEqual(_elfhash))
+ throw new InvalidOperationException("Elf changed disguise!");
+ if (br.ReadInt64() != _loadoffset)
+ throw new InvalidOperationException("Trickys elves moved on you!");
+
+ foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Writable) != 0))
+ {
+ var len = br.ReadInt64();
+ if (sec.Size != len)
+ throw new InvalidOperationException("Unexpected section size for " + sec.Name);
+ var ms = _base.GetStream((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, true);
+ CopySome(br.BaseStream, ms, len);
+ }
+
+ if (_heap != null) _heap.LoadStateBinary(br);
+ if (_sealedheap != null) _sealedheap.LoadStateBinary(br);
+ if (br.ReadUInt64() != MAGIC)
+ throw new InvalidOperationException("Magic not magic enough!");
+
+ // the syscall trampolines were overwritten in loadstate (they're in .bss), and if we're cross-session,
+ // are no longer valid. cores must similiarly resend any external pointers they gave the core.
+ ConnectAllClibPatches();
+ }
+ finally
+ {
+ Exit();
}
-
- if (_heap != null) _heap.LoadStateBinary(br);
- if (_sealedheap != null) _sealedheap.LoadStateBinary(br);
- if (br.ReadUInt64() != MAGIC)
- throw new InvalidOperationException("Magic not magic enough!");
-
- // the syscall trampolines were overwritten in loadstate (they're in .bss), and if we're cross-session,
- // are no longer valid. cores must similiarly resend any external pointers they gave the core.
- ConnectAllClibPatches();
}
#endregion
+ #region utils
+
private static void CopySome(Stream src, Stream dst, long len)
{
var buff = new byte[4096];
@@ -531,11 +652,6 @@ namespace BizHawk.Emulation.Cores
return ret;
}
- //public Stream GetStream(bool writer)
- //{
- // return Memory.GetStream(Memory.Start, Used, writer);
- //}
-
public void Seal()
{
if (!Sealed)
@@ -600,5 +716,7 @@ namespace BizHawk.Emulation.Cores
}
}
}
+
+ #endregion
}
}
diff --git a/BizHawk.Emulation.Cores/MemoryBlock.cs b/BizHawk.Emulation.Cores/MemoryBlock.cs
index 4c9ab5dc0a..46d7262ce4 100644
--- a/BizHawk.Emulation.Cores/MemoryBlock.cs
+++ b/BizHawk.Emulation.Cores/MemoryBlock.cs
@@ -171,7 +171,6 @@ namespace BizHawk.Emulation.Cores
End = start + size;
Size = size;
_pageData = new Protection[GetPage(End - 1) + 1];
- Activate();
}
///