I think you are making this more complex than it needs to be: * If the command completes before the engine gets the abort, then the command succeeds and the abort fails. Commands which happen instantly, like "quit" or "boardsize", the abort never comes in time, so it's always the abort that fails. This makes sense because the whole point of the abort is not to undo things - instead, it is to get the GTP client to stop what it is doing and start listening to commands from the server again. Whether the client stops because it completed the command (in which case the command returns success & the abort returns an error) or because it has aborted the command (in which case the command returns an error & the abort returns success) doesn't matter. * Since "abort" causes the previous command to fail (if the abort succeeds), and the GTP spec says that a failed command should not change the state of the engine, that does imply that if you can't return to the original engine state, you should continue working on the genmove then return an error for the abort. This is fine with me, but if we explicitly say that an aborted genmove can change the engine state that is fine with me also; I don't see that it matters whether you undo your engine state or not after an abort to a genmove. If you can, fine, if you can't, fine, as long as these state changes don't leave you with a broken engine! For example, in a "genmove/abort/genmove", if you have worked out some life&death situations in the first genmove, why not be able to use them in the 2nd? I do agree that abort needs to be 100% optional. That makes it easier; anything you can't abort, just finish the command and error out the abort. If you can't abort anything, just treat abort as an unrecognized command. A server must treat abort as a "hint" that it doesn't care about the output of the current command, so this "hint" is only needed to save time, not needed for correctness.
On Mon, 2002-11-04 at 03:00, Andrew Derrick Balsa: I have thought about implementing it in the various small "xxxplus" engines. But I immediately hit a snag: it's quite obvious that the engine has to react differently according to what it was doing when it receives the "abort" command. What should it do if it receives the "abort" just after it received a "quit"? Or after receiving a "boardsize"?
Let's assume it can only receive an "abort" after it has received a "genmove"... Should it "undo" anything it has done until now? What if it can't undo changes to some data structures? Should it fail? But the spec says that any failed command should not change the state of the engine...
I confess I gave up after a few minutes. Receiving an "abort", either as a single character or as an (IMHO preferable) full word, in a separate thread, is easy in Linux. Making the engine react in a consistent manner, compatible with the rest of the GTP command set, would certainly require a lot of work.
Perhaps I am wrong and somebody can come up with an unambiguous one-paragraph GTP spec for the "abort" command. I couldn't think of any way to do that. :-(
Regards, -- Andrew D. Balsa andrebalsa@mailingaddress.org
Bill wrote:
I think you are making this more complex than it needs to be:
Agreed.
This makes sense because the whole point of the abort is not to undo things - instead, it is to get the GTP client to stop what it is doing and start listening to commands from the server again.
This is the key point to the whole issue.
Whether the client stops because it completed the command (in which case the command returns success & the abort returns an error) or because it has aborted the command (in which case the command returns an error & the abort returns success) doesn't matter.
I don't see any reason for the abort command to return an error just because the engine didn't notice it in time to actually abort anything. It should be apparent from the results of the pending commands whether anything was aborted.
* Since "abort" causes the previous command to fail (if the abort succeeds), and the GTP spec says that a failed command should not change the state of the engine, that does imply that if you can't return to the original engine state, you should continue working on the genmove then return an error for the abort. This is fine with me, but if we explicitly say that an aborted genmove can change the engine state that is fine with me also; I don't see that it matters whether you undo your engine state or not after an abort to a genmove. If you can, fine, if you can't, fine, as long as these state changes don't leave you with a broken engine! For example, in a "genmove/abort/genmove", if you have worked out some life&death situations in the first genmove, why not be able to use them in the 2nd?
The protocol does not concern itself with any random internal state in the engine, like cached life&death statuses. Only the state variables explicitly listed in the spec are required to be unchanged by a failed command. This should hold also for failures due to an abort.
I do agree that abort needs to be 100% optional.
Indeed.
If you can't abort anything, just treat abort as an unrecognized command.
Also agreed.
A server must treat abort as a "hint" that it doesn't care about the output of the current command, so this "hint" is only needed to save time, not needed for correctness.
A corollary is that if your engine is fast enough to respond to any command in a fraction of a second, just don't bother with abort. There's no point.
A few more comments to other things mentioned somewhere in this discussion and a few which have not been mentioned:
* I think it's clear that it's not worthwhile to involve control characters. A regular command "abort" is good enough and fits better with the overall protocol structure.
* An engine which receives an abort during the processing of a genmove may freely choose whether to: * Abort the processing and fail the genmove with the error message "aborted". * Abort the processing and return a "half thought through" move successfully. * Finish the processing and successfully return the generated move as usual. However, if it would always choose the latter alternative, regardless of where it is in the processing, it probably shouldn't implement the abort command at all.
* If several commands are pending when the abort is received and one command is aborted (i.e. failing with the "aborted" error message), all the following commands must also be aborted.
* The response to the abort command must of course come after the responses to pending commands. I think it should always succeed, and return nothing, if it is recognized at all. It isn't worthwhile to have it report anything.
Finally I'd like to hear, just out of curiousity, how the various go programs out there would be able to handle abort of a genmove (whether or not GTP has been implemented in the program at this time). I guess there are five major categories:
1a. The program isn't able to poll for abort commands at all. 1b. The program is so fast that there's no point polling for an abort. 2. The program isn't able to abort the genmove. 3. The program can abort the genmove but not generate a half thought through move. 4. The program can abort the genmove and return a half thought through move.
Hi,
On Monday 04 November 2002 19:44, Gunnar Farnebäck wrote:
Bill wrote:
I think you are making this more complex than it needs to be:
Agreed.
Thanks, Bill, Gunnar, for bringing some simplification in the matter.
I have been looking into a simple implementation of the "abort" command for the *plus programs. There are some non-trivial issues:
1) I would use pthreads. This seems reasonable. I see three threads: main loop, GTP input, and a separate thread for "long" commands (see below). 2) There would be a separate thread for reading GTP commands. Should it (does it need to ?) queue the commands received or not? 3) Make an arbitrary distinction between "immediate" and "long" commands. I see two "long" commands right now: move generation and scoring. 4) Immediate commands are executed in an atomic way - they cannot be aborted. 5) "long" commands are started in a separate thread. This thread can be aborted if it has not reached the "commit point" at the end. At the commit point the engine makes changes to data structures that define its state. 6) Asynchronous commands: only abort right now.
What do you think?
Regards,
Gunnar Farnebdck wrote:
This makes sense because the whole point of the abort is not to undo things - instead, it is to get the GTP client to stop what it is doing and start listening to commands from the server again.
This is the key point to the whole issue.
And I think it would be really nice for the interactivity of GUIs.
Finally I'd like to hear, just out of curiousity, how the various go programs out there would be able to handle abort of a genmove (whether or not GTP has been implemented in the program at this time). I guess there are five major categories:
1a. The program isn't able to poll for abort commands at all. 1b. The program is so fast that there's no point polling for an abort. 2. The program isn't able to abort the genmove. 3. The program can abort the genmove but not generate a half thought through move. 4. The program can abort the genmove and return a half thought through move.
From Markus' messages I conclude that Neurogo and Explorer fall into
category 4 and I'm pretty sure that all of Andrew's *plus programs so far fall into category 1b. I can add that GNU Go is a 1a and that it would require a major effort to get it further than 2.
I'd assume that all programs doing full-board alpha-beta search would not require too much effort to satisfy 4. I would also think that GNU Go could satisfy 3 with some reasonable effort.
For the record, I checked what the corresponding protocol between chess GUIs (xboard, winboard, ...) and chess engines is doing. Apparently a platform dependant solution with signals, which is I assume not an option for GTP.
Arend