1

I am using a .Net JavaScript implementation called Jurassic to run user-controlled scripts within my .Net 4 WPF application coded in VB.Net - C# answers are fine. The script engine runs on its own thread and provides an API for the script to interact with my application. This all works really nicely until a user executes a script that causes an infinite loop and takes out a core of the processor.

The method that runs within the thread looks something like this but with more irrelevant bits:

Sub run()   
    Do While True
        Try
            Do Until queue.Count = 0
                Dim functionName As String = queue(queue.Count - 1)
                queue.RemoveAt(queue.Count - 1)
                scriptEngine.CallGlobalFunction(functionName)
            Loop
            thread.Sleep(Timeout.Infinite)
        Catch ex As ThreadInterruptedException
            ' Wake and loop
        Catch ex As Exception
            ' Log
        End Try
    Loop
End Sub

I have a monitoring class on another thread in place that detects script engine threads that are using a lot of resources by taking note of when they wake and sleep and flagging up any that run for too long.

Once a thread has been flagged up by the monitoring class I'm a bit stuck. Currently I explain the situation to the user and give them the option of terminating the thread. I do this using Thread.Abort(). However I would much rather allow the thread to keep running but somehow prevent it from using so much processor time.

The problem here is that I cannot run any code on the thread once it is in this state as the infinite loop is occurring within CallGlobalFunction() method within the Jurassic code, so I cannot just add a Thread.Yield() into the loop. I have briefly considered hacking my way through the Jurassic code to see if there is any way I can but that would be very tough and quite possibly impossible.

The only ways I have been able to interact with the thread so far are using Thread.Abort() as stated above and the deprecated Thread.Suspend().

So is there any way to yield a thread while it is executing a method or am I just going to have to kill it?

Thanks for any help and I hope this makes sense,
Sam.

Extra Info:
The reason I care about keeping the thread alive is because the user who wrote the script and the user who is running it may not be the same, and I want to keep the experience as smooth as possible the the user running the thread. There also might be legitimate situations in which a single JavaScript function would run for a long time and I do not want to kill that, I just want to stop being allowed to hog the resources.

Solutions that involve stopping the thread from slowing down the system but that still show high CPU usage are not preferable because I do not want the user to wrongly feel that the application is resource intensive.

SeriousSamP
  • 436
  • 3
  • 18
  • Could you do it in a BackgroundWorker with WorkerSupportsCancellation? But I don't think you can set priority with BackgroundWorker. – paparazzo Jun 25 '12 at 20:13
  • It seems like your problem is mostly intractable; there's a reason that modern browsers present the choice of either force-killing scripts they think have gone infinite, or else allowing them to continue. I would think that would be fine for your users as well: tell them something seems to be wrong, and let them decide what to do. The actual solution is to provide an easy way for users to alert the script author that their script might be buggy, and then ensuring that updates can easily flow through. Not ideal, I know, but why slow down a script that is likely broken? Just kill it. – dlev Jun 25 '12 at 21:00
  • Sorry, I missed these comments somehow. Blam: Thanks, but this won't work because the cancelAsync method on a backgroundWorker doesn't force anything to exit, it only sets a boolean property which the method then needs to check and I cannot control the method. dlev: If I cannot work something else out then I will likely use this method, however I like the idea of being able to react to a potential problem very quickly (within 500 milliseconds or so) and throttle it down then give in a chance to behave itself. I don't want to terminate things unless I'm sure there's a problem. – SeriousSamP Jun 25 '12 at 21:29

1 Answers1

1

I think you should set the thread priority to Lowest. That way it will not take away resources from the system.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Thanks for the answer, however the thread priority already is lowest and although it does still make the resources available to the rest of the system it keeps my CPU usage at 100% once I have as many instances as cores and then the fan revs up and the power usage rockets. This is not an acceptable situation for the end user. – SeriousSamP Jun 25 '12 at 20:03
  • You can put your process into a Windows Job object and set maximum CPU usage percent. This involves unmanaged APIs. – usr Jun 25 '12 at 20:06
  • 1
    I've never heard of that before and I've read in various places that it is not possible to limit CPU usage by percent so this sounds interesting. I will read up now, thanks. – SeriousSamP Jun 25 '12 at 20:09
  • 1
    http://msdn.microsoft.com/en-us/library/windows/desktop/hh448384(v=vs.85).aspx Looks like CpuRate can support it. – usr Jun 25 '12 at 20:13
  • As great as that would be it is Windows 8 and up which is not an option for me. I'm currently trying to make sense of http://msdn.microsoft.com/en-us/library/windows/desktop/ms684147%28v=vs.85%29.aspx to see if that can do what I need... – SeriousSamP Jun 25 '12 at 20:47
  • I did not notice the dependency on Windows 8. I guess you can build throttling yourself by quickly supending and resuming the thread. Although I don't know if that will be enough to spin down the fan. It might be the case that your goal is unachievable. – usr Jun 25 '12 at 20:56
  • I have already experimented previously with suspending then resuming and it seems not to work but I will try again in case I missed something. I fear you may be right about this not being possible... Thanks for your help anyway. – SeriousSamP Jun 25 '12 at 22:04
  • I have succeeded in reducing the CPU usage a little using a basic Suspend(), Resume(), Sleep(1) loop down from 25% (1 core) to a point where it fluctuates between 17% and 25% which is an improvement but not enough to be a solution. – SeriousSamP Jun 25 '12 at 22:25
  • I guess you could schedule more granularly so your timing gets more accurate. Try while (1) { toggle(); sleep(50); } which should give you 50% throttling. – usr Jun 25 '12 at 23:35