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

Play RetroMUD
Post new topic  Reply to topic     Home » Forums » CMUD Beta Forum Goto page 1, 2  Next
Malach
Apprentice


Joined: 03 Nov 2007
Posts: 132

PostPosted: Sat Nov 10, 2007 6:38 pm   

[2.11] #VAR creating a duplicate variable, causing crashes
 
Okay so I have had trouble duplicating this bug but basically what happens is that during the course of a system running, a duplicate of a variable that already exists is created. This then causes cmud to pop up an error at me which then leads to my #RETURN no longer being a recognized command. After noticing this happening quite frequently in my system, I did the following test with a clean install and fresh package:

#TRIGGER {<>-} {#ALARM +2 {#WHILE @WhileVar {#VAR TestVar 1}}}
#VAR TestVar 1
#VAR WhileVar 1

I have had this happen in multiple situations, including when I would just use alarm to test it as well as with my normal system but I'm using an infinite while loop like this because it causes it to happen faster in testing. It is not the only situation that it happens. I had to do a couple of times for this to happen (escaping out of the thread since it's a loop) but I got these lines in the debugger once it did happen:

0.0013 | variable | [56] variabletest Comline : start : #say <>-
0.0014 | variable | Pattern: <>-
0.0021 | variable | Trigger "_Alarm43" compiled (Pattern) : +2
0.0017 | variable | [56] variabletest Comline : stopped
0.8635 | variable | Alarm: +2
0.0028 | variable | Trigger "_Alarm28" compiled (Normal) : #WHILE 0.0031 | @WhileVar {#VAR TestVar 1}
0.0022 | variable | [57] variabletest Trigger : start : Alarm "_Alarm28" : 0.0025 | #WHILE @WhileVar {#VAR TestVar 1}
0.0021 | variable | [57] variabletest Trigger : running
0.1657 | variable | Alarm: +2
0.0018 | variable | Trigger "_Alarm29" compiled (Normal) : #WHILE 0.0020 | @WhileVar {#VAR TestVar 1}
0.0015 | variable | [58] variabletest Trigger : start : Alarm "_Alarm29" 0.0017 | : #WHILE @WhileVar {#VAR TestVar 1}
0.0059 | variable | [58] variabletest Trigger : running
0.0204 | variable | [58] Var "TestVar" changed from "" to "1"
0.0022 | variable | [57] Var "TestVar" changed from "" to "1"


After this there were two TestVar variables and I got an error to pop by trying to access @TestVar. Errors also seem to pop up when I delete a duplicate. Generally it is an access violation or pointer violation. This also seems to be the cause of the errors I would get while searching, when there were duplicate variables.

I have always used #VAR to update my existing variables without (visible) problem in zmud and 1.34 and it worked without incident. I am guessing with the threading activity this is no longer the case? In any event when I change the test trigger to the following:

#TRIGGER {<>-} {#ALARM +2 {#WHILE @WhileVar {TestVar=1}}}

I cannot get the problem to ever fire. So I'm thinking the problem is with using #VAR to update variables. But I'm still reporting it as a bug as the help file for NEWVAR states the following:

"Normally the #VAR command will search for existing variables and modify their value rather than creating a new variable."

So I am thinking that my understanding of how #VAR is supposed to work is correct in that it is supposed to create a new variable if one doesn't exist and update the existing one if it does.

I also have no real idea why exactly this would corrupt CMUD to the point where it had to be reinstalled in order to get #RETURN to be recognized but it has happened so many times to me on so many clean installs with fresh packages and this is the only common denominator in terms of this is what is throwing my errors and it's after those errors that the #RETURN isn't recognized.

Anyway, I hope someone else can verify this.
Reply with quote
Zugg
MASTER


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

PostPosted: Sat Nov 10, 2007 6:55 pm   
 
You'll notice that TestVar was first changed by thread [58], which is your Alarm29 and then it is changed again by thread [57] which is Alarm28. Since both alarms are running at the same time, this could be one of those thread issues where you have multiple threads accessing the same resource (@TestVar) at the same time. So you might want to change your alarms to this:

#WHILE @WhileVar {#SECTION TestVarCrit {#VAR TestVar 1}}

so that the access to TestVar is protected by #SECTION and then each thread will take turns and not run at the same time. In other word, the first alarm looks for the variable and doesn't find it, so it gets ready to create the variable. In the meantime, the second alarm runs at the same time and also doesn't find the variable, so it gets ready to create it. Then both threads create the variable...so now you have a corrupted duplicate.
Reply with quote
Malach
Apprentice


