/*
Protector -- a UCI chess engine
Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "coordination.h"
#include "protector.h"
#include "search.h"
#include "matesearch.h"
#include "io.h"
#include "hash.h"
#include <stdio.h>
#include <assert.h>
#include <pthread.h>
#include <time.h>
/* #define DEBUG_COORDINATION */
pthread_t searchThread[MAX_THREADS];
pthread_t timer;
static pthread_mutex_t guiSearchMutex = PTHREAD_MUTEX_INITIALIZER;
long searchThreadId[MAX_THREADS];
static int numThreads = 1;
static SearchTask dummyTask;
static SearchTask *currentTask = &dummyTask;
static Variation variations[MAX_THREADS];
static Hashtable localHashtable;
static PawnHashInfo pawnHashtable[MAX_THREADS][PAWN_HASHTABLE_SIZE];
int setNumberOfThreads(int _numThreads)
{
numThreads = max(1, min(MAX_THREADS, _numThreads));
return numThreads;
}
int getNumberOfThreads()
{
return numThreads;
}
UINT64 getNodeCount(void)
{
int threadCount;
UINT64 sum = 0;
for (threadCount = 0; threadCount < numThreads; threadCount++)
{
sum += variations[threadCount].nodes;
}
return sum;
}
Variation *getCurrentVariation()
{
return &variations[0];
}
void getGuiSearchMutex(void)
{
#ifdef DEBUG_COORDINATION
logDebug("aquiring search lock...\n");
#endif
pthread_mutex_lock(&guiSearchMutex);
#ifdef DEBUG_COORDINATION
logDebug("search lock aquired...\n");
#endif
}
void releaseGuiSearchMutex(void)
{
pthread_mutex_unlock(&guiSearchMutex);
#ifdef DEBUG_COORDINATION
logDebug("search lock released...\n");
#endif
}
static int startSearch(Variation * currentVariation)
{
currentVariation->searchStatus = SEARCH_STATUS_RUNNING;
switch (currentTask->type)
{
case TASKTYPE_BEST_MOVE:
currentTask->bestMove = search(currentVariation, NULL);
break;
case TASKTYPE_TEST_BEST_MOVE:
currentTask->bestMove =
search(currentVariation, ¤tTask->solutions);
break;
case TASKTYPE_MATE_IN_N:
searchForMate(currentVariation,
¤tTask->calculatedSolutions,
currentTask->numberOfMoves);
break;
case TASKTYPE_TEST_MATE_IN_N:
searchForMate(currentVariation,
¤tTask->calculatedSolutions,
currentTask->numberOfMoves);
break;
default:
break;
}
currentTask->nodes = getNodeCount();
if (currentVariation->threadNumber == 0)
{
int threadCount;
for (threadCount = 1; threadCount < numThreads; threadCount++)
{
variations[threadCount].terminate = TRUE;
}
pthread_cancel(timer);
}
#ifdef DEBUG_COORDINATION
logDebug("Search thread terminated.\n");
#endif
getGuiSearchMutex();
currentVariation->searchStatus = SEARCH_STATUS_FINISHED;
releaseGuiSearchMutex();
return 0;
}
static void *executeSearch(void *arg)
{
Variation *currentVariation = arg;
startSearch(currentVariation);
return 0;
}
static void *watchTime(void *arg)
{
Variation *currentVariation = arg;
long timeLimit = currentVariation->timeLimit;
struct timespec requested, remaining;
int result;
requested.tv_sec = timeLimit / 1000;
requested.tv_nsec = 1000000 * (timeLimit - 1000 * requested.tv_sec);
/* logReport("### Timer thread working sec=%ld nsec=%ld ###\n",
requested.tv_sec, requested.tv_nsec); */
result = nanosleep(&requested, &remaining);
if (result != -1)
{
getGuiSearchMutex();
prepareSearchAbort();
releaseGuiSearchMutex();
}
return 0;
}
void scheduleTask(SearchTask * task)
{
const unsigned long startTime = getTimestamp();
int threadCount;
localHashtable.entriesUsed = 0;
if (task->variation->timeLimit > 0)
{
if (pthread_create(&timer, NULL, &watchTime, task->variation) == 0)
{
#ifdef DEBUG_COORDINATION
logDebug("Timer thread started.\n");
#endif
}
else
{
logDebug("### Timer thread could not be started. ###\n");
exit(EXIT_FAILURE);
}
}
for (threadCount = 0; threadCount < numThreads; threadCount++)
{
Variation *currentVariation = &variations[threadCount];
currentVariation->terminate = TRUE;
#ifdef DEBUG_COORDINATION
logDebug("Schedule task: Search abort signaled.\n");
#endif
currentTask = task;
*currentVariation = *(currentTask->variation);
currentVariation->searchStatus = SEARCH_STATUS_TERMINATE;
currentVariation->bestBaseMove = NO_MOVE;
currentVariation->terminate = FALSE;
currentVariation->hashtable = &localHashtable;
currentVariation->pawnHashtable = &(pawnHashtable[threadCount][0]);
currentVariation->kingsafetyHashtable =
&(kingSafetyHashtable[threadCount][0]);
currentVariation->threadNumber = threadCount;
currentVariation->startTime = startTime;
if (pthread_create(&searchThread[threadCount], NULL,
&executeSearch, currentVariation) == 0)
{
#ifdef DEBUG_COORDINATION
logDebug("Search thread %d started.\n", threadCount);
#endif
}
else
{
logDebug("### Search thread could not be started. ###\n");
exit(EXIT_FAILURE);
}
}
}
void waitForSearchTermination(void)
{
int threadCount;
bool finished;
int count = 0;
do
{
finished = TRUE;
if (count > 1000)
{
logDebug("waiting for search termination.\n");
count = 0;
}
for (threadCount = 0; threadCount < numThreads; threadCount++)
{
Variation *currentVariation = &variations[threadCount];
if (currentVariation->searchStatus != SEARCH_STATUS_FINISHED)
{
finished = FALSE;
if (searchThread[threadCount] != 0)
{
pthread_join(searchThread[threadCount], 0);
}
break;
}
else
{
searchThread[threadCount] = 0;
}
#ifdef DEBUG_COORDINATION
logDebug("Task %d finished.\n", threadCount);
#endif
}
count++;
}
while (finished == FALSE);
}
void completeTask(SearchTask * task)
{
scheduleTask(task);
#ifdef DEBUG_COORDINATION
logDebug("Task scheduled. Waiting for completion.\n");
#endif
waitForSearchTermination();
}
void prepareSearchAbort(void)
{
int threadCount;
const bool terminateImmediately =
FALSE == movesAreEqual(variations[0].bestBaseMove, NO_MOVE);
for (threadCount = 0; threadCount < numThreads; threadCount++)
{
variations[threadCount].terminate = TRUE;
if (terminateImmediately &&
variations[threadCount].searchStatus != SEARCH_STATUS_FINISHED)
{
variations[threadCount].searchStatus = SEARCH_STATUS_TERMINATE;
#ifdef DEBUG_COORDINATION
logDebug("terminating immediately\n");
#endif
}
}
}
void unsetPonderMode(void)
{
int threadCount;
for (threadCount = 0; threadCount < numThreads; threadCount++)
{
Variation *currentVariation = &variations[threadCount];
currentVariation->ponderMode = FALSE;
}
}
void setTimeLimit(unsigned long timeTarget, unsigned long timeLimit)
{
int threadCount;
for (threadCount = 0; threadCount < numThreads; threadCount++)
{
Variation *currentVariation = &variations[threadCount];
currentVariation->timeTarget = timeTarget;
currentVariation->timeLimit = timeLimit;
}
}
void setHashtableSizeInMb(unsigned int size)
{
UINT64 tablesize = 1024 * 1024 * (UINT64) size;
setHashtableSize(&localHashtable, tablesize);
resetHashtable(&localHashtable);
}
int initializeModuleCoordination()
{
int threadCount;
initializeHashtable(&localHashtable);
setHashtableSize(&localHashtable, 16 * 1024 * 1024);
resetHashtable(&localHashtable);
for (threadCount = 0; threadCount < MAX_THREADS; threadCount++)
{
Variation *currentVariation = &variations[threadCount];
currentVariation->searchStatus = SEARCH_STATUS_FINISHED;
}
return 0;
}
int testModuleCoordination()
{
return 0;
}