Register to post in forums, or Log in to your existing account
 

Play RetroMUD
Post new topic  Reply to topic     Home » Forums » CMUD General Discussion
Liath
Novice


Joined: 25 Aug 2009
Posts: 38

PostPosted: Sat Sep 19, 2009 8:39 am   

[Suggestion] Completely OFFLINE package editing mode / Infinite Loop detection?
 
Today, I had made a really really stupid mistake when I edited a script, and had a #WHILE whose counter ended up with an endless loop.

Wouldn't have been a problem if I could have edited the script to stop it, but as soon as cMUD opened the session, whether offline or online, it would spike 99% CPU, and was unusable. After a few retries, I noticed spam from a recently edited script that was stuck in an infinite loop. cMUD could really use [better?] infinite loop detection to prevent this. Not sure if there even is infinite loop detection, but if there is, it could use a second look. I can supply my package file if you need to look at how i ended up with a loop.

I had to manually open the package database with SQLite Browser 1.1 (3 wont open old 2.5 sqlite databases which cMUD uses) and find the offending loop, and comment it out.
If I didn't figure out via notepad that the package was even *in* database format, or if I wasn't nerdy enough to think about opening it in the first place.... I might have been a very unhappy person, since I'd have had to completely remove the account and start over.

So, suggestion would be a 'safe' offline mode for disaster recovery :P
Reply with quote
Tech
GURU


Joined: 18 Oct 2000
Posts: 2733
Location: Atlanta, USA

PostPosted: Sat Sep 19, 2009 7:26 pm   
 
CMUD does have infinite loop detection, but as I'm sure you can imagine it can be a difficult thing to detect.

You can have also pressed the Esc key to abort the currently executing thread or try to click on the gun icon to disable triggers. There is a way to open your session offline. Just right click on the session and choose Offline. Alternatively you could select the session and type Ctrl-O.
_________________
Asati di tempari!
Reply with quote
Liath
Novice


Joined: 25 Aug 2009
Posts: 38

PostPosted: Sat Sep 19, 2009 8:47 pm   Re: [Suggestion] Completely OFFLINE package editing mode / Infinite Loop detection?
 
Since it seems that I wasn't clear, I quoted myself with the important bits that were missed in bold.

Liath wrote:
... but as soon as cMUD opened the session, whether offline or online, it would spike 99% CPU, and was unusable. After a few retries, I noticed spam from a recently edited script that was stuck in an infinite loop.
....
I had to manually open the package database with SQLite Browser 1.1 (3 wont open old 2.5 sqlite databases which cMUD uses) and find the offending loop, and comment it out.

If I didn't figure out via notepad that the package was even *in* database format, or if I wasn't nerdy enough to think about opening it in the first place.... I might have been a very unhappy person, since I'd have had to completely remove the account and start over.

So, suggestion would be a 'safe' offline mode for disaster recovery :P


I know about offline mode. What I was referring to is a way to open the package without the scripting engine processing anything. Like a 'safe' offline mode.
Reply with quote
Tech
GURU


Joined: 18 Oct 2000
Posts: 2733
Location: Atlanta, USA

PostPosted: Sun Sep 20, 2009 2:09 am   
 
I would consider that disabling the triggers. I'm curious as to what exactly you have occurring on session open that will cause an infinite loop.

That aside, other than my daft recommendation about opening offline the suggested remedies still stand.

Yet another option would be to remove the offending package, disable trigger processing and re-add the package.
_________________
Asati di tempari!
Reply with quote
ReedN
Wizard


Joined: 04 Jan 2006
Posts: 1279
Location: Portland, Oregon

PostPosted: Mon Sep 21, 2009 3:39 pm   
 
Tech, pressing ESC doesn't work unless you executed it from the command line. If it is in your package and executed via a trigger of some type then ESC doesn't exactly nothing.

Also, when in an infinite loop there is no way to click on the gun because the whole interface is locked up due to the loop. If there was a separation of threads of some sort between the executing scripts and the gui then perhaps clicking on the gun would work in these situations, but it has never worked when I get in a loop.

This is something that I've posted about several times in the past and remains a sore spot with me. Yes I understand this may be difficult to do, yes I understand that there already is a measure of loop detection built in. But I think there is a great deal of room for improvement yet.

At a minimum I would want some way for the gui thread to be independent from the script execution thread. Without this minimum separation lockups will always require killing Cmud through the task manager as the gui will be by default locked up as well.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Mon Sep 21, 2009 4:26 pm   
 
