Hi,
OK, I have something that works and that seems half-decently coded. Here is a
sample output for Randyplus with threaded genmove:
-----
30 genmove black
=30 d1
40 genmove white
50 abort
?40 aborted
=50
60 genmove white
=60 e11
-----
If you ask me how I managed to type the abort: I introduced a 10-second
"thinking" delay in the separate genmove thread. ;-)
The implementation is coded using basic POSIX pthreads calls, so the basic
algorithm should work on anything that has POSIX …
[View More]pthreads.
To abort the separate genmove thread, I have used the pthreads call
pthreads_cancel( ).
In the threaded genmove, I use the call:
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
when I reach the commit point. Beyond that point, the cancel request is not
honoured anymore.
All the changes to my original code were made in gtp.cpp.
Limitations:
1) Presently only genmove can be aborted (this can be extended easily but I
didn't try it).
2) Abort only works if it is the first command following the genmove. Other
commands will block, waiting for genmove to finish.
3) There must be a clearly established "commit point" in the genmove routine,
beyond which it should not be cancelled.
Thanks to Bill and Gunnar for the necessary clarifying. Up to you now if you
want to include "abort" as an optional GTP command!
Rather than packaging Randyplus again, I am attaching the bzipped diff file
(2KB) if anyone wants to try it out.
--
Andrew D. Balsa
andrebalsa(a)mailingaddress.org
[View Less]
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
…
[View More]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(a)mailingaddress.org
[View Less]
I still don't think we should allow the engine to return
half-baked results when interrupted during genmove.
Either succeed or fail. Bill schubert's scheme is cleaner
, IMO.
wrt the ordering of commands and responses: there is nothing new
here: if we treat abort like any other command, it's responses should
come in the right order.
The reason I proposed the (optional) report_post_abortem_move is a
previous post was to avoid the three valued logic that creeps in
if we allow the genmove to …
[View More]produce half-baked moves.
My program falls in category 1a or 1b. It does not have
a move generator yet, and it is pretty hard to maintain state
even without asynchronous commands ... Since I am not concerned
with portability, it would be relatively easy to get upto step 3.
Counter-question to gunnar: would it be easyer to implement
te abort command in Gnugo, if it were allowed to use a SIGINT
instead ? (my guess is: yes)
AvK
[View Less]
Markus wrote:
> I still think that there is no need to specify, if the engine should
> return success or error to an aborted genmove.
What I think wms meant (and what I tried to quote ...) was:
every command should either succeed or fail.
when it succeeds, it could output it results, if any.
If it fails, it issues a simple error message/reason.
Aborting a command should be considered an error
(from the command's perspective): the aborted command
should result in an "?123 command aborted\…
[View More]n\n" reply.
There is no timing problem possible here: it is either succeed,
or fail. If the abort is received too late, the command will already
have reported it's success, and the abort will of course fail.
I hope I summarised correctly.
AvK
[View Less]
My attempt to summarise some of the points of discussion
wrt the gtp-interrupt-feature:
*) One-character (^C) vs full-command ("abort") interrupt.
We seem to agree that this is a non-issue.
This involves only the difference between one-
character lookahead vs word- line- or bigger lookaheads.
For the implementation, the size of the lookahead buffer
is less important than the lookahead per se, since that
will require the program to use some kind of polling,
threading, or event-driven …
[View More]input.
(a related issue, the signal-style interrupt has
already been abandoned)
*) The dialog-style of the command.
Bill Schubert's approach produces the cleanest
interface: the interrupt command should behave just like any
normal command, so the program should report separate results
for both the interrupted command and the interrupt command
itself. Both of them can either succeed or fail, seemingly
independently.
*) Semantics.
*) should the aborted command ("genmove", in most cases) report
the best move found sofar ?
*) if so, should it also have "committed" the move to
it's internal state ?
*) if it doen not have any move yet, what should the reply be ?
*) should the abort also undo the aborted command's side-effects ?
Personally, I think the aborted command should return an error,
and not some partial result. This will keep most of the
semantics out of the protocol.
a New proposal:
Maybe a separate command could be introduced to query the
best move post-abortum, but this could also introduce some fuzzy
semantics.
I could live with the following dialog
<1 genmove B
<2 abort
>?1 aborted
<=2 abort successful
<3 report_post_abortum_move
>=3 B E4
<4 play B E4
=4 Ok
Of course, both abort and report_post_abortum_move are optional
commands are optional and their presence can be verified with
the valid_command command.
For the report_post_abortum_move command, IMO it should either
succeed and report exactly one move, or fail with an appropriate
error message. ("no move available" or "not implemented" seem to
be the only plausible errors)
WRT the atomicity of other commands than genmove[Andrew Balsa]:
IMO, We cannot implicitly expect implementations to support full undo.
Allowing abortion of *any* command(reset,boardsize,quit, undo ...)
without full undo would make no sense. The engine would be left
in an undetermined state, and a reset/boardsize command would be
the only way out, anyway.
If the command (boardsize, quit, undo) cannot be undone, the cheapest
way for the implementation would be to just ignore the abort
, execute the command anyway, report it's success or error,
and report "nothing to abort" or "can't abort" for the abort command.
This will allow the driver program to blindly trust the
error/success reply.
(most commands will have completed execution before seeing the
abort command, so the abort would fail, anyway)
AvK
[View Less]
I think that this "getc/putc works with single-character abort" is not
really as big an advantage as it seems. getc/putc are simply a standard
1-character buffer on FILE *'s provided by posix. It would be about 10
lines to write a similar system that can buffer up entire lines, and
that will work equally as well with single-character aborts or "abort"
as a text command. The advantages of a readable text "abort" outweigh
the simuler code in POSIX systems for single character aborts in my
opinion.…
[View More] Although I admit, now we're getting into opinions rather than
facts, so it is harder to really argue either side of this.
One thing I haven't heard discussed, does anybody really dislike having
an optional "abort" command? Or is everybody OK with adding a section to
the spec that says "If you support aborting commands, this is the way to
do it"?
On Tue, 2002-10-29 at 03:00, Markus Enzenberger wrote:
the single character solution leaves the possibility open for a simple
implementation on POSIX systems avoiding the need for a thread.
You could use select, getc and putc to poll at certain times during your
lengthy command and do a blocking read when waiting for the next real
command. Putc allows you to put back only one character.
(It does not work if there is still something in stdio's buffer,
but this is no problem, since interrupting won't be used in batch mode
anyway).
[View Less]
Marco Scheurer wrote:
> makes me wonder if other optional commands would not be better:
> start_thinking
> stop_thinking
> best_move
I agree: once we allow "asynchronous" processing of commands,
this wil be as easy as the abort-feature.
It could have been the interface to the engine.
At the moment, it is not.
To allow simple implementations to talk GTP, we need
the protocol to be "one line at a time" and blocking.
The "abort" feature is an exception. It is optional,
but we still …
[View More]should try to agree on it's semantics.
BTW it is not forbidden to think in the opponebt's time.
As long as it responds to the GTP dialog, the program
may do anything it chooses to, given it maintains the
necessary board state between moves/commands.
Implementing start_thinking/stop_thinking/best_move in terms
of genmove, abort, report_post_abortum_move is left as an
exercise to the reader ... (hint: maybe built-in aliases/macro's
would help :-)
AvK
[View Less]