Use a timer to avoid polling

The timer will be fired asynchronously to handle
time management flags, while other threads are
searching.

This implementation uses a thread waiting on a
timed condition variable instead of real timers.
This approach allow to reduce platform dependant
code to a minimum and also is the most portable given
that timers libraries are very different among platforms
and also the best ones are not compatible with olds
Windows.

Also retire the now unused polling code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
This commit is contained in:
Marco Costalba
2011-11-05 11:19:21 +01:00
parent 0095f423f2
commit d58176bfea
8 changed files with 149 additions and 96 deletions

View File

@@ -19,6 +19,7 @@
#include <iostream>
#include "search.h"
#include "thread.h"
#include "ucioption.h"
@@ -28,7 +29,8 @@ namespace { extern "C" {
// start_routine() is the C function which is called when a new thread
// is launched. It simply calls idle_loop() of the supplied thread. The
// last thread is dedicated to I/O and so runs in listener_loop().
// last two threads are dedicated to read input from GUI and to mimic a
// timer, so they run in listener_loop() and timer_loop() respectively.
#if defined(_MSC_VER)
DWORD WINAPI start_routine(LPVOID thread) {
@@ -38,6 +40,9 @@ namespace { extern "C" {
if (((Thread*)thread)->threadID == MAX_THREADS)
((Thread*)thread)->listener_loop();
else if (((Thread*)thread)->threadID == MAX_THREADS + 1)
((Thread*)thread)->timer_loop();
else
((Thread*)thread)->idle_loop(NULL);
@@ -149,7 +154,7 @@ void ThreadsManager::init() {
lock_init(&threadsLock);
// Initialize sleep and split point locks
for (int i = 0; i <= MAX_THREADS; i++)
for (int i = 0; i < MAX_THREADS + 2; i++)
{
lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond);
@@ -165,7 +170,7 @@ void ThreadsManager::init() {
// Create and launch all the threads but the main that is already running,
// threads will go immediately to sleep.
for (int i = 1; i <= MAX_THREADS; i++)
for (int i = 1; i < MAX_THREADS + 2; i++)
{
threads[i].is_searching = false;
threads[i].threadID = i;
@@ -190,7 +195,7 @@ void ThreadsManager::init() {
void ThreadsManager::exit() {
for (int i = 0; i <= MAX_THREADS; i++)
for (int i = 0; i < MAX_THREADS + 2; i++)
{
if (i != 0)
{
@@ -349,10 +354,39 @@ template Value ThreadsManager::split<false>(Position&, SearchStack*, Value, Valu
template Value ThreadsManager::split<true>(Position&, SearchStack*, Value, Value, Value, Depth, Move, int, MovePicker*, int);
// Thread::listner_loop() is where the last thread, used for IO, waits for input.
// Input is read in sync with main thread (that blocks) when is_searching is set
// to false, otherwise IO thread reads any input asynchronously and processes
// the input line calling do_uci_async_cmd().
// Thread::timer_loop() is where the timer thread waits maxPly milliseconds
// and then calls do_timer_event().
void Thread::timer_loop() {
while (!do_terminate)
{
lock_grab(&sleepLock);
timed_wait(&sleepCond, &sleepLock, maxPly ? maxPly : INT_MAX);
lock_release(&sleepLock);
do_timer_event();
}
}
// ThreadsManager::set_timer() is used to set the timer to trigger after msec
// milliseconds. If msec is 0 then timer is stopped.
void ThreadsManager::set_timer(int msec) {
Thread& timer = threads[MAX_THREADS + 1];
lock_grab(&timer.sleepLock);
timer.maxPly = msec;
cond_signal(&timer.sleepCond); // Wake up and restart the timer
lock_release(&timer.sleepLock);
}
// Thread::listener_loop() is where the listener thread, used for I/O, waits for
// input. When is_searching is false then input is read in sync with main thread
// (that blocks), otherwise the listener thread reads any input asynchronously
// and processes the input line calling do_uci_async_cmd().
void Thread::listener_loop() {
@@ -390,7 +424,7 @@ void Thread::listener_loop() {
if (cmd == "quit")
is_searching = false;
Threads.do_uci_async_cmd(cmd);
do_uci_async_cmd(cmd);
cmd = ""; // Input has been consumed
}