An infinite loop such as "#WHILE (1) {#NOOP}" will hang an application in *ANY* programming language. The problem is that Windows is a event-driven operating system. When you are in an infinite loop, Windows doesn't have any way to check it's message queue to process keystrokes or mouse events.

For example, in Delphi, when doing a really long loop, we learn that you need to do this:
Code:
while (true) do begin
  ...do stuff here...
  Application.ProcessMessages; // allow Windows to look at it's message queue
end

Without the call to ProcessMessages, Windows never processes the message queue during the loop. In CMUD, there isn't any way to call "Application.ProcessMessages" in your loop.

Now, if your script runs in a background thread, then it won't hang the main application interface like this. But CMUD doesn't create a separate thread for *every* script. The overhead for that would be huge and CMUD would be slow. CMUD only creates a new background thread for a script when it detects that you are using commands that require threads, such as various #WAIT events.

For example, the following script would use a thread because of the #WAIT command:
Code:
#WHILE (1) {#WAIT 0}

If you put this infinite loop in an alias and execute the alias, then you'll notice that the command line still works and CMUD is not hung. However, if you remove the #WAIT command and just use something like #NOOP, then CMUD will be hung because CMUD is not using a new thread in that case.

So the GUI thread *IS* independent from the script execution thread when it needs to be (when threading commands are used). Otherwise it uses the main application thread. Always using a background thread would make CMUD much slower.

Detecting an infinite loop like the above #WHILE loops is not feasible. Again, just like in other programming languages. If I write a Delphi application like: "while (true) do begin end;" then it's going to hang my application just like an infinite loop in CMUD. This is just something you need to learn when writing any sort of program in any scripting language or programming language. You need to pay attention to your loops and avoid making infinite loops. It's no different then when programming in PHP, or C++, or Java, or any other language.

Anything the programming language tries to do to "hold your hand" will slow down the application for everyone else. And adding checks to every #WHILE and other loop that will slow down everyone elses script is not something I'm willing to do.
Reply with quote
ReedN
Wizard


Joined: 04 Jan 2006
Posts: 1279
Location: Portland, Oregon

PostPosted: Mon Sep 21, 2009 5:00 pm   
 
Why can't all user scripts all use one particular thread (unless they create an additional thread) and the main program use a different thread? Would this still incur performance penalties since it isn't continuously creating new threads? I'm thinking of a compartmentalization between the user code and Cmud code. User code uses one thread for its operation and the cmud program uses a different one. I just don't see why this isn't possible. We see this done all the time. When Cmud locks up I don't need to hit the reset button on my computer. Somehow they are able to compartmentalize Cmud code from the Windows code so I can shut down Cmud when it locks up. I don't see why this case is so different.

Referring to your example, I'd point out that I can write a program that locks up, but I can always type ctrl-c on the command line (assuming it was run from a command shell) and it terminates the program. It doesn't lock up the command shell and it doesn't lock up Windows when this happens.
Reply with quote
Liath
Novice


Joined: 25 Aug 2009
Posts: 38

PostPosted: Tue Sep 22, 2009 4:17 pm   
 
I don't know how great programmers are on sharing snippets of code, but there is a game that I usually play that has excellent loop detection in its script engine. Egosoft (X-series) Of course, the language is different, and z/cMUD are trigger-driven, where X is more event-driven, so it might not even be feasible... but I figured I'd see if you'd want to fire off a message and see if they're willing to share :P

The game uses scripts to control basically every thing in the game. There are thousands of ships/stations at any given moment, and each ship/station has at least one script that is controlling it, for trade, fighting, general flight, etc. Bigger ships or player-owned ships can get worse, with turret-control scripts, group scripts, etc. etc.

The language is a little odd mixture of BASIC and C. It will generally detect and shut-down a script and display "Infinite loop detected!" if you have a block like this:
while x:
blah;
end

without a "dec x;" in there.

For whole (large) scripts, or many nested blocks, you have to put "infinite loop detection= [TRUE]" for it to detect an infinite loop, though. I think what the engine does is keep tabs on small blocks or sections of code, and the "infinite loop detection" line flags the script so that the engine pays a little more attention to it and makes the pool of code it monitors larger.

I know that something like this would require a boatload of code, probably a complete re-write, so I know it most likely will not be in cMUD, but it might be something to check out, and see if it's feasible to put in a future product.

