 |
Vijilante SubAdmin

Joined: 18 Nov 2001 Posts: 5187
|
Posted: Tue Nov 13, 2007 9:47 pm
[2.11] #WAITFOR pattern problems |
It would seem that #WAITFOR has some problems handling pattern syntaxes. The behavior varies with different patterns, some just fall through unexpectedly, others never get detected. I will be listing a few that work as well as some that don't work.
#WAITFOR {{list1|list2}};#SHOW Fired
This works perfectly.
#WAITFOR {^{list1|list2}};#SHOW Fired
#WAITFOR {{list1|list2}$};#SHOW Fired
#WAITFOR {({list1|list2})};#SHOW Fired
All fall through, debugger output is very disturbing
| Code: |
untitled | [8] untitled Comline : start : #WAITFOR {^{list1|list2}};#SHOW fired
untitled | [8] untitled Comline : timeout
untitled | [8] untitled Comline : wait for pattern: : ^{list1|list2} |
#VAR abc {list1|list2}
#WAITFOR {{@abc}};#SHOW Fired
Never matches anything in the variable, but does actually make a thread waiting for the pattern.
------
I think it should be possible to support the full pattern syntax in #WAITFOR. Obviously something like the variable usage would be a snap shot of the variable's value at the time the #WAITFOR occured. I can't really think of a good way to handle parameter generation from () in the pattern, since the #WAITFOR can't reasonably overwrite existing parameters. Maybe there is some way.
The part I consider disturbing about the debugger's output for it is that it says it is waiting for it after it says the timeout occured. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
 |
Zugg MASTER

Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Wed Nov 14, 2007 12:04 am |
Hmm, very odd. I'll look into this. The parser is treating the argument the same as the argument to #TRIGGER so I'm not sure why it's working differently. And yes, the debugger output is definitely odd too.
|
|
|
|
 |
Zugg MASTER

Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Nov 16, 2007 7:49 pm |
Looks like this is a thread "reuse" problem. The first #WAITFOR actually always works fine, and the next #WAITFORs always fail. So it actually doesn't matter what the pattern string is. Still working on this, but hopefully I can get it fixed.
|
|
|
|
 |
Zugg MASTER

Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Nov 16, 2007 8:39 pm |
Well I found the source for this. And it's really annoying. Delphi's "Resume" method has an Assert that can fail, but they don't trap the exception, so their internal FSuspended property doesn't get reset properly. Also, neither their Suspend or Resume methods are threadsafe. Can't believe this really. How could they forget something so basic in their own thread routines??? And to make matters worse, neither of these methods are marked as Virtual, so I cannot override them. Also stupid since it doesn't allow alternate thread implementations. Sigh. So, I'm not sure what I'm going to do about this yet. But it explains some other weird errors that I was getting with threads a couple of weeks ago.
|
|
|
|
 |
Zugg MASTER

Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Nov 16, 2007 8:42 pm |
Hmm, found this in the newgroups:
| Quote: |
The problem is that the API does not offer any way to check the suspend
state of a thread, so the VCL has to try to track that, using this
FSuspended kludge. But this is not completely threadsafe if a thread is
suspended and resumed from outside and also suspends itself when done with
a task, there is a potential for a race condition that can end up with
FSuspended = false but the thread being suspended on the API level. That is
one of the reasons why it is a bad idea to control threads via
suspend/resume.
Borland has been aware of the problem since before D5, if memory serves,
but there is simply no solution to the problem that does not add a risk of
deadlocks, so we have to live with it. |
That's a bad sign. I'm not sure how else we are supposed to control threads if not with Suspend/Resume! |
|
|
|
 |
Zugg MASTER

Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Nov 16, 2007 9:22 pm |
Well, I went ahead and wrote my own Suspend/Resume routines that properly trap the exception so that my own FSuspended flag doesn't get screwed up. Then I check my own FSuspended flag instead of checking the Delphi Suspended property. This seems to work better now. Hopefully there aren't any other side effects of this.
I can see why it's very hard to make this thread safe though. If you put a Critical Section lock around it, you get a hang because the thread gets suspended while the lock is active. So I can see the problem the Delphi developer's were dealing with. But not trapping their own exception was a bad oversight. Maybe that will be fixed in Delphi 2007. |
|
|
|
 |
Vijilante SubAdmin

Joined: 18 Nov 2001 Posts: 5187
|
Posted: Fri Nov 16, 2007 9:53 pm |
I did a little research to see if anyone had developed a better solution. I still haven't found anything specific to this yet, but I did find a nice site that has just about everything on threading. It was written in 2000 and its author used Delphi4 so the may be a few tidbits of use in there for you. At the very least others that are considering using threads in thier scripts can benefit from the first few chapters.
http://www.eonclash.com/Tutorials/Multithreading/MartinHarvey1.1/ToC.html |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
 |
Zugg MASTER

Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Nov 16, 2007 10:20 pm |
I'll take a look, but Delphi threads have changed a *lot* since Delphi4.
Most of the time I'm actually using TIdThreads from the Indy 10 Internet components (which all require threading). The Indy people also had some good threading discussions in their eBook that I bought a couple of years ago. It's not generally available on the net though. But using threads properly is a big part of developing network server applications, so it makes sense that they would have a good discussion of it. And unlike most WinSock components, Indy has always been "blocking" and relied upon proper thread usage.
Their wrapper to the Delphi TThread fixes a lot of issues, but they also were not able to fix this particular issue because Delphi didn't make the Suspend and Resume methods virtual. |
|
|
|
 |
Zugg MASTER

Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Sat Nov 17, 2007 6:21 am |
Well, my "fix" screwed everything up. I noticed that CMUD was about five times slower than normal. Then I noticed that threads were never getting reused...it was always creating new threads for the command line and triggers. I took out my "fix" and then it went back to normal.
So, I thought about the statement above:
| Quote: |
| That is one of the reasons why it is a bad idea to control threads via suspend/resume. |
Since it was only causing a problem for #WAITFOR, I looked for another way to handle it instead of doing a suspend. I already had the code for handling #WAITFOR with a timeout value (which *doesn't* use suspend...it uses a WaitForObjects call). So, instead of doing a Suspend when there isn't any timeout, I just set the timeout to INFINITE and used the same wait code. This fixed #WAITFOR. I'm going to look for other places where Suspend is used like this and change them too.
It's going to be a very late release tonight. That is, unless I go completely crazy first. It has *not* been a good day for getting this release finished. But I'm busy this weekend and I really want people to be able to play with the new scoping rules. |
|
|
|
 |
|
|
|