Another question about a problem which has me tearing my hair out.
I've been trying to implement two threads, one for input and one for thinking.
The idea is this: The main() function calls process_uci(), in which a function is called init_threads() that starts a thread in a compute loop (all will be shown below)
So, I end up with the main thread getting input, and the compute thread waiting for an event called "thinkEvent" to be signalled starting the search. As soon as the signal comes, th think thread resets the thinkEvent.
Problem is, when I start the search (from main thread SetEvent(thinkEvent) )
and at some point enter "stop", all is well, and the the think thread pauses waiting to be signalled again.
However, if I leave the search to time out from normal tc play, it ignores the waitforevent().
I hope this makes sense...
Here are the functions (excuse the mess)
Code: Select all
/*
called from main(), computing thread started here
*/
void process_uci()
{
setbuf(stdout, NULL);
setbuf(stdout, NULL);
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
string input;
init_threads(); // --> thread init
initucioptions();
sayversion();
sayucioptions();
cout<<"uciok"<<endl;
int pos;
while(getline(cin, input))
{
fflush(stdout);
EnterCriticalSection(&critid);
if(islog()) logger.file<<"\nUCI COMMAND IN : "<<input<<endl;
LeaveCriticalSection(&critid);
if(findsubstr("uci", input, pos))
{
sayversion();
sayucioptions();
cout<<"uciok"<<endl;
continue;
}
else if(findsubstr("isready", input, pos))
{
cout<<"readyok"<<endl;
continue;
}
else if(findsubstr("quit", input, pos))
{
EnterCriticalSection(&critid);
tree->status->quit_called = true;
LeaveCriticalSection(&critid);
//now signal the event so the search thread picks up the quit
SetEvent(thinkEvent);
break;
}
else if(findsubstr("ucinewgame", input, pos))
{
continue;
}
else if(findsubstr("position", input, pos))
{
uciposition(input);
continue;
}
else if(findsubstr("setoption", input, pos))
{
setucioptions(input);
continue;
}
else if(findsubstr("go", input, pos))
{
uci_go(input);
continue;
}
else if(findsubstr("stop", input, pos))
{
send_stop();
continue;
}
else if(findsubstr("ponderhit", input, pos))
{
EnterCriticalSection(&critid);
tree->param->ponderhit = true;
LeaveCriticalSection(&critid);
continue;
}
else if(findsubstr("eval", input, pos))
{
cout<<"\n eval score: "<<eval();
cout<<endl;
continue;
}
else if(findsubstr("print", input, pos))
{
printboard();
cout<<endl;
continue;
}
else
{
cout<<"unknown command "<<input<<endl;
continue;
}
}
//cout<<"waiting for search thread to stop"<<endl;
WaitForSingleObject(thredid, INFINITE);
// cout<<" done, now quitting"<<endl;
}
/*
please ignore "stopThinkEvent". it's another event used for xboard mode pondering hacking I am trying to do :)
*/
void send_stop()
{
EnterCriticalSection(&critid);
if(islog())logger.file<<"Input Thread setting stop "<<endl;
tree->status->interrupted = true;
LeaveCriticalSection(&critid);
WaitForSingleObject(stopThinkEvent, INFINITE); // wait for stopThink to be signalled
ResetEvent(stopThinkEvent);
}
void init_threads()
{
thinkEvent = CreateEvent(NULL,TRUE,FALSE,TEXT("thinkEvent Created\n"));
stopThinkEvent = CreateEvent(NULL,TRUE,FALSE,TEXT("stopThinkEvent Created\n"));
if(thinkEvent==NULL)printf( "thinkEvent() failed.\n" );
InitializeCriticalSectionAndSpinCount(&critid,0);
thredid = (HANDLE)_beginthread( thread_compute, 0, (void*)12 );
}
/*
here, last action is to signal thinkEvent
*/
void uci_go(string command)
{
init_go();
string input;
int pos;
if(findsubstr("depth", command, pos))
{
setdepth(strtouint(command, pos+6));
setmodetpm(false);
setmodemtg(false);
setdepthlimit(true);
#ifdef DEBUG
cout<<" set depth to "<<GETDEPTH<<endl;
#endif
}
if(findsubstr("movetime", command, pos))
{
settimepermove(strtouint(command, pos+9));
setmodetpm(true);
setmodemtg(false);
setdepthlimit(false);
setdepth(maxply);
}
if(findsubstr("infinite", command, pos))
{
setmode(smINFINITE);
}
if(findsubstr("wtime", command, pos))
{
setmovetime(strtoint(command, pos+6), cW);
setmodetpm(false);
setdepth(maxply);
}
if(findsubstr("btime", command, pos))
{
setmovetime(strtoint(command, pos+6), cB);
setmodetpm(false);
setdepth(maxply);
}
if(findsubstr("winc", command, pos))
{
setinc(strtoint(command, pos+5),cW);
}
if(findsubstr("binc", command, pos))
{
setinc(strtoint(command, pos+5),cW);
}
if(findsubstr("ponder", command, pos))
{
setmode(smPONDER);
}
if(findsubstr("movestogo", command, pos))
{
setmovestogo(strtoint(command, pos+10),SIDENOW);
setmodemps(strtoint(command, pos+10));
}
if(islog())
{
logger.file <<"Jabba entering think \nmovestogo "<<GETMTG(cW)<<"(w) "<<GETMTG(cB)<<"(b)";
logger.file <<"\ndepth "<<GETDEPTH;
logger.file <<"\nwtime "<<GETMOVETIME(cW);
logger.file <<"\nbtime "<<GETMOVETIME(cB);
logger.file <<"\nmovetime "<<GETTPM<<"\n";
}
SetEvent(thinkEvent); //kick the think thread into gear
}
/*
here, the idea is that thinkEvent has been signalled in the uci_go(), the think thred finishes waiting, resets the event, and when the loop comes around after thinking, it's waiting again due to the reset. This works if there is a manual intervaention in the think (e.g enter stop or quit) but not if the search times out on normal tc play
*/
void thread_compute(void *) //runs parallel with input thread
{
//reset quit variable
EnterCriticalSection(&critid);
tree->status->quit_called = false;
LeaveCriticalSection(&critid);
bool leave = false;
uint best;
log_options();
for(;;) //infinite loop where the search thread resides, until quit has been set to true by the main thread
{
//if(islog())logger.file<<"Think Thread waiting"<<endl;
cout<<"Think Thread waiting "<<endl;
WaitForSingleObject(thinkEvent, INFINITE); // start thinking signal
ResetEvent(thinkEvent);
//if(islog())logger.file<<"Think Thread go"<<endl;
cout<<"Think Thread go"<<endl;
EnterCriticalSection(&critid);
if(tree->status->quit_called)
{
cout<<"Quit seen "<<endl;
leave = true;
}
logboard();
LeaveCriticalSection(&critid);
if(leave)
{
cout<<"leave is set, breaking "<<endl;
break;
}
startsearchtimer(SIDENOW, tree->status->mode, tree->param->ponderhit);
resetdata();
preparerootlist();//also limits depth in the case of one legal move
// EnterCriticalSection(&critid);
// if(islog())logger.file<<"Think Thread going to iterdeep "<<endl;
cout<<"\nThink Thread going to iterdeep "<<endl;
// LeaveCriticalSection(&critid);
iter_deep(); //iterative deepening loop
if(!tree->param->tuning) ttablesays_stats();
if(!tree->param->tuning) mlistsays_stats();
if(!tree->param->tuning) printstats();
// EnterCriticalSection(&critid);
// if(islog())logger.file<<"Think Thread back from iterdeep "<<endl;
cout<<"Think Thread back from iterdeep, saying best "<<endl;
// LeaveCriticalSection(&critid);
/*
could be back now due to either a stop intervention or timeout
*/
say_best();
if(tree->xb && !tree->status->interrupted && engopt.ponder) // if we've not been interrupted, start pondering in xbmode
xb_ponder();
cout<<"\nbottom of loop compute() .. "<<endl;
// printboard();
}
cout<<"\n out of think thread loop ";
}
Thanks for any help.
Richard