Might be worth contacting Egosoft and figuring out how they do it, and if it is even do-able in this environment.


Last edited by Liath on Tue Sep 22, 2009 5:07 pm; edited 1 time in total
Reply with quote
ReedN
Wizard


Joined: 04 Jan 2006
Posts: 1279
Location: Portland, Oregon

PostPosted: Tue Sep 22, 2009 4:30 pm   
 
Zugg wrote:

For example, in Delphi, when doing a really long loop, we learn that you need to do this:
Code:
while (true) do begin
  ...do stuff here...
  Application.ProcessMessages; // allow Windows to look at it's message queue
end

Without the call to ProcessMessages, Windows never processes the message queue during the loop. In CMUD, there isn't any way to call "Application.ProcessMessages" in your loop.

As an alternate idea, perhaps an equivalent 'Application.ProcessMessages' for Cmud would be useful as well. We could insert the equivalent statement in our loops if we think there is the possibility they might affect the main application. This would also help alleviate the Cmud unresponsiveness I get when my scripts are executing a large amount. Or if I just by default allowed Cmud the opportunity to process after every trigger/alias and within every #while/loop, it seems possible to theoretically eliminate even the possibility of locking up Cmud.

But I still think isolating the main program and the user scripts from one another would be the most ideal situation. There is no limit to what might occur in the user's scripts and all it takes is one mistake and then the whole Cmud program goes down because when you take down your own scripts you also take down Cmud, so there is no chance to disable or fix the problem without restarting. Isolating them would allow Cmud to still function and give you the opportunity to fix the problem. Additionally, separating Cmud from the user scripts thread should speed up operation on a multi-core machine. Allowing the separation of these two processes should provide a nice speed boost for both as they should be able to take advantage of more hardware.

I was thinking about this a bit more and I was wondering if the threading features just added allow us to put our alias/trigger/etc into a particular thread? If I put #wait at the beginning of a script it starts a new thread for it, but is it possible to tell Cmud to run it in a particular thread? If I were able to put something like "#thread 2" at the beginning of all my scripts and have it run all my scripts in the thread #2, it would allow me to separate my scripts from the main Cmud processing thread, and it would largely allow me to avoid multiple access issues with variables that are shared between my different scripts. There might be a few buttons for which I'd need to setup semaphores, but it shouldn't be too bad.
Reply with quote
Liath
Novice


Joined: 25 Aug 2009
Posts: 38

PostPosted: Tue Sep 22, 2009 5:18 pm   
 
Wow... scrap pretty much the whole post I made earlier... ReedN's #thread idea makes more sense and wouldn't require all of the work...

To expand on ReedN's post:
Perhaps get all userland code to run in it's own thread, then have an option for the triggers/alias' that reads something like:

Sub-Thread: (drop-down) [1, 2, 3, etc] (Default=1 - the main script thread)

That could keep it a little simpler, and not require every script to have the "#thread x" command.

And for those that do heavy code and need variables to be accessed at different times, etc, maybe an option for classes that makes every item under the class use the classes' thread as default?
Reply with quote
ReedN
Wizard


Joined: 04 Jan 2006
Posts: 1279
Location: Portland, Oregon

PostPosted: Tue Sep 22, 2009 5:42 pm   
 
I really like your idea of having a drop down selector for the thread number, that'd be pretty slick. I guess the critical question is whether Cmud can set the script to run on a particular thread or not.

----------------------------------------------------

At a crude level, I wonder if this would work?

#THREAD "abc" {#WAITFOR "abc";def}
Creates a thread named "abc" with the commands listed

So, at the beginning of each of my scripts I'd have:

#thread "scripts" {

My Script

}

Would Cmud handle this properly since I'm not creating the thread each time? After the first creation, I'd want it to use the existing thread named 'scripts' not to create a new thread with that name.

--------------------------

Following up on the alternate idea, I was also wonder about this command for suspending the current thread (to allow Cmud itself to execute):

Syntax: #WAITSIG signal timeout {match-commands} {timeout-commands}

Suspends the current thread and wait until the specified signal is fired. If timeout is specified, then it is the maximum number of milliseconds to wait. If the timeout expires, the rest of the thread's script is aborted.

