ON BREAK doesn't always work

  • This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn more.
#1
I have a BTM which starts like this.
Code:
setlocal
setarray b[9]
on break ( unsetarray b & echo Quitting! & quit )
There are no other ON commands. The body is nested inside 9 DO loops.
In the first run below, when I pressed Ctrl-C, it quit with no message and without unsetting the array (as evidenced by the error when it was restarted). After restarting (the second run below) the first Ctrl-C resulted in the array being unset and the message being echoed but the BTM didn't quit; The second Ctrl-C got it to quit (but with an error because the array had been unset by the first Ctrl-C). The behavior is erratic. The BTM is attached. It plays all possible games of tic-tac-toe and calculates the fraction which end in a win (with occasional progress reports). Well, the server won't let me upload it (A server error occurred. Please try again later.) neither as a BTM nor as a ZIP. Rex, I'll email it to you.
Code:
v:\> ttt.btm
15:12:01
8 / 10 = 0.8
37 / 50 = 0.74
101 / 130 = 0.7769230769
129 / 170 = 0.7588235294
^C
 
v:\> ttt.btm
TCC: V:\ttt.btm [2]  Array variable is already defined "b"
15:12:21
8 / 10 = 0.8
37 / 50 = 0.74
101 / 130 = 0.7769230769
129 / 170 = 0.7588235294
158 / 210 = 0.7523809524
185 / 250 = 0.74
^C
Quitting!
236 / 310 = 0.7612903226
236 / 320 = 0.7375
236 / 330 = 0.7151515152
236 / 340 = 0.6941176471
236 / 350 = 0.6742857143
236 / 360 = 0.6555555556
236 / 370 = 0.6378378378
236 / 380 = 0.6210526316
236 / 390 = 0.6051282051
236 / 400 = 0.59
236 / 410 = 0.5756097561
^C
TCC: V:\ttt.btm [26]  Not an array variable "b"
Quitting!
 
v:\>
Here's another (on another machine) where it took many attempts before it quit, each time unsetting the array (or trying to) and echoing the message ... except for the last when neither the error message nor the "Quitting" message appeared.
Code:
v:\> \\zz\h$\Workplace\ttt.btm
15:23:02
8 / 10 = 0.8
37 / 50 = 0.74
101 / 130 = 0.7769230769
129 / 170 = 0.7588235294
158 / 210 = 0.7523809524
185 / 250 = 0.74
252 / 330 = 0.7636363636
^C
Quitting!
270 / 360 = 0.75
270 / 370 = 0.7297297297
270 / 380 = 0.7105263158
270 / 390 = 0.6923076923
270 / 400 = 0.675
270 / 410 = 0.6585365854
270 / 420 = 0.6428571429
270 / 430 = 0.6279069767
270 / 440 = 0.6136363636
270 / 450 = 0.6
270 / 460 = 0.5869565217
270 / 470 = 0.5744680851
^C
TCC: \\zz\h$\Workplace\ttt.btm [74]  Not an array variable "b"
Quitting!
270 / 480 = 0.5625
270 / 490 = 0.5510204082
270 / 500 = 0.54
270 / 510 = 0.5294117647
270 / 520 = 0.5192307692
270 / 530 = 0.5094339623
270 / 540 = 0.5
270 / 550 = 0.4909090909
270 / 560 = 0.4821428571
270 / 570 = 0.4736842105
^C
TCC: \\zz\h$\Workplace\ttt.btm [78]  Not an array variable "b"
Quitting!
270 / 580 = 0.4655172414
270 / 590 = 0.4576271186
270 / 600 = 0.45
270 / 610 = 0.4426229508
^C
TCC: \\zz\h$\Workplace\ttt.btm [80]  Not an array variable "b"
Quitting!
270 / 620 = 0.435483871
270 / 630 = 0.4285714286
270 / 640 = 0.421875
^C
TCC: \\zz\h$\Workplace\ttt.btm [79]  Not an array variable "b"
Quitting!
270 / 650 = 0.4153846154
270 / 660 = 0.4090909091
270 / 670 = 0.4029850746
^C
 
v:\>
 
