none
How to stop, pause or kill a long running script?

    Question

  • Here is script that I want to stop, pause, or kill while the script is running.

    for (int i = 0; i < 1000; i++)
    {

        DoSomething();
        Sleep(25);
    }


    • Edited by Joseph Pham Friday, January 13, 2012 10:45 PM
    Friday, January 13, 2012 10:45 PM

Answers

  • Hi Joseph - In order to cancel / pause some code thats running you would have to run this code on a separate thread. If you are calling ScriptEngine.Execute() inside a separate thread / if you are spinning off a new thread to run some operation from within your script, then it is possible to cancel this operation by using some cancellation mechanism like below (i.e. by sharing some cancellation state between your script and the host program and using this shared cancellation state to signal when the script / thread should stop).

    using System.Threading;
    using System.Threading.Tasks;
    using Roslyn.Compilers;
    using Roslyn.Scripting.CSharp;

    namespace Host
    {
        public class HostObject
        {
            public CancellationTokenSource CancellationTokenSource;
        }
    }

    class Program
    {
        static void Main()
        {
            var hostObject = new Host.HostObject { CancellationTokenSource = new CancellationTokenSource() };

            var hostReference =
                new AssemblyFileReference(typeof(Host.HostObject).Assembly.Location);

            // Create SciptEngine and initialize it such that script code can access the "HostObject" type.
            var engine = new ScriptEngine(
                references: new[] { hostReference },
                importedNamespaces: new[] { "System", "System.Threading", "Host" });

            // In this example, I start a new thread and run my (cancellable) script inside this thread.
            // Alternately, you could also write code inside the script to execute the below for loop
            // on a separate thread.
            Task t = Task.Factory.StartNew(() =>
            {
                engine.Execute(@"
    for (int i = 0; i < 10000; i++)
    {
        // State from within the HostObject (i.e. the CancellationTokenSource field)
        // can be referenced from within the script!
        if (!CancellationTokenSource.IsCancellationRequested)
        {
            // In this loop we do some task and then sleep for 100 ms before doing the same task again...
            // If a cancellation is not received then this loop should execute 10000 times.
            
            // DoSomething();
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
        else
        {
            Console.WriteLine(""Received Cancellation"");
            break;
        }
    }", hostObject); // Pass HostObject to ScriptEngine.Execute() so that the cancellation
                     // state is shared between the script and the host program.
            });

            Thread.Sleep(1000); // Allow the for loop in the script thread to run for a few
                                // iterations by sleeping for 1000 ms.
            hostObject.CancellationTokenSource.Cancel(); // Now cancel the script thread.
            t.Wait(); // Wait for the script thread to exit gracefully.
        }
    }

    Normally, the for loop in the script code should run 10000 times. But because we signal the cancellation earlier it only ends up running for around 10 iterations...

    Is this what you were looking for? If not could you please elaborate a bit more on your scenario as svick requested above?


    Shyam Namboodiripad | Software Development Engineer in Test | Roslyn Compilers Team
    Saturday, January 14, 2012 12:39 AM
  • Hi, Joseph.  If you look at the roslyn-scripting-spec.doc I posted (link in the first pinned post of this forum), you can see roughly what we're planning for this.  We have to make c-c (or c-break) work in the REPL anyway too :-). 

    For now though, looks like Shyam has a work around.  Of course, the more common scenario is the script is not cooperative and needs to be hit on the head :-), which the APIs will provide for as long as you're executing in script code (that is, we won't interrupt the thread when it is running in .NET library code that was not compiled through the Scripting APIs).

    Cheers,

    Bill

    Monday, January 16, 2012 6:02 PM

