How does input console for UCI work ?

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

OneTrickPony
Posts: 157
Joined: Tue Apr 30, 2013 1:29 am

How does input console for UCI work ?

Post by OneTrickPony »

Hi,

Specifically I mean how it works in Robbolito but all general comments are welcome as well as I am completely new to this.
I thought I would need to create a separate thread for an engine and read the input in the main thread to allow for responsiveness while the engine is searching but I don't see such thing in Robbo code. Still it works somehow.
Here is main loop in Robbo:

Code: Select all

    while( 1 )
        input_console();
    return 0;
and input_console looks like this:

Code: Select all

void input_console()
    {
    char string[65536];
    fgets(string, 65536, stdin);
    string[strlen(string) - 1] = 0;
    parse(string);
    }
but fgets is a blocking call so for search to continue it must be in separate thread (right?). The problem is I don't see where that thread is created. Grepping for _beginthread or _beginthreadex doesn't bring any results and reading through search_initialization (which is called after "go" command is inputted) I can't see any thread creation either.
I am probably missing something obvious. Help would be greatly appreciated.
User avatar
hgm
Posts: 27790
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: How does input console for UCI work ?

Post by hgm »

I don't know about Robbolito, but my single-threaded engines also use blocking input calls. The trick is to only use them when you know there is input (that is, when you have other things to do, like searching), so that they don't actually block. E.g. in Windows use PeekNamedPipe to poll for input in a non-blocking way.
Edmund
Posts: 670
Joined: Mon Dec 03, 2007 3:01 pm
Location: Barcelona, Spain

Re: How does input console for UCI work ?

Post by Edmund »

Also no idea about Robbolito.

I agree with HGM on the non blocking input checking part.
See for example: https://chessprogramming.wikispaces.com/CPW-Engine_com

On another note, there is also the command CreateThread to start a thread:
WINBASEAPI
__out_opt
HANDLE
WINAPI
CreateThread(
__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in SIZE_T dwStackSize,
__in LPTHREAD_START_ROUTINE lpStartAddress,
__in_opt __deref __drv_aliasesMem LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out_opt LPDWORD lpThreadId
);

CreateThread(0, 0, (LPTHREAD_START_ROUTINE) com_parser, 0, 0, 0);
OneTrickPony
Posts: 157
Joined: Tue Apr 30, 2013 1:29 am

Re: How does input console for UCI work ?

Post by OneTrickPony »

So what we do is check every time_tick if there is something on stdin, if so we call fgets routine, if not we continue with search ?
I worry that doing it this way I will have to pollute my code with stdin checks all over the place. That wouldn't be ideal as I would like my engine to be separate entity which can work without UCI-like interface as well (so I script for it or write an API with functionality to call search(20 seconds) for example).

What I mean is that if there is some state like ANALYSIS_MODE for which engine checks during searching I can provide this state as well without relying on stdin/stdout interface but if I insert stdin checks everywhere then my code will only be usable in UCI mode (only as separate running process which communicates via stdin/stdout).

Also I would really like to confine all Windows specific calls into one place (so it's easy to replace that one file for other OS'es) and keep rest of the code in pure C.


Is there any way around it or maybe I am missing something ?
User avatar
hgm
Posts: 27790
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: How does input console for UCI work ?

Post by hgm »

OneTrickPony wrote:So what we do is check every time_tick if there is something on stdin, if so we call fgets routine, if not we continue with search ?
I worry that doing it this way I will have to pollute my code with stdin checks all over the place.
In my engines I check in only one place, which is the same place as where I check the time if the engine is thinking. But of course I only have a single Search() routine, and not zillions of versions for white, black, pv-, cut-, all-nodes, QS, etc. But I guess it would be enough to only check for time or input in two of them (e.g. white and black cut nodes), as it will never take long until you get to one of those. You want to make such check only once every 1000 nodes or so anyway, as they are relatively expensive, and you won't need sub-millisecond response to input. (You won't get that anyway, even if your program would heve instant response, due to system overhead.)
That wouldn't be ideal as I would like my engine to be separate entity which can work without UCI-like interface as well (so I script for it or write an API with functionality to call search(20 seconds) for example).
I don't see why that would be a problem. If you have a call to TerminationCheck() in a few critical places, which sets an abortFlag to unwind the search depending in time, pending input and mode, you could simply define a mode that would make it return immediately in situations where you want non-abortable search.
Also I would really like to confine all Windows specific calls into one place (so it's easy to replace that one file for other OS'es) and keep rest of the code in pure C.
Well, so define subroutines ReadClock() and InputPending() in the platform-dependent sections, and call those from TerminationCheck(). You could look in the HaChu source code to see an example.
kbhearn
Posts: 411
Joined: Thu Dec 30, 2010 4:48 am

Re: How does input console for UCI work ?

Post by kbhearn »

you can of course do what you originally intended and keep the engine itself in separate threads than the input routines and just have stop() and waitforstop() routines to sync with it when you have new instructions from the input stream. It's not like a blocked input thread consumes any significant amount of cpu time.
kbhearn
Posts: 411
Joined: Thu Dec 30, 2010 4:48 am

Re: How does input console for UCI work ?

Post by kbhearn »

re: robbolito's source, it does indeed use select() to check if there's input during the search. when it calls risolvere_termine at a couple points in search it uses interrogativo_ingresso to check for input (which calls select) and while that function says there's more input on the commandline it calls ingresso which is the function you're already familiar with as the internal of the main loop.
OneTrickPony
Posts: 157
Joined: Tue Apr 30, 2013 1:29 am

Re: How does input console for UCI work ?

Post by OneTrickPony »

Thanks, this is helpful.
I guess I am just too afraid of polluting "pure" code with interface code. Preferably I would call a function "run this other function for 10ms and go back here saving the state so you could go back" so my engine code wouldn't even know the interface code exists.
Not being able to do that I am going to use your suggestions though :)
OneTrickPony
Posts: 157
Joined: Tue Apr 30, 2013 1:29 am

Re: How does input console for UCI work ?

Post by OneTrickPony »

Could you point me to the file name ?
The source I've found is already translated to English and grepping for the names you provided (or for select) brings no results.
I am really curious about this mechanism as I can't find a point where it checks for input (maybe it's in some macro ?).
User avatar
velmarin
Posts: 1600
Joined: Mon Feb 21, 2011 9:48 am

Re: How does input console for UCI work ?

Post by velmarin »

You should find it in utils.c or utilita.c,
The call is made from time.c

Code: Select all

bool question_input()
{
	static int init = 0, is_pipe;
	static HANDLE stdin_h;
	DWORD val;

	if (stdin->_cnt > 0)
		return 1;

	if (!init)
	{
		init = 1;
		stdin_h = GetStdHandle(STD_INPUT_HANDLE);
		is_pipe = !GetConsoleMode(stdin_h, &val);

		if (!is_pipe)
		{
			SetConsoleMode(stdin_h, val & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
			FlushConsoleInputBuffer(stdin_h);
		}
	}

	if (is_pipe)
	{
		if (!PeekNamedPipe(stdin_h, NULL, 0, NULL, &val, NULL))
			return 1;
		return val > 0;
	}
	else
	{
		GetNumberOfConsoleInputEvents(stdin_h, &val);
		return val > 1;
	}
	return 0;
}