#2
P.S. If I put the ON BREAK command in the inner loop the behavior is the same, erratic. If I put the ON BREAK inside the ":win" subroutine, TCC disappears when I press Ctrl-C, and leaves this GPF file.
Code:
TCC  14.02.39
Module=g:\tc14\TakeCmd.dll
Address=1007E47C
Exception=C0000005
EAX=3090FC28  EBX=76EDAAD7  ECX=00000000  EDX=00000000
ESI=3090E199  EDI=058E7648  EBP=00BFC83C  ESP=00BEC7B8
CS=0000001B  DS=00000023  ES=00000023  SS=00000023
Flags=00010206
 
Stack:
1 : TakeCmd.dll 00000001:0007d47c
2 : TakeCmd.dll 00000001:0009164d
3 : TakeCmd.dll 00000001:000913be
 

rconn

Administrator
Staff member
May 14, 2008
10,101
85
#3
The problem isn't with ON BREAK. Because you've nested your DO loops so deeply, the parser has to unwind a couple dozen levels before it can even determine that you've done a QUIT. (A QUIT just sets the batch file pointer to the end; it does not immediately terminate the batch file.)

A DO causes a recursive entry into the parser; nested DO's double that (at least -- it could be more depending on what else you're doing, like command grouping). So depending on where you were in your DO hierarchy, the parser may have to execute a *lot* more DO statements before it reaches a point where the QUIT could take effect.
 
#4
I thought I posted a simple example with just a single DO. Here it is again. It won't QUIT if the break happens in a subroutine.
Code:
v:\> type simplified.btm
 on break ( echo Quitting at line %_batchline & quit )
 do i=1 to 1000000
 set a=%@exec[gosub foo]
 enddo
 quit
 
:foo
if 1==1 set b=b
if 1==1 set b=b
if 1==1 set b=b
if 1==1 set b=b
if 1==1 set b=b
if 1==1 set b=b
return 1
 
v:\> simplified.btm
^C
Quitting at line 8
^C
Quitting at line 12
^C
Quitting at line 12
^C
Quitting at line 8
^C
Quitting at line 3
 
v:\>
 
#5
Something is definitely screwy. I ran the BTM below several times. Every time it ended when I pressed Ctrl-C. The 4th time it neither echoed "bye" nor unset the array!
Code:
v:\> type crazy.btm
echo %_time
setarray b[9]
set brk=0
on break ( set brk=1 )
do i=1 to 1000
do j=1 to 1000
        if %brk == 1 goto goner
enddo
enddo
 
:goner
echo bye
unsetarray b
quit
Code:
v:\> crazy.btm
23:30:46
^C
bye
 
v:\> crazy.btm
23:30:48
^C
bye
 
v:\> crazy.btm
23:30:50
^C
bye
 
v:\> crazy.btm
23:30:52
^C
 
v:\> crazy.btm
23:30:57
TCC: V:\crazy.btm [2]  Array variable is already defined "b"
^C
bye
 
v:\>
 

rconn