Joined: 03 Nov 2007
Posts: 132

PostPosted: Sat Nov 10, 2007 7:01 pm   
 
Would this also happen even if the variable already existed? In the lines above the TestVar variable was already in existence and then got created twice again by the trigger.
_________________
Intel Core2 Quad CPU @ 2.4 GHZ with Windows Vista Home Premium and 2 GB Ram
Reply with quote
Malach
Apprentice


Joined: 03 Nov 2007
Posts: 132

PostPosted: Sat Nov 10, 2007 7:33 pm   
 
Okay I read up on #SECTION and I think I'm understanding correctly. Basically if I'm using some sort of command where several threads might be created and running in parallel that will be trying to access the same global variables (whether to read or write) a #section should be used around whatever commands are doing that accessing. That makes sense.

My question is the help file indicates things such as #WAIT, #WAITFOR, etc. that would likely cause these sorts of resource conflicts. I don't use any of these commands but I I do use a lot of alarms. I am assuming this from what I've read on the forum but alarm threads will run in parallel of one another? What if an alarm calls up a function or an alias that in turn accesses some global resources that another alarm might be accessing? Would that alias that was called up operate on its own thread or the original alarm thread that called it?

I'm just trying to determine where I need my sections. Just in my alarms or also in the aliases/functions my alarms might be calling up?
_________________
Intel Core2 Quad CPU @ 2.4 GHZ with Windows Vista Home Premium and 2 GB Ram
Reply with quote
Zugg
MASTER


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

PostPosted: Sun Nov 11, 2007 4:56 am   
 
Yes, Alarms also create parallel threads. If your alarms calls an alias that access global variables that might be used in another thread at the same time, then put the alias call into a #section within your alarm. Anything that you call from your alarm will run in the alarm's thread. The only time you get a new thread is when a trigger or alarm is executed, or when you enter something on the command line (or press a macro key or click a button).

In general, the #section should go at the lowest possible level and should only be put around fast code. So it would be best to put the #section in the alias that actually accesses the variable. But if you are using someone else's alias, then you can put the #section in your alarm.

Just be careful that you *never* have nested sections. For example, never have Alias A with Section A call Alias B with section B if there is any way for Alias A to get called from within Alias B. Yeah, that sounded confusing. Just try not to use too many sections and keep it as simple as possible. If you get a script waiting for a section, and that section is never released because it's already waiting on something else, then you'll get a complete deadlock and not even pressing ESC will fix it.
Reply with quote
Malach
Apprentice


Joined: 03 Nov 2007
Posts: 132

PostPosted: Sun Nov 11, 2007 1:29 pm   
 
So in theory if I do not use alarms that access any global resources at all and any #WAIT, #WAITFOR, etc. that I do does not have any access to global resources following it, I should never enounter this problem?
_________________
Intel Core2 Quad CPU @ 2.4 GHZ with Windows Vista Home Premium and 2 GB Ram
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Nov 11, 2007 3:13 pm   
 
Ok, so you have a variable. Health.

You have many many triggers, aliases, macros, functions that READ that variable, But you also have multiple things that are WRITING it too.

Create a Function that does a section to set or retrieve the variable.

#FUNCTION Health($val) {#SECTION HealthVarCrit {#IF (!%null($val)) {Health=$val};#Return {@Health}}}

Replace the ordinary use of @Health with @Health()

Replace the ordinary use of health=value with #call @Health(value)

Now your variable should be thread safe...

I THINK what Zugg meant by global value he simply meant that any variable that is used by many different things. Normally health wouldn't need this sort of protection because most folks only set their health from their prompt and don't do many calculations on it. If on the other hand you had for example an alarm that is also setting a value you run into more of a chance of having a thread conflict.

The above simply covers the issue that started this thread. There is STILL a possibility that your threads will run out of order because this method is at SUCH a low level that it is only protecting the variable itself.

Don't go and alter your scripts based on the above example and expect them to be suddenly thread safe they will not be.
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Nov 11, 2007 4:43 pm   
 
Malach wrote:
So in theory if I do not use alarms that access any global resources at all and any #WAIT, #WAITFOR, etc. that I do does not have any access to global resources following it, I should never enounter this problem?