I don't think #WAITSIG would work for this purpose. If I suspended the main thread that wouldn't allow Cmud to execute because it's part of the thread I just suspended. Additionally, I wonder how it would resume unless I had at minimum a second thread running that would signal the main thread to resume.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Tue Sep 22, 2009 6:07 pm   
 
You don't need a Application.ProcessMessages. Just put "#WAIT 0" within your loop and then it won't hang the main thread. Problem solved!

The performance issue involved with threads is that most commands require access to the user interface, and that can *only* be done via the main thread. So having background threads requires synchronization with the main UI thread. If the command line *always* used a background thread, then performance would drop a lot because it would always need to synchronize with the main thread.

Threading is a very complex topic. It has taken a long time to get the threading in CMUD to be stable and fast. I have no intention on changing it at this point just because someone accidentally created an infinite loop.

There is no way to "attach" to an existing thread like you suggested. Threads just don't work like that.

Just put the #WAIT command in your loop if it is going to take a long time to execute. That is the supported way to handle this issue.
Reply with quote
Liath
Novice


Joined: 25 Aug 2009
Posts: 38

PostPosted: Wed Sep 23, 2009 8:18 am   
 
Oh yeah, lovign the discussion so far, but as far as my OP... Zugg, do you think adding some kind of "safe mode" would be feasible, until something like reedn's ideas can be looked into??

Kinda like offline mode is now, but with a 'safe' option of disabling the script engine *before* it loads/executes triggers and such?

Basically I found that the previous script that had blown up cMUD had a faulty "#while" where I goofed.:

eval @position = 5 (standing)
while (%numitems(@spell_list) >0) {
use alias to cast last item in @spell_list
delete last item of @spell_list <-- I pooched up this line
}


And to top it off it was in an expression trigger... so it could (and did) fire before I even was logged into the mud :(

I'm finding expression triggers useful since I can't really run a script/CPU-friendly version of a never-ending subroutine o.0
Reply with quote
Rahab
Wizard


Joined: 22 Mar 2007
Posts: 2320

PostPosted: Wed Sep 23, 2009 4:12 pm   
 
Liath, all you have to do is start your session in offline mode, then toggle triggers off. An easy way to do that is to click on the gun icon in the commandline toolbar.
Reply with quote
Liath
Novice


Joined: 25 Aug 2009
Posts: 38

PostPosted: Wed Sep 23, 2009 5:22 pm   
 
There is the problem. If you have something that comes up true as soon as you connect, then you will never hit that trigger icon... cause cMUD will be locked up as soon as you open the session.

I know how to turn off the scripting engine. Hence the idea//feature request to start with.
You can click on the trigger icon all day. but if the cMUD is cpu-choked, it will not work.

Try it some time :P

Make an eval trigger that is true as soon as the session is open
make a variable, set it to 1
make a eval trigger variable = 1
which fires an alias
loops through a string list
which calls an oninput trigger
loop ends... but make a stupid mistake that makes the loop never end.

close and open session

have fun trying to turn off triggers... or any thing else for that matter.... generally if a program doesn't even respond to the close window button... you can hang up on getting it to do anything else. Hope you know how to get to taskman and force-quit the app, cause that is what you'll have to do.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Wed Sep 23, 2009 8:46 pm   
 
Another way:

1) Run CMUD
2) Close the Session window
3) Turn off triggers
4) Load the package file via: #LOAD packagefilename.pkg or via the File/Open menu in the Settings Editor
5) Make the change to the package file to prevent the infinite loop

In other words, you turn off triggers *before* you load the package that is causing problems. That should work.
Reply with quote
Liath
Novice


Joined: 25 Aug 2009
Posts: 38

PostPosted: Thu Sep 24, 2009 5:39 am   
 
OOOooooh The great Poombah has the solution :D
That works! yay.
Could ya perhaps in some future version/update put in a nifty neat little check-box that does that for those retarded unthinking people like me that overlook simple solutions like that?
Laughing
Reply with quote
Rahab
Wizard


Joined: 22 Mar 2007
Posts: 2320

PostPosted: Thu Sep 24, 2009 6:45 pm   
 
I see. I misunderstood you--you said it loops as soon as you connect, and I was telling you to turn off triggers before you connect. What you meant was it loops as soon as it the session gets loaded, which is not the same thing.
Reply with quote
Display posts from previous:   
Post new topic   Reply to topic     Home » Forums » CMUD General Discussion All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

© 2009 Zugg Software. Hosted by Wolfpaw.net