/*
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>
#ifndef _MSC_VER
#include <pthread.h>
#else // _MSC_VER
#include <process.h> // Pierre-Marie Baty -- Win32 threads support
__declspec(dllimport) void __stdcall Sleep (unsigned long Timeout); // Pierre-Marie Baty -- for Sleep()
#endif // !_MSC_VER
#include <time.h>
/* #define DEBUG_COORDINATION */
#ifndef _MSC_VER
#define THREAD_RETURN_TYPE void * // Pierre-Marie Baty -- multiplatform threads support
#define INVALID_THREAD_ID 0 // Pierre-Marie Baty -- multiplatform threads support
pthread_t searchThread[MAX_THREADS];
pthread_t timer;
static pthread_mutex_t guiSearchMutex = PTHREAD_MUTEX_INITIALIZER;
#else // _MSC_VER
#define THREAD_RETURN_TYPE unsigned int // Pierre-Marie Baty -- Win32 threads support
#define INVALID_THREAD_ID -1 // Pierre-Marie Baty -- Win32 threads support
uintptr_t searchThread[MAX_THREADS]; // Pierre-Marie Baty -- Win32 threads support
uintptr_t timer; // Pierre-Marie Baty -- Win32 threads support
static bool guiSearchMutex = false; // Pierre-Marie Baty -- Win32 threads support
bool terminate_timer = false; // Pierre-Marie Baty -- Win32 threads support
#endif // !_MSC_VER
long searchThreadId[MAX_THREADS];
static int numThreads = 1;
static SearchTask dummyTask;
static SearchTask *currentTask = &dummyTask;
static Variation variations[MAX_THREADS];
static Hashtable sharedHashtable;
static PawnHashInfo pawnHashtable[MAX_THREADS][PAWN_HASHTABLE_SIZE];
Hashtable *getSharedHashtable()
{
return &sharedHashtable;
}
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
#ifndef _MSC_VER
pthread_mutex_lock(&guiSearchMutex);
#else // _MSC_VER
while (guiSearchMutex)
Sleep (10); // allow context switching
guiSearchMutex = true; // Pierre-Marie Baty -- mutex support on Win32
#endif // !_MSC_VER
#ifdef DEBUG_COORDINATION
logDebug("search lock aquired...\n");
#endif
}
void releaseGuiSearchMutex(void)
{
#ifndef _MSC_VER
pthread_mutex_unlock(&guiSearchMutex);
#else // _MSC_VER
guiSearchMutex = false; // Pierre-Marie Baty -- mutex support on Win32
#endif // !_MSC_VER
#ifdef DEBUG_COORDINATION
logDebug("search lock released...\n");
#endif
}
static int startSearch(Variation * currentVariation)
{
currentVariation->searchStatus = SEARCH_STATUS_RUNNING;
#ifdef DEBUG_COORDINATION
logDebug("Search with thread #%d started.\n",
currentVariation->threadNumber);
#endif
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;
}
#ifndef _MSC_VER
pthread_cancel(timer);
#else // _MSC_VER
terminate_timer = true; // Pierre-Marie Baty -- Win32 threads support
#endif // !_MSC_VER
}
#ifdef DEBUG_COORDINATION
logDebug("Search thread #%d terminated.\n",
currentVariation->threadNumber);
#endif
currentVariation->searchStatus = SEARCH_STATUS_FINISHED;
return 0;
}
long getElapsedTime()
{
return getTimestamp() - variations[0].startTime;
}
static THREAD_RETURN_TYPE executeSearch(void *arg)
{
Variation *currentVariation = arg;
#ifdef _MSC_VER
currentVariation->finished = false; // Pierre-Marie Baty -- Win32 thread control
#endif // _MSC_VER
startSearch(currentVariation);
#ifdef _MSC_VER
currentVariation->finished = true; // Pierre-Marie Baty -- Win32 thread control
#endif // _MSC_VER
return 0;
}
static THREAD_RETURN_TYPE watchTime(void *arg)
{
Variation *currentVariation = arg;
long timeLimit = currentVariation->timeLimit;
struct timespec requested;// , remaining; // Pierre-Marie Baty -- unused variable
int result;
requested.tv_sec = timeLimit / 1000;
requested.tv_nsec = 1000000 * (timeLimit - 1000 * (long)requested.tv_sec); // Pierre-Marie Baty -- added type cast
/* logReport("### Timer thread working sec=%ld nsec=%ld ###\n",
requested.tv_sec, requested.tv_nsec); */
#ifndef _MSC_VER
result = nanosleep(&requested, &remaining);
if (result != -1)
#else // _MSC_VER
result
= (clock () * 1000 / CLOCKS_PER_SEC
); // in milliseconds
while (!terminate_timer
&& ((clock () * 1000 / CLOCKS_PER_SEC
) < result
+ timeLimit
))
Sleep (10); // allow context switching if necessary
if (!terminate_timer)
#endif // !_MSC_VER
{
getGuiSearchMutex();
prepareSearchAbort();
releaseGuiSearchMutex();
}
return 0;
}
void startTimerThread(SearchTask * task)
{
if (task->variation->timeLimit > 0 && task->variation->ponderMode == FALSE)
{
#ifndef _MSC_VER
if (pthread_create(&timer, NULL, &watchTime, task->variation) == 0)
#else // _MSC_VER
terminate_timer = false; // Pierre-Marie Baty -- Win32 threads support
if ((timer = _beginthread (watchTime, 0, task->variation)) != -1) // Pierre-Marie Baty -- Win32 threads support
#endif // !_MSC_VER
{
#ifdef DEBUG_COORDINATION
logDebug("Timer thread started.\n");
#endif
}
else
{
logDebug("### Timer thread could not be started. ###\n");
}
}
}
void scheduleTask(SearchTask * task)
{
const unsigned long startTime = getTimestamp();
int threadCount;
sharedHashtable.entriesUsed = 0;
startTimerThread(task);
for (threadCount = 0; threadCount < numThreads; threadCount++)
{
Variation *currentVariation = &variations[threadCount];
currentTask = task;
*currentVariation = *(currentTask->variation);
currentVariation->searchStatus = SEARCH_STATUS_TERMINATE;
currentVariation->bestBaseMove = NO_MOVE;
currentVariation->terminate = FALSE;
currentVariation->pawnHashtable = &(pawnHashtable[threadCount][0]);
currentVariation->kingsafetyHashtable =
&(kingSafetyHashtable[threadCount][0]);
currentVariation->threadNumber = threadCount;
currentVariation->startTime = startTime;
#ifndef _MSC_VER
if (pthread_create(&searchThread[threadCount], NULL,
&executeSearch, currentVariation) == 0)
#else // _MSC_VER
if ((searchThread[threadCount] = _beginthread(executeSearch, 0, currentVariation)) != -1) // Pierre-Marie Baty -- Win32 threads support
#endif // !_MSC_VER
{
#ifdef DEBUG_COORDINATION
logDebug("Search thread #%d created.\n", threadCount);
#endif
}
else
{
logDebug("### Search thread #%d could not be started. ###\n",
threadCount);
}
}
}
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)
{
if (searchThread[threadCount] != INVALID_THREAD_ID)
{
#ifndef _MSC_VER
const int result = pthread_join (searchThread[threadCount], 0);
if (result == 0)
{
searchThread[threadCount] = INVALID_THREAD_ID;
}
else
{
finished = FALSE;
}
#else // _MSC_VER
while (!currentVariation->finished)
Sleep (10); // allow context switching
searchThread[threadCount] = INVALID_THREAD_ID;
#endif // !_MSC_VER
}
}
else
{
searchThread[threadCount] = INVALID_THREAD_ID;
}
#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;
for (threadCount = 0; threadCount < numThreads; threadCount++)
{
variations[threadCount].terminate = TRUE;
}
}
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;
}
}*/ // Pierre-Marie Baty -- unused function?
void setHashtableSizeInMb(unsigned int size)
{
UINT64 tablesize = 1024 * 1024 * (UINT64) size;
setHashtableSize(&sharedHashtable, tablesize);
resetHashtable(&sharedHashtable);
}
int initializeModuleCoordination()
{
int threadCount;
initializeHashtable(&sharedHashtable);
setHashtableSize(&sharedHashtable, 16 * 1024 * 1024);
resetHashtable(&sharedHashtable);
for (threadCount = 0; threadCount < MAX_THREADS; threadCount++)
{
Variation *currentVariation = &variations[threadCount];
currentVariation->searchStatus = SEARCH_STATUS_FINISHED;
}
return 0;
}
int testModuleCoordination()
{
return 0;
}