Zugg wrote:
The only time you get a new thread is when a trigger or alarm is executed, or when you enter something on the command line (or press a macro key or click a button).


In other words you get new threads when YOU do something. Triggers and Alarms count as you doing something too, presumably because you made them.

If you have multiple threads that are trying to do something to the same thing. Alter the same variable for example. There can be conflicts.

My little example above stops you getting the multiple variables created at the same time. It does NOT stop thread A from reading the value that thread B wrote AFTER thread A made its change and thus thread A is reading a value that it did NOT expect to be there.

Usually this shouldn't be a problem. Most of my triggers are setting a variable, substituting/coloring text, performing a single action. And they are done so quickly that thread A is done executing before thread B is even started.

But, the more code you have in your script the longer it takes to execute. If there is a chance that another thread will change the value of your variables before your current thread is finished then you may want to place the portion of your script that relies upon those variables NOT being changed within a #section. Be warned however that if your #section relies on ANOTHER section you could find yourself in a section lock.

The example I gave you above works inside another section nicely however.
Code:
#FUNCTION Health($val) {#SECTION HealthVarCrit {#IF (!%null($val)) {Health=$val};#Return {@Health}}}
#Alias test {#section test {#show {@health(0)};#while (@health()<200) {#show {@health((@health()+1))}}}}
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Malach
Apprentice


Joined: 03 Nov 2007
Posts: 132

PostPosted: Sun Nov 11, 2007 4:51 pm   
 
...this is a nightmare. What if I have a trigger that calls an alias or a function. Would the script inside that alias or function be considered part of that trigger's thread?
_________________
Intel Core2 Quad CPU @ 2.4 GHZ with Windows Vista Home Premium and 2 GB Ram
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Nov 11, 2007 5:11 pm   
 
Now we slightly alter the Original example and show the difference.
Enter the things in the code boxes onto the command line. The quotes are the results.
Code:
#TRIGGER {<>-} {#ALARM +2 {#WHILE @WhileVar {#call @health(1)}}}
#VAR Health 1
#VAR WhileVar 1


And trigger it.

Code:
#show {<>-}
#thread

Quote:
Threads:
# ID Window Name Status Script
-------------------------------------------------------------------------------------------------
1 untitled running #thread
2 untitled running Alarm "_Alarm0" : #WHILE @WhileVar {#...

Code:
#VAR WhileVar 0
#thread

Quote:
Threads:
# ID Window Name Status Script
-------------------------------------------------------------------------------------------------
1 [u] untitled running #thread
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Nov 11, 2007 5:16 pm   
 
Unless you stipulated otherwise yes.
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Nov 11, 2007 5:25 pm   
 
We are talking extremes here though. USUALLY when a trigger/alias/macro/function is called from within another trigger/alias/etc then the thread that called them is used to execute everything within the script block.

Said another way. Everything in your trigger script executes as if it were in Cmudv1.34 meaning in a single thread. UNLESS you tell it to do otherwise. But UNLIKE v1.34 there is the possibility that other threads could be changing your data before the current thread is finished running. So SOMETIMES you need to use #section to protect your thread.

USUALLY you do not need to worry about this.
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Malach
Apprentice


Joined: 03 Nov 2007
Posts: 132

PostPosted: Sun Nov 11, 2007 5:48 pm   
 
I'd say usually I do not need to worry about this except that for the kind of scripts I'm writing, I very much do have to worry about it as has been made clear by the problems I've been having.

I think I'll be alright if I have all my global variable setting and reading done through protected functions and for expression triggers I'll just have to hope they execute quickly I suppose since there aren't a lot of attractive options for making sure an expression is trigger is making only protected calls to a global variable.

An example of an expression trigger that would be doing this is one that is checking to see if current health is less than some limit. I have an ATCP trigger that sets my current health. Well I might get an ATCP trigger (with its own thread) writing to current health at the same time my expression trigger (with its own thread) is calling up the value for current health. In order to make sure there aren't conflicts there I would basically have to disable the ATCP trigger until the expression trigger thread is finished and then re-enable it.
_________________
Intel Core2 Quad CPU @ 2.4 GHZ with Windows Vista Home Premium and 2 GB Ram
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Nov 11, 2007 5:58 pm   
 
Ok, WHAT am I talking about when I say threads altering each other?

Presumebly you still have the untitled session open where I had you enter all of the above the commands. If not please recreate the health function, the test alias and the <>- trigger that I wrote that is slightly altered.

