Ver código fonte

Added methods to build a Table from an array.
Added support for preemptive coroutines.

Xanathar 10 anos atrás
pai
commit
726954c1ce

+ 1 - 1
README.md

@@ -1,4 +1,4 @@
-MoonSharp       [![Build Status](https://travis-ci.org/xanathar/moonsharp.svg?branch=master)](https://travis-ci.org/xanathar/moonsharp) [![Build Status](https://img.shields.io/nuget/v/MoonSharp.svg)](https://www.nuget.org/packages/MoonSharp/)   [![Join the chat at https://gitter.im/xanathar/moonsharp](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/xanathar/moonsharp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+MoonSharp       [![Build Status](https://travis-ci.org/xanathar/moonsharp.svg?branch=master)](https://travis-ci.org/xanathar/moonsharp) [![Build Status](https://img.shields.io/nuget/v/MoonSharp.svg)](https://www.nuget.org/packages/MoonSharp/)
 =========
 =========
 http://www.moonsharp.org   
 http://www.moonsharp.org   
 
 

+ 46 - 0
src/MoonSharp.Interpreter.Tests/EndToEnd/CoroutineTests.cs

@@ -238,6 +238,52 @@ checkresume(6, false, 'cannot resume dead coroutine');
 		}
 		}
 
 
 
 
+		[Test]
+		public void Coroutine_AutoYield()
+		{
+			string code = @"
+				function fib(n)
+					if (n == 0 or n == 1) then
+						return 1;
+					else
+						return fib(n - 1) + fib(n - 2);
+					end
+				end
+				";
+
+			// Load the code and get the returned function
+			Script script = new Script(CoreModules.None);
+			script.DoString(code);
+
+			// get the function
+			DynValue function = script.Globals.Get("fib");
+
+			// Create the coroutine in C#
+			DynValue coroutine = script.CreateCoroutine(function);
+
+			// Set the automatic yield counter every 10 instructions. 
+			// 10 is a too small! Use a much bigger value in your code to avoid interrupting too often!
+			coroutine.Coroutine.AutoYieldCounter = 10;
+
+			int cycles = 0;
+			DynValue result = null;
+
+			// Cycle until we get that the coroutine has returned something useful and not an automatic yield..
+			for (result = coroutine.Coroutine.Resume(8); 
+				result.Type == DataType.YieldRequest;
+				result = coroutine.Coroutine.Resume()) 
+			{
+				cycles += 1;
+			}
+
+			// Check the values of the operation
+			Assert.AreEqual(DataType.Number, result.Type);
+			Assert.AreEqual(34, result.Number);
+
+			// Check the autoyield actually triggered
+			Assert.Greater(cycles, 10);
+		}
+
 
 
 
 
 
 

+ 1 - 0
src/MoonSharp.Interpreter/CoreLib/CoroutineModule.cs

@@ -118,6 +118,7 @@ namespace MoonSharp.Interpreter.CoreLib
 						DynValue.NewString("normal");
 						DynValue.NewString("normal");
 				case CoroutineState.NotStarted:
 				case CoroutineState.NotStarted:
 				case CoroutineState.Suspended:
 				case CoroutineState.Suspended:
+				case CoroutineState.ForceSuspended:
 					return DynValue.NewString("suspended");
 					return DynValue.NewString("suspended");
 				case CoroutineState.Dead:
 				case CoroutineState.Dead:
 					return DynValue.NewString("dead");
 					return DynValue.NewString("dead");

+ 14 - 2
src/MoonSharp.Interpreter/DataTypes/Coroutine.cs

@@ -76,7 +76,7 @@ namespace MoonSharp.Interpreter
 			if (Type != CoroutineType.Coroutine)
 			if (Type != CoroutineType.Coroutine)
 				throw new InvalidOperationException("Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead");
 				throw new InvalidOperationException("Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead");
 
 
-			while (this.State == CoroutineState.NotStarted || this.State == CoroutineState.Suspended)
+			while (this.State == CoroutineState.NotStarted || this.State == CoroutineState.Suspended || this.State == CoroutineState.ForceSuspended)
 				yield return Resume();
 				yield return Resume();
 		}
 		}
 
 
@@ -241,7 +241,7 @@ namespace MoonSharp.Interpreter
 					return CoroutineState.NotStarted;
 					return CoroutineState.NotStarted;
 				else if (Type == CoroutineType.ClrCallbackDead)
 				else if (Type == CoroutineType.ClrCallbackDead)
 					return CoroutineState.Dead;
 					return CoroutineState.Dead;
-				else
+				else 
 					return m_Processor.State;
 					return m_Processor.State;
 			}
 			}
 		}
 		}
