handling the UCI "stop" command

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

handling the UCI "stop" command

Post by lucasart »

As specified by the UCI protocol, an engine needs to respond to the stop command at anytime. In particular, when it is searching, or analyzing.

I haven't coded that yet, but basically I need to check whether there is something in the stdin, without waiting for input (so a simple fgets won't do it).

I see in the code of Fruit 2.1 the following function, which basically does just that

Code: Select all

bool input_available() {



#if defined(_WIN32) || defined(_WIN64)



   static bool init = false, is_pipe;

   static HANDLE stdin_h;

   DWORD val, error;



   // val = 0; // needed to make the compiler happy?



   // have a look at the "local" buffer first, *this time before init (no idea if it helps)*



   if (UseDebug && !init) printf("info string init=%d stdin->_cnt=%d\n",int(init),stdin->_cnt);



   if (stdin->_cnt > 0) return true; // HACK: assumes FILE internals



   // input init (only done once)



   if (!init) {



      init = true;



      stdin_h = GetStdHandle(STD_INPUT_HANDLE);



      if (UseDebug && (stdin_h == NULL || stdin_h == INVALID_HANDLE_VALUE)) {

         error = GetLastError();

         printf("info string GetStdHandle() failed, error=%d\n",error);

      }



      is_pipe = !GetConsoleMode(stdin_h,&val); // HACK: assumes pipe on failure



      if (UseDebug) printf("info string init=%d is_pipe=%d\n",int(init),int(is_pipe));



      if (UseDebug && is_pipe) { // GetConsoleMode() failed, everybody assumes pipe then

         error = GetLastError();

         printf("info string GetConsoleMode() failed, error=%d\n",error);

      }



      if (!is_pipe) {

         SetConsoleMode(stdin_h,val&~(ENABLE_MOUSE_INPUT|ENABLE_WINDOW_INPUT));

         FlushConsoleInputBuffer(stdin_h); // no idea if we can lose data doing this

      }

   }



   // different polling depending on input type

   // does this code work at all for pipes?



   if (is_pipe) {



      if (!PeekNamedPipe(stdin_h,NULL,0,NULL,&val,NULL)) {



         if (UseDebug) { // PeekNamedPipe() failed, everybody assumes EOF then

            error = GetLastError();

            printf("info string PeekNamedPipe() failed, error=%d\n",error);

         }



         return true; // HACK: assumes EOF on failure

      }



      if &#40;UseDebug && val < 0&#41; printf&#40;"info string PeekNamedPipe&#40;)&#58; val=%d\n",val&#41;;



      return val > 0; // != 0???



   &#125; else &#123;



      GetNumberOfConsoleInputEvents&#40;stdin_h,&val&#41;;

      return val > 1; // no idea why 1

   &#125;



   return false;



#else // assume POSIX



   int val;

   fd_set set&#91;1&#93;;

   struct timeval time_val&#91;1&#93;;



   FD_ZERO&#40;set&#41;;

   FD_SET&#40;STDIN_FILENO,set&#41;;



   time_val->tv_sec = 0;

   time_val->tv_usec = 0;



   val = select&#40;STDIN_FILENO+1,set,NULL,NULL,time_val&#41;;

   if &#40;val == -1 && errno != EINTR&#41; &#123;

      my_fatal&#40;"input_available&#40;)&#58; select&#40;)&#58; %s\n",strerror&#40;errno&#41;);

   &#125;



   return val > 0;



#endif

&#125;

Is there no way to write a simpler, and more importantly portable (ISO C99) version of this ? (I'm coding in C)
Joost Buijs
Posts: 1568
Joined: Thu Jul 16, 2009 10:47 am
Location: Almere, The Netherlands

Re: handling the UCI "stop" command

Post by Joost Buijs »

Under Windows I use the following and that works fine. It's a simplified part of my UCI protocol. For other systems I have no clue.

Code: Select all

class UCI
&#123;
public&#58;
  UCI&#40;void&#41;;
  ~UCI&#40;void&#41;;
  bool CheckInput&#40;);

private&#58;
  lock_t lock;
  HANDLE hStdin;
  HANDLE hStdout;
  bool bPipe;
&#125;

UCI&#58;&#58;UCI&#40;void&#41;
&#123;
  LockInit&#40;lock&#41;;
  hStdin = GetStdHandle&#40;STD_INPUT_HANDLE&#41;;
  hStdout = GetStdHandle&#40;STD_OUTPUT_HANDLE&#41;;
  bPipe = &#40;GetFileType&#40;hStdin&#41; == FILE_TYPE_PIPE&#41;;
  if &#40;bPipe&#41; &#123;
    setvbuf&#40;stdin, NULL, _IONBF, 0&#41;;
    setvbuf&#40;stdout, NULL, _IONBF, 0&#41;;
  &#125;
  fflush&#40;NULL&#41;;
&#125;

UCI&#58;&#58;~UCI&#40;void&#41;
&#123;
  LockDelete&#40;lock&#41;;
&#125;

bool UCI&#58;&#58;CheckInput&#40;void&#41;
&#123;
  // anonymous pipe
  if &#40;bPipe&#41; &#123;
    DWORD dwAvail = 0;
    if &#40;PeekNamedPipe&#40;hStdin, NULL, 0, NULL, &dwAvail, NULL&#41; &&
       dwAvail > 0&#41; return true;
  &#125;
  // console
  else if (_kbhit&#40;)) return true;

  return false;
&#125;
kongsian
Posts: 46
Joined: Thu Jun 15, 2006 11:21 am

Re: handling the UCI "stop" command

Post by kongsian »

Instead of going this route, I would recommend using a thread to handle the UCI protocol and other thread(s) for the search. Your resulting code would be much cleaner and more portable.

Rgds.
Kong Sian
Joost Buijs
Posts: 1568
Joined: Thu Jul 16, 2009 10:47 am
Location: Almere, The Netherlands

Re: handling the UCI "stop" command

Post by Joost Buijs »

Of course you can use a thread that constantly sits waiting for input instead of polling. Probably the code will be somewhat cleaner, but I don't see why it would be more portable.
User avatar
michiguel
Posts: 6401
Joined: Thu Mar 09, 2006 8:30 pm
Location: Chicago, Illinois, USA

Re: handling the UCI "stop" command

Post by michiguel »

Joost Buijs wrote:Of course you can use a thread that constantly sits waiting for input instead of polling. Probably the code will be somewhat cleaner, but I don't see why it would be more portable.
Technically, it may not be more portable, but I think it would be easier to handle the portability issues. It is easy to encapsulate, say, the functions to create and destroy threads with a wrapper.

I always did it this way (more than 10 years now) and I am really happy with that early decision.

Miguel
tpetzke
Posts: 686
Joined: Thu Mar 03, 2011 4:57 pm
Location: Germany

Re: handling the UCI "stop" command

Post by tpetzke »

I tried both using the PeekNamedPipe and the Thread option.

I had some problems with PeekNamedPipe. Sometimes the engine locked itself because it thought input was present while no input was there, so a call to read the input locked the engine. I assume it received a Window Event (like window is getting focus) and although I specified that I do not want those events it still received them.

I removed that code and implemented a thread instead. It locks until input is present and then sets a"input is present" flag for the main thread so that the main thread can catch the input. The main thread just has to poll a boolean flag now.

Save and clean.

Thomas...
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: handling the UCI "stop" command

Post by lucasart »

tpetzke wrote:I tried both using the PeekNamedPipe and the Thread option.

I had some problems with PeekNamedPipe. Sometimes the engine locked itself because it thought input was present while no input was there, so a call to read the input locked the engine. I assume it received a Window Event (like window is getting focus) and although I specified that I do not want those events it still received them.

I removed that code and implemented a thread instead. It locks until input is present and then sets a"input is present" flag for the main thread so that the main thread can catch the input. The main thread just has to poll a boolean flag now.

Save and clean.

Thomas...
Actually POSIX (linux, free bsd, mac os etc.) is much simpler than Windows for that. Here's my code:

Code: Select all

int input_available&#40;)
&#123;
	fd_set readfds;
	struct timeval timeout;

	FD_ZERO&#40;&readfds&#41;;
	FD_SET&#40;0, &readfds&#41;;	// fd 0 is always stdin
	timeout.tv_sec = timeout.tv_usec = 0;
	
	select&#40;1, &readfds, 0, 0, &timeout&#41;;
	return &#40;FD_ISSET&#40;0, &readfds&#41;);
&#125;
I might have a look at how to do it right for Windows one day. But for now I'll do a POSIX only version, as I am developping under GNU/Linux.

I was naively hoping to find a portable way of doing this. But it seems that this kind of thing is not standard I/O covered by any of the standard C functions, so any solution has to be plateform dependant and based on system calls :-(