In the command line type #VAR WhileVar 1;#show {<>-};#thread and hit enter.

Note that the While is running in the background.

Now type test and hit enter.

The screen will spit out a LOT of numbers most of them being 2.

Hitting the escape key will stop the madness but don't do that.
Notice that you are still able to type in the command line as well.

type #VAR WhileVar 0;#thread

The test alias is now allowed to finish. If you scroll up you will see that there were three threads running.
Type #thread again.

Note that there is once again only one thread.
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Sun Nov 11, 2007 5:59 pm   
 
I swear alarms were changed to use the main thread at some point - it seems like every time someone asks this, the answer's changed.

I'm going to go ahead and create a Changes for zMUD users page for multi-thread commands and add a warning to the #alarm page. If it turns out that I'm wrong and alarms don't use multiple threads, they're easily removed.
_________________
Rorso's syntax colouriser.

- Happy bunny is happy! (1/25)
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Nov 11, 2007 6:06 pm   
 
In your above example it is not necessary to place a call to your protected function to READ the value of your health variable in your expression trigger.
Just to write to it. Also remember that I said MOST triggers that are for getting and setting values execute very quickly and triggers can be told to execute in order.

So make certain that your expression trigger has a higher priority number than your ATCP trigger does, and that your ATCP trigger makes the secure function call to make the WRITE.
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Malach
Apprentice


Joined: 03 Nov 2007
Posts: 132

PostPosted: Sun Nov 11, 2007 6:11 pm   
 
Alright I am officially confused. It has been said that accessing the same global resources in parallel threads can lead to corrupted global variables. Now is it that accessing at all (read/write) can cause this corruption or *only* writing. If it's only when writing that's much easier to work with than if it's both reading and writing.
_________________
Intel Core2 Quad CPU @ 2.4 GHZ with Windows Vista Home Premium and 2 GB Ram
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Nov 11, 2007 6:23 pm   
 
You aren't going to corrupt your data when just READING your variables.

It is possible to make certain that your variable is read from the correct place or created in the correct place inside of a protected function. {I didn't do that in the above example.}
AND your variable is more likely to have the data from the correct order in the stack.

But you will NOT corrupt the variable by reading from it outside of the protected function.

Not to mention that expression triggers don't work with a user defined function as the value. {Unless you did this @health(@health) it wouldn't work anyway!!}
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Malach
Apprentice


Joined: 03 Nov 2007
Posts: 132

PostPosted: Sun Nov 11, 2007 6:37 pm   
 
Right, I know expression triggers don't work with user functions. Thanks. My confusion was in thinking that reading or writing to a global variable in parallel threads could corrupt them.
_________________
Intel Core2 Quad CPU @ 2.4 GHZ with Windows Vista Home Premium and 2 GB Ram
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Nov 11, 2007 7:51 pm   
 
It appears that Fang was correct and that #Alarms WERE/ARE supposed to run in the main thread.

SOMETHING about this particular set of circumstances creates the alarm in the tread owned by the trigger. {Remember my comment about things within the script of a trigger using the same thread AS the trigger?}

For an example of how they are SUPPOSED to work type this into the command line.

#alarm +3 {#say One Thread}

Then quickly type #thread

Note that the Alarm is NOT mentioned. It is running in the main thread and we cannot see it.

But if we place a #while or an #until into the Alarm then the Alarm is given its own thread. THIS seems logical to me.
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Seb
Wizard


Joined: 14 Aug 2004
Posts: 1269

PostPosted: Mon Nov 12, 2007 1:47 am   
 
Arminas wrote:
For an example of how they are SUPPOSED to work type this into the command line.

#alarm +3 {#say One Thread}

Then quickly type #thread

Note that the Alarm is NOT mentioned. It is running in the main thread and we cannot see it.

Isn't the reason why the alarm is not mentioned is because it is not actually running yet:
Code:
#alarm +3 {#wait 5000;#say One Thread}

After 3 seconds:
Quote:
#thread
Threads:
# ID Window Name Status Script
-------------------------------------------------------------------------------------------------
3 [u] Xxxx.mud running #thread
171 Xxxx.mud wait 174 ms Alarm "_Alarm2" : #wait 5000;#say One...
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Mon Nov 12, 2007 3:09 am   
 
Could be. Or it could be the fact that #wait calls another thread.
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Malach
Apprentice


Joined: 03 Nov 2007
Posts: 132