Administrator
Staff member
May 14, 2008
10,101
85
#6
I thought I posted a simple example with just a single DO. Here it is again. It won't QUIT if the break happens in a subroutine.
Code:
v:\> type simplified.btm
on break ( echo Quitting at line %_batchline & quit )
do i=1 to 1000000
set a=%@exec[gosub foo]
enddo
quit
I can't imagine what you're trying to do with the @EXEC (which doesn't add anything other than a LOT of overhead). The ON BREAK works fine without the @EXEC, so your problem with QUIT seems unrelated to DO (or ON BREAK, for that matter).

There does seem to be an issue with executing QUIT from inside an @EXEC function, though that fortunately doesn't seem to be something that users are typically going to want to do. I'll take a look at it but it's not likely to be something that I'm going to want to rewrite the parser for.
 

rconn

Administrator
Staff member
May 14, 2008
10,101
85
#7
The parser is saving the global variables (including the current batch file state -- offset, line number, name, etc.) before running @EXEC / @EXECSTR / @EXECARRAY. So the QUIT inside @EXEC sets the offset to the end of the file, and the variable expansion sets it back to its previous position when @EXEC returns.

Changing this is going to have a very far-ranging impact, so I think I'm going to need a better argument than your (contrived?) example. Can you think of a convincing argument why anyone would need to be doing @EXEC[quit] ?
 
#8
How about my last example (not involving a subroutine) in which ON BREAK doesn't always behave the same. Below, sometimes it executes the commands after the label "goner" and sometimes it doesn't.
Code:
v:\> type crazy.btm
echo %_time
setarray b[9]
set brk=0
on break ( set brk=1 )
do i=1 to 1000000
        if %brk == 1 goto goner
enddo
 
:goner
echo bye
unsetarray b
quit
v:\>
Code:
v:\> crazy.btm
14:33:52
^C
bye
 
v:\> crazy.btm
14:33:54
^C                    <======= no "bye"; array not unset
 
v:\> crazy.btm
14:33:56
TCC: V:\crazy.btm [2]  Array variable is already defined "b"
^C
bye
 
v:\> crazy.btm
14:33:57
^C                    <======= no "bye"; array not unset
 
v:\> crazy.btm
14:33:59
TCC: V:\crazy.btm [2]  Array variable is already defined "b"
^C                    <======= no "bye"; array not unset
 
v:\> crazy.btm
14:34:01
TCC: V:\crazy.btm [2]  Array variable is already defined "b"
^C
bye
 
v:\> crazy.btm
14:34:03
^C
bye
 
v:\>
 
#10
Not reproducible here.
I can only suppose you didn't try hard enough. Here's the same BTM on another machine.
Code:
echo %_time
setarray b[9]
set brk=0
on break ( set brk=1 )
do i=1 to 1000000
        if %brk == 1 goto goner
enddo
 
:goner
echo bye
unsetarray b
quit
Code:
v:\> crazy.btm
20:12:59
^C
bye
 
v:\> crazy.btm
20:13:02
^C
 
v:\> crazy.btm
20:13:06
TCC: V:\crazy.btm [2]  Array variable is already defined "b"
^C
bye
 
v:\> crazy.btm
20:13:09
^C
bye
 
v:\> crazy.btm
20:13:12
^C
bye
 
v:\> crazy.btm
20:13:15
^C
 
v:\> crazy.btm
20:13:18
TCC: V:\crazy.btm [2]  Array variable is already defined "b"
^C
bye
 
v:\>
 
#14
I don't have the slightest idea. Does it happen without any plugins loaded?

Can you reproduce it with anything other than DO -- i.e., FOR?
I discovered it with no plugins loaded and almost all my testing was with no plugins. It does not do it (50-60 tries) if I replace DO with FOR in the most recently posted BTMfile. It also doesn't do it if I replace the DO loop with
Code:
:label
if %brk == 1 goto goner
goto label
Is there another CtrlHandler hanging around ... doing something other than the GOTO? That's what it seems like.

Even with DO, this (a plugin workaround) works (as long as I remember to press Ctrl-q).
Code:
if %_inkey == @17 goto goner
So it really seems to deal with the CtrlHandler.
 
#17
The problem isn't with ON BREAK. Because you've nested your DO loops so deeply, the parser has to unwind a couple dozen levels before it can even determine that you've done a QUIT.
Sounds like the parser needs to be improved. What's the maximum level of nested loops? Documentation under "Limits" says "no limit", which means it should be able to handle an infinite number (allowing for memory, of course).

Can you think of a convincing argument why anyone would need to be doing @EXEC[quit] ?
There doesn't need to be a "convincing argument". If someone can think of it and do it, even if "contrived", then it either needs to be fixed or an exception needs the be thrown and caught. It like what George Carlin said...
George Carlin said:
If you can nail together two things that have never been nailed together before, some dumb schmuck will buy it from you.
Why does it always sound as if you are putting the blame on the user?

It's like going to the doctor and telling him "Doc, it hurts when I move my arm like this." So the doctor says "Then don't move your arm like that."
 
#18
In Rex's defence, I was using @EXEC[GOSUB ... ] only because I remembered doing that a long time ago and had forgotten that I could simply GOSUB and then look at _?. I deserve to be scolded for that. But changing that in my original BTM left me with all the same ON BREAK problems.
 

rconn

Administrator
Staff member
May 14, 2008
10,101
85
#19
Sounds like the parser needs to be improved. What's the maximum level of nested loops? Documentation under "Limits" says "no limit", which means it should be able to handle an infinite number (allowing for memory, of course).
The ON BREAK issue doesn't have anything to do with the parser or the maximum level of nested DO loops. The problem is with using QUIT embedded deeply within DO loops -- QUIT does *not* immediately exit the batch file, it simply sets the read pointer to the end of the file, so that the next read will terminate it. But when you're deep within nested DO's, the parser isn't reading from the batch file, it's executing the already-read DO blocks, so it's not going to see the QUIT until it finishes the DO loops.
 
#20
The ON BREAK issue doesn't have anything to do with the parser or the maximum level of nested DO loops. The problem is with using QUIT embedded deeply within DO loops -- QUIT does *not* immediately exit the batch file, it simply sets the read pointer to the end of the file, so that the next read will terminate it. But when you're deep within nested DO's, the parser isn't reading from the batch file, it's executing the already-read DO blocks, so it's not going to see the QUIT until it finishes the DO loops.
That simply does not seem to be the case. Using "on break ( set brk=1 )" I put these two statements inside the 9 nested DO loops
Code:
if %_inkey == @17 (echo Ctrl-q! & unsetarray b & quit)
if %brk == 1 (echo Break! & unsetarray b & quit)
Pressing Ctrl-q works every time (completely and immediately) while pressing Ctrl-c often does not echo the message or unset the array. I could/have done the same thing with "INKEY /P /W0 /X" and with the same results as the plugin _INKEY.
Code:
v:\> ttt.btm
14:45:12
8 / 10 = 0.8000000000          (202.0 min)
37 / 50 = 0.7400000000          (202.6 min)
Ctrl-q!
 
v:\> ttt.btm
14:45:17
8 / 10 = 0.8000000000          (201.4 min)
Ctrl-q!
 
v:\> ttt.btm
14:45:21
8 / 10 = 0.8000000000          (201.4 min)
^C                        <====================no message; array not unset
 
v:\> ttt.btm
TCC: V:\ttt.btm [2]  Array variable is already defined "b"
14:45:26
8 / 10 = 0.8000000000          (200.8 min)
37 / 50 = 0.7400000000          (202.3 min)
^C                        <====================message; array unset
Break!
 
v:\> ttt.btm
14:45:30
8 / 10 = 0.8000000000          (202.6 min)
37 / 50 = 0.7400000000          (202.7 min)
^C                        <====================no message; array not unset
 
v:\> ttt.btm
TCC: V:\ttt.btm [2]  Array variable is already defined "b"
14:45:34
(and so on)
 
May 29, 2008
517
3
Groton, CT
#21
The ON BREAK issue doesn't have anything to do with the parser or the maximum level of nested DO loops. The problem is with using QUIT embedded deeply within DO loops -- QUIT does *not* immediately exit the batch file, it simply sets the read pointer to the end of the file, so that the next read will terminate it. But when you're deep within nested DO's, the parser isn't reading from the batch file, it's executing the already-read DO blocks, so it's not going to see the QUIT until it finishes the DO loops.
So it needs a QUIT and a LEAVE? Is there some way to effect that like IF condition (QUIT & LEAVE) ?
Would that first set the pointer to the EOF and then exit the DO-loop?
 
#22
Note that in my last post, the BTM ends when I press Ctrl-C .. but apparently (sometimes) **NOT** because I said
Code:
if %brk == 1 (echo Break! & unsetarray b & quit)
If it were executing **MY** "QUIT" I'd expect to see the message and that the array be unset.
 

samintz

Scott Mintz
May 20, 2008
1,204
11
Solon, OH, USA
#23
Not reproducible here.
I ran Vince's CRAZY.BTM file and got pretty much the same results as he did.
Code:
echo %_time
setarray b[9]
set brk=0
on break ( set brk=1 )
do i=1 to 1000000
        if %brk == 1 goto goner
enddo
 
:goner
echo bye
unsetarray b
quit
And this is my output:
Code:
crazy
16:11:34
^C
bye
 
[R:\LNX] crazy
16:11:41
^C
 
[R:\LNX] crazy
16:11:54
TCC: R:\LNX\crazy.btm [2]  Array variable is already defined "b"
^C
 
[R:\LNX] crazy
16:11:59
TCC: R:\LNX\crazy.btm [2]  Array variable is already defined "b"
^C
bye
 
[R:\LNX] crazy
16:12:16
^C
 
[R:\LNX] crazy
16:12:21
TCC: R:\LNX\crazy.btm [2]  Array variable is already defined "b"
^C
 
[R:\LNX] crazy
16:12:25
TCC: R:\LNX\crazy.btm [2]  Array variable is already defined "b"
^C
bye