All replies

  • How are you running the "script"? Using ScriptEngine.Execute()? Why do you want to stop, pause or kill it? And which one of those three is it actually? Could you elabore more on what exactly are you trying to do? What have you tried so far?
    Friday, January 13, 2012 11:55 PM
  • Hi Joseph - In order to cancel / pause some code thats running you would have to run this code on a separate thread. If you are calling ScriptEngine.Execute() inside a separate thread / if you are spinning off a new thread to run some operation from within your script, then it is possible to cancel this operation by using some cancellation mechanism like below (i.e. by sharing some cancellation state between your script and the host program and using this shared cancellation state to signal when the script / thread should stop).

    using System.Threading;
    using System.Threading.Tasks;
    using Roslyn.Compilers;
    using Roslyn.Scripting.CSharp;

    namespace Host
    {
        public class HostObject
        {
            public CancellationTokenSource CancellationTokenSource;
        }
    }

    class Program
    {
        static void Main()
        {
            var hostObject = new Host.HostObject { CancellationTokenSource = new CancellationTokenSource() };

            var hostReference =
                new AssemblyFileReference(typeof(Host.HostObject).Assembly.Location);

            // Create SciptEngine and initialize it such that script code can access the "HostObject" type.
            var engine = new ScriptEngine(
                references: new[] { hostReference },
                importedNamespaces: new[] { "System", "System.Threading", "Host" });

            // In this example, I start a new thread and run my (cancellable) script inside this thread.
            // Alternately, you could also write code inside the script to execute the below for loop
            // on a separate thread.
            Task t = Task.Factory.StartNew(() =>
            {
                engine.Execute(@"
    for (int i = 0; i < 10000; i++)
    {
        // State from within the HostObject (i.e. the CancellationTokenSource field)
        // can be referenced from within the script!
        if (!CancellationTokenSource.IsCancellationRequested)
        {
            // In this loop we do some task and then sleep for 100 ms before doing the same task again...
            // If a cancellation is not received then this loop should execute 10000 times.
            
            // DoSomething();
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
        else
        {
            Console.WriteLine(""Received Cancellation"");
            break;
        }
    }", hostObject); // Pass HostObject to ScriptEngine.Execute() so that the cancellation
                     // state is shared between the script and the host program.
            });

            Thread.Sleep(1000); // Allow the for loop in the script thread to run for a few
                                // iterations by sleeping for 1000 ms.
            hostObject.CancellationTokenSource.Cancel(); // Now cancel the script thread.
            t.Wait(); // Wait for the script thread to exit gracefully.
        }
    }

    Normally, the for loop in the script code should run 10000 times. But because we signal the cancellation earlier it only ends up running for around 10 iterations...

    Is this what you were looking for? If not could you please elaborate a bit more on your scenario as svick requested above?


    Shyam Namboodiripad | Software Development Engineer in Test | Roslyn Compilers Team
    Saturday, January 14, 2012 12:39 AM
  • Thank you!  I will try it out.  I got this sample from http://www.codeproject.com/KB/cs/csharpforscripting.aspx

    I ran the code from a WinForm application. 

            public void AddToList(string text)
            {
                listBox1.Items.Add(text);
            }
            
           
            public OurDog CreateDog(string name)
            {
                return new OurDog(name);
            }
    
            public MainWin CreateMainWin()
            {
                return this;
            }
            
            //Pass the code to the engine, nothing much here
            public object Execute(string code)
            {        
                return engine.Execute(code, session);                     
            }
    
            public T Execute<T>(string code)
            {
                return engine.Execute<T>(code, session);
            }
    
            public void Sleep(int millisecondsTimeout)
            {
                int doEventInterval = 100;
                if (millisecondsTimeout > doEventInterval)
                {
                    int counter = millisecondsTimeout / doEventInterval;
                    for (int i = 0; i < counter; i++)
                    {
                        Thread.Sleep(doEventInterval);
                        Application.DoEvents();
                    }
                    millisecondsTimeout = millisecondsTimeout % doEventInterval;
                    Thread.Sleep(millisecondsTimeout);
                    Application.DoEvents();
                }
                else
                {
                    Thread.Sleep(millisecondsTimeout);
                    Application.DoEvents();
                }
            }
    
           
            private void button2_Click(object sender, EventArgs e)
            {
                listBox1.Items.Clear();            
            }
    
    
    

    Saturday, January 14, 2012 12:58 AM
  • Hi, Joseph.  If you look at the roslyn-scripting-spec.doc I posted (link in the first pinned post of this forum), you can see roughly what we're planning for this.  We have to make c-c (or c-break) work in the REPL anyway too :-). 

    For now though, looks like Shyam has a work around.  Of course, the more common scenario is the script is not cooperative and needs to be hit on the head :-), which the APIs will provide for as long as you're executing in script code (that is, we won't interrupt the thread when it is running in .NET library code that was not compiled through the Scripting APIs).

    Cheers,

    Bill

    Monday, January 16, 2012 6:02 PM