@@ -275,5 +275,17 @@ namespace MoonSharp.Interpreter
 			get;
 			get;
 			private set;
 			private set;
 		}
 		}
+
+		/// <summary>
+		/// Gets or sets the automatic yield counter.
+		/// </summary>
+		/// <value>
+		/// The automatic yield counter.
+		/// </value>
+		public long AutoYieldCounter
+		{
+			get { return m_Processor.AutoYieldCounter; }
+			set { m_Processor.AutoYieldCounter = value; }
+		}
 	}
 	}
 }
 }

+ 4 - 0
src/MoonSharp.Interpreter/DataTypes/CoroutineState.cs

@@ -24,6 +24,10 @@ namespace MoonSharp.Interpreter
 		/// </summary>
 		/// </summary>
 		Suspended,
 		Suspended,
 		/// <summary>
 		/// <summary>
+		/// Coroutine has been forcefully suspended (i.e. auto-yielded)
+		/// </summary>
+		ForceSuspended,
+		/// <summary>
 		/// Coroutine is running
 		/// Coroutine is running
 		/// </summary>
 		/// </summary>
 		Running,
 		Running,

+ 23 - 0
src/MoonSharp.Interpreter/DataTypes/DynValue.cs

@@ -221,6 +221,7 @@ namespace MoonSharp.Interpreter
 			};
 			};
 		}
 		}
 
 
+
 		/// <summary>
 		/// <summary>
 		/// Creates a new writable value initialized to an empty table.
 		/// Creates a new writable value initialized to an empty table.
 		/// </summary>
 		/// </summary>
@@ -229,6 +230,14 @@ namespace MoonSharp.Interpreter
 			return NewTable(new Table(script));
 			return NewTable(new Table(script));
 		}
 		}
 
 
+		/// <summary>
+		/// Creates a new writable value initialized to with array contents.
+		/// </summary>
+		public static DynValue NewTable(Script script, params DynValue[] arrayValues)
+		{
+			return NewTable(new Table(script, arrayValues));
+		}
+
 		/// <summary>
 		/// <summary>
 		/// Creates a new request for a tail call. This is the preferred way to execute Lua/MoonSharp code from a callback,
 		/// Creates a new request for a tail call. This is the preferred way to execute Lua/MoonSharp code from a callback,
 		/// although it's not always possible to use it. When a function (callback or script closure) returns a
 		/// although it's not always possible to use it. When a function (callback or script closure) returns a
@@ -286,6 +295,20 @@ namespace MoonSharp.Interpreter
 			};
 			};
 		}
 		}
 
 
+		/// <summary>
+		/// Creates a new request for a yield of the current coroutine.
+		/// </summary>
+		/// <param name="args">The yield argumenst.</param>
+		/// <returns></returns>
+		internal static DynValue NewForcedYieldReq()
+		{
+			return new DynValue()
+			{
+				m_Object = new YieldRequest() { Forced = true },
+				m_Type = DataType.YieldRequest,
+			};
+		}
+
 		/// <summary>
 		/// <summary>
 		/// Creates a new tuple initialized to the specified values.
 		/// Creates a new tuple initialized to the specified values.
 		/// </summary>
 		/// </summary>

+ 13 - 0
src/MoonSharp.Interpreter/DataTypes/Table.cs

@@ -35,6 +35,19 @@ namespace MoonSharp.Interpreter
 			m_Owner = owner;
 			m_Owner = owner;
 		}
 		}
 
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Table"/> class.
+		/// </summary>
+		/// <param name="owner">The owner.</param>
+		/// <param name="arrayValues">The values for the "array-like" part of the table.</param>
+		public Table(Script owner, params DynValue[] arrayValues)
+			: this(owner)
+		{
+			for (int i = 0; i < arrayValues.Length; i++)
+			{
+				this.Set(DynValue.NewNumber(i + 1), arrayValues[i]);
+			}
+		}
 
 
 		/// <summary>
 		/// <summary>
 		/// Gets the script owning this resource.
 		/// Gets the script owning this resource.

+ 5 - 0
src/MoonSharp.Interpreter/DataTypes/YieldRequest.cs

@@ -14,5 +14,10 @@ namespace MoonSharp.Interpreter
 		/// The return values of the coroutine
 		/// The return values of the coroutine
 		/// </summary>
 		/// </summary>
 		public DynValue[] ReturnValues;
 		public DynValue[] ReturnValues;
+
+		/// <summary>
+		/// Gets or sets a value indicating whether this <see cref="YieldRequest"/> is a forced yield.
+		/// </summary>
+		public bool Forced { get; internal set; }
 	}
 	}
 }
 }

+ 19 - 4
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Coroutines.cs

@@ -35,26 +35,41 @@ namespace MoonSharp.Interpreter.Execution.VM
 			{
 			{
 				int entrypoint = 0;
 				int entrypoint = 0;
 
 
-				if (m_State != CoroutineState.NotStarted && m_State != CoroutineState.Suspended)
+				if (m_State != CoroutineState.NotStarted && m_State != CoroutineState.Suspended && m_State != CoroutineState.ForceSuspended)
 					throw ScriptRuntimeException.CannotResumeNotSuspended(m_State);
 					throw ScriptRuntimeException.CannotResumeNotSuspended(m_State);
 
 
 				if (m_State == CoroutineState.NotStarted)
 				if (m_State == CoroutineState.NotStarted)
 				{
 				{
 					entrypoint = PushClrToScriptStackFrame(CallStackItemFlags.ResumeEntryPoint, null, args);
 					entrypoint = PushClrToScriptStackFrame(CallStackItemFlags.ResumeEntryPoint, null, args);
 				}
 				}
-				else
+				else if (m_State == CoroutineState.Suspended)
 				{
 				{
 					m_ValueStack.Push(DynValue.NewTuple(args));
 					m_ValueStack.Push(DynValue.NewTuple(args));
 					entrypoint = m_SavedInstructionPtr;
 					entrypoint = m_SavedInstructionPtr;
 				}
 				}
+				else if (m_State == CoroutineState.ForceSuspended)
+				{
+					if (args != null && args.Length > 0)
+						throw new ArgumentException("When resuming a force-suspended coroutine, args must be empty.");
+
+					entrypoint = m_SavedInstructionPtr;
+				}
 
 
 				m_State = CoroutineState.Running;
 				m_State = CoroutineState.Running;
 				DynValue retVal = Processing_Loop(entrypoint);
 				DynValue retVal = Processing_Loop(entrypoint);
 
 
 				if (retVal.Type == DataType.YieldRequest)
 				if (retVal.Type == DataType.YieldRequest)
 				{
 				{
-					m_State = CoroutineState.Suspended;
-					return DynValue.NewTuple(retVal.YieldRequest.ReturnValues);
+					if (retVal.YieldRequest.Forced)
+					{
+						m_State = CoroutineState.ForceSuspended;
+						return retVal;
+					}
+					else
+					{
+						m_State = CoroutineState.Suspended;
+						return DynValue.NewTuple(retVal.YieldRequest.ReturnValues);
+					}
 				}
 				}
 				else
 				else
 				{
 				{

+ 15 - 2
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs

@@ -12,10 +12,15 @@ namespace MoonSharp.Interpreter.Execution.VM
 	{
 	{
 		const int YIELD_SPECIAL_TRAP = -99;
 		const int YIELD_SPECIAL_TRAP = -99;
 
 
+		internal long AutoYieldCounter = 0;
+
 		private DynValue Processing_Loop(int instructionPtr)
 		private DynValue Processing_Loop(int instructionPtr)
 		{
 		{
-		// This is the main loop of the processor, has a weird control flow and needs to be as fast as possible.
-		// This sentence is just a convoluted way to say "don't complain about gotos".
+			// This is the main loop of the processor, has a weird control flow and needs to be as fast as possible.
+			// This sentence is just a convoluted way to say "don't complain about gotos".
+
+			long executedInstructions = 0;
+			bool canAutoYield = (AutoYieldCounter > 0) && m_CanYield && (this.State != CoroutineState.Main);
 
 
 			repeat_execution:
 			repeat_execution:
 
 
@@ -30,6 +35,14 @@ namespace MoonSharp.Interpreter.Execution.VM
 						ListenDebugger(i, instructionPtr);
 						ListenDebugger(i, instructionPtr);
 					}
 					}
 
 
+					++executedInstructions;
+
+					if (canAutoYield && executedInstructions > AutoYieldCounter)
+					{
+						m_SavedInstructionPtr = instructionPtr;
+						return DynValue.NewForcedYieldReq();
+					}
+
 					++instructionPtr;
 					++instructionPtr;
 
 
 					switch (i.OpCode)
 					switch (i.OpCode)

+ 0 - 1
src/MoonSharp.Interpreter/ScriptOptions.cs

@@ -96,6 +96,5 @@ namespace MoonSharp.Interpreter
 		/// </summary>
 		/// </summary>
 		public bool CheckThreadAccess { get; set; }
 		public bool CheckThreadAccess { get; set; }
 
 
-
 	}
 	}
 }
 }