diff --git a/src/compile/bhl_front.csproj b/src/compile/bhl_front.csproj index 763593ae..8c25911a 100644 --- a/src/compile/bhl_front.csproj +++ b/src/compile/bhl_front.csproj @@ -25,5 +25,6 @@ + diff --git a/src/vm/bhl_runtime.csproj b/src/vm/bhl_runtime.csproj index 047201a9..a593866d 100644 --- a/src/vm/bhl_runtime.csproj +++ b/src/vm/bhl_runtime.csproj @@ -7,4 +7,8 @@ netstandard2.1 true + + + + diff --git a/src/vm/coro.cs b/src/vm/coro.cs index 82e40fa2..253339b1 100644 --- a/src/vm/coro.cs +++ b/src/vm/coro.cs @@ -22,7 +22,7 @@ public interface IInspectableCoroutine public abstract class Coroutine : ICoroutine { - internal VM.Pool pool; + internal Pool pool; public abstract void Tick(VM.Frame frm, VM.ExecState exec, ref BHS status); public virtual void Cleanup(VM.Frame frm, VM.ExecState exec) {} @@ -33,7 +33,7 @@ public class CoroutinePool //TODO: add debug inspection for concrete types static class PoolHolder where T : Coroutine { - //alternative implemenation + //TODO: alternative implemenation, check if it's simpler and faster? //[ThreadStatic] //static public VM.Pool _pool; //static public VM.Pool pool { @@ -44,10 +44,10 @@ static class PoolHolder where T : Coroutine // } //} - public static System.Threading.ThreadLocal> pool = - new System.Threading.ThreadLocal>(() => + public static System.Threading.ThreadLocal> pool = + new System.Threading.ThreadLocal>(() => { - return new VM.Pool(); + return new Pool(); }); } diff --git a/src/vm/extensions.cs b/src/vm/extensions.cs index eafbd542..bac332d6 100644 --- a/src/vm/extensions.cs +++ b/src/vm/extensions.cs @@ -42,6 +42,32 @@ static public VM.Fiber Start(this VM vm, FuncSymbolScript fs, StackList arg var addr = new VM.FuncAddr() { module = fs._module, fs = fs, ip = fs.ip_addr }; return vm.Start(addr, args.Count, args, opts); } + + static public void Alloc(this Pool pool, VM vm, int num) + { + for(int i=0;i pool) + { + string res = "=== Val POOL ===\n"; + res += "busy:" + pool.BusyCount + " idle:" + pool.IdleCount + "\n"; + + var dvs = new Val[pool.stack.Count]; + pool.stack.CopyTo(dvs, 0); + for(int i=dvs.Length;i-- > 0;) + { + var v = dvs[i]; + res += v + " (refs:" + v._refs + ") " + v.GetHashCode() + "\n"; + } + + return res; + } } } diff --git a/src/vm/util/blob.cs b/src/vm/util/blob.cs new file mode 100644 index 00000000..0b8f662f --- /dev/null +++ b/src/vm/util/blob.cs @@ -0,0 +1,92 @@ +using System; +using System.Buffers; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace bhl { + +public class Blob : IValRefcounted where T : unmanaged +{ + internal Pool> pool; + + //NOTE: -1 means it's in released state, + // public only for quick inspection + internal int _refs; + + public int refs => _refs; + + internal byte[] data; + + static int Size = Marshal.SizeOf(); + + static class PoolHolder where T1 : unmanaged + { + public static System.Threading.ThreadLocal>> pool = + new System.Threading.ThreadLocal>>(() => + { + return new Pool>(); + }); + } + + static public Blob New(ref T val) + { + var pool = PoolHolder.pool.Value; + + Blob blob = null; + if(pool.stack.Count == 0) + { + ++pool.miss; + blob = new Blob(); + } + else + { + ++pool.hits; + blob = pool.stack.Pop(); + } + + var data = ArrayPool.Shared.Rent(Size); + + ref var valRef = ref Unsafe.As(ref data[0]); + valRef = val; + + blob._refs = 1; + blob.data = data; + blob.pool = pool; + + return blob; + } + + public ref T Value => ref Unsafe.As(ref data[0]); + + public void Retain() + { + if(_refs == -1) + throw new Exception("Invalid state(-1)"); + ++_refs; + } + + public void Release() + { + if(_refs == -1) + throw new Exception("Invalid state(-1)"); + if(_refs == 0) + throw new Exception("Double free(0)"); + + --_refs; + if(_refs == 0) + Del(this); + } + + static void Del(Blob blob) + { + if(blob._refs != 0) + throw new Exception("Freeing invalid object, refs " + blob._refs); + + blob._refs = -1; + ArrayPool.Shared.Return(blob.data); + + blob.pool.stack.Push(blob); + } +} + +} diff --git a/src/vm/util/pool.cs b/src/vm/util/pool.cs new file mode 100644 index 00000000..a97b4420 --- /dev/null +++ b/src/vm/util/pool.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace bhl { + + public class Pool where T : class + { + internal Stack stack = new Stack(); + internal int hits; + internal int miss; + + public int HitCount { + get { return hits; } + } + + public int MissCount { + get { return miss; } + } + + public int IdleCount { + get { return stack.Count; } + } + + public int BusyCount { + get { return miss - IdleCount; } + } +} + +} diff --git a/src/vm/util/refc_list.cs b/src/vm/util/refc_list.cs index be38fee9..fe46af18 100644 --- a/src/vm/util/refc_list.cs +++ b/src/vm/util/refc_list.cs @@ -3,9 +3,9 @@ namespace bhl { - public class RefcList : List, IValRefcounted, IDisposable - { - internal VM.Pool> pool; +public class RefcList : List, IValRefcounted, IDisposable +{ + internal Pool> pool; //NOTE: -1 means it's in released state, // public only for quick inspection @@ -15,10 +15,10 @@ public class RefcList : List, IValRefcounted, IDisposable static class PoolHolder { - public static System.Threading.ThreadLocal>> pool = - new System.Threading.ThreadLocal>>(() => + public static System.Threading.ThreadLocal>> pool = + new System.Threading.ThreadLocal>>(() => { - return new VM.Pool>(); + return new Pool>(); }); } diff --git a/src/vm/util/val_list_adapter.cs b/src/vm/util/val_list_adapter.cs index b6007b64..c76b21c2 100644 --- a/src/vm/util/val_list_adapter.cs +++ b/src/vm/util/val_list_adapter.cs @@ -8,7 +8,7 @@ namespace bhl { //TODO: writing is not implemented public class ValList : IList, IValRefcounted, IDisposable { - internal VM.Pool> pool; + internal Pool> pool; Func val2native; @@ -26,10 +26,10 @@ public class ValList : IList, IValRefcounted, IDisposable static class PoolHolder { - public static System.Threading.ThreadLocal>> pool = - new System.Threading.ThreadLocal>>(() => + public static System.Threading.ThreadLocal>> pool = + new System.Threading.ThreadLocal>>(() => { - return new VM.Pool>(); + return new Pool>(); }); } diff --git a/src/vm/vm.pool.cs b/src/vm/vm.pool.cs index cfafbefa..0d5fcd6a 100644 --- a/src/vm/vm.pool.cs +++ b/src/vm/vm.pool.cs @@ -1,62 +1,9 @@ -using System.Collections.Generic; namespace bhl { public partial class VM : INamedResolver { - public class Pool where T : class - { - internal Stack stack = new Stack(); - internal int hits; - internal int miss; - - public int HitCount { - get { return hits; } - } - - public int MissCount { - get { return miss; } - } - - public int IdleCount { - get { return stack.Count; } - } - - public int BusyCount { - get { return miss - IdleCount; } - } - } - - public class ValPool : Pool - { - public void Alloc(VM vm, int num) - { - for(int i=0;i 0;) - { - var v = dvs[i]; - res += v + " (refs:" + v._refs + ") " + v.GetHashCode() + "\n"; - } - - return res; - } - } - - public ValPool vals_pool = new ValPool(); + public Pool vals_pool = new Pool(); public Pool vlsts_pool = new Pool(); public Pool vmaps_pool = new Pool(); public Pool frames_pool = new Pool(); diff --git a/tests/test_blobs.cs b/tests/test_blobs.cs new file mode 100644 index 00000000..105af191 --- /dev/null +++ b/tests/test_blobs.cs @@ -0,0 +1,32 @@ +using bhl; +using Xunit; + +public class TestBlobs : BHL_TestBase +{ + public struct StructBlob + { + public int x; + public int y; + public int z; + } + + [Fact] + public void TestBlob() + { + var val = new StructBlob(); + val.x = 1; + val.y = 10; + val.z = 100; + + var blob = Blob.New(ref val); + + AssertEqual(1, blob.Value.x); + AssertEqual(10, blob.Value.y); + AssertEqual(100, blob.Value.z); + + blob.Value.y = 30; + AssertEqual(30, blob.Value.y); + + blob.Release(); + } +}