PostPosted: Mon Nov 12, 2007 12:02 pm   
 
In Zugg's initial response to my post he points out that there are two alarms running simultaneously in their own threads. He also says somewhere in this thread that both triggers and alarms create their own threads. This, as well as my own experience in which an alarm is pretty much always creating a new thread regardless of where it's called from (there's another thread that talks about how alarms are not reusing threads and the thread count just keeps going up) would make it a reasonable conclusion that alarms are not running in the main thread.
_________________
Intel Core2 Quad CPU @ 2.4 GHZ with Windows Vista Home Premium and 2 GB Ram
Reply with quote
Vijilante
SubAdmin


Joined: 18 Nov 2001
Posts: 5182

PostPosted: Mon Nov 12, 2007 12:24 pm   
 
Alarms were changed to run in thier own thread quite a few versions ago. I did a test like this
Code:
#LOOP 20 {#ALARM +1 {#SHOW fired}}
and prior to the change 1 alarm would fire each second, even though they should all fire in the same second. Alarms had to operate in thier own threads in order for that simple test to work.

The problem with threads not getting reused is a seperate issue. I think there are actually 2 bugs involved with it. One of which is how it flags the end state of the thread as shown in my other post. The other we haven't found yet, but I have my guess and can't really test it until the known bug is fixed.
_________________
The only good questions are the ones we have never answered before.
Search the Forums
Reply with quote
Zugg
MASTER


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

PostPosted: Mon Nov 12, 2007 6:12 pm   
 
Yes, Alarms were changed to run in their own thread. There was a very important reason for this. Alarms fire when a Windows Timer expires (and sends a WM_TIMER message to the window). If an alarm ran in this main thread, then it would block Windows messages until it was done. Blocking Windows messages in a WM_TIMER message is a really bad idea and can cause all sorts of problems. All it would take is one bad alarm and the entire system could lock up. So I was forced to make alarms spawn their own thread. Alarms do not reuse any existing thread...they always create their own thread.

So yes, with alarms you will get parallel threads, just like when using #WAIT. The only way to "wait" for something without causing parallel threads would be to use a wait condition within a multistate trigger.

However, let's not get too carried away with scaring people away from threads.

1) Writing to the same variable in multiple threads does *NOT* cause data "corruption". That's a bad word and you really shouldn't use it like that. What happens when multiple threads write to the same variable is that you just don't know which thread finished last. So if thread A sets a variable to "1" and thread B sets the same variable to "2", then you don't really know if the value is going to be set to "1" or "2". If A finished after B then the value is "1". If B finished after A then the value is "0". And really, this is just what you'd expect. If you did this, even in zMUD, what is the result:
Code:
#ALARM +2 {#VAR a 1}
#ALARM +2 {#VAR a 2}

So after waiting for 2 seconds, is the value of @a "1" or "2"? It's really undefined. We are just saying the same thing with threads. The above code will work just fine...it won't "corrupt" any data. You just won't know if @a is "1" or "2".

2) When using Alarms (or when delaying execution using #WAIT), you just need to keep in mind that now you will have multiple scripts running at the same time. Windows itself will time-share between these two threads, and unless you synchronize the scripts somehow, you'll never know which script finished first. And when CMUD is in the middle of running one script, it might get interrupted and start running another script. So just think about this when using Alarms. Look at your alarm script, and think about how it might break if some other script runs at the same time.

But really...let's not get people all "panicked" over using threads and alarms. Yes there are still some bugs, but really it works fine in the vast majority of cases. And as I've said before, zMUD had these same problems...they were just hidden. But these kind of problems is why the #wait command didn't work sometimes in zMUD. zMUD just handled it by creating multiple Windows messages and then creating multiple message wait loops, which caused all sorts of other problems that were just hidden from you. Your script would just fail sometimes and you wouldn't know why.

CMUD just makes this more obvious and makes you think about it a bit more. There is no "magic" way to just make these problems go away. When you are using an Alarm, you are telling CMUD to run multiple scripts at the same time and no matter how that is implemented, you will always need to think about what you are doing.

If you *do* want to block execution when an alarm fires, then simply have the alarm #raise an event, or #fire a trigger. The event/trigger will be created in their own thread, but the main thread will block and wait an them to finish before continuing.
Reply with quote
Display posts from previous:   
Post new topic   Reply to topic     Home » Forums » CMUD Beta Forum All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
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