Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 112 | pmbaty | 1 | /* | 
| 2 |     Protector -- a UCI chess engine | ||
| 3 | |||
| 4 |     Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com) | ||
| 5 | |||
| 6 |     This program is free software: you can redistribute it and/or modify | ||
| 7 |     it under the terms of the GNU General Public License as published by | ||
| 8 |     the Free Software Foundation, either version 3 of the License, or | ||
| 9 |     (at your option) any later version. | ||
| 10 | |||
| 11 |     This program is distributed in the hope that it will be useful, | ||
| 12 |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||
| 14 |     GNU General Public License for more details. | ||
| 15 | |||
| 16 |     You should have received a copy of the GNU General Public License | ||
| 17 |     along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | |||
| 19 | */ | ||
| 20 | |||
| 21 | #include "coordination.h" | ||
| 22 | #include "protector.h" | ||
| 23 | #include "search.h" | ||
| 24 | #include "matesearch.h" | ||
| 25 | #include "io.h" | ||
| 26 | #include "hash.h" | ||
| 27 | #include <stdio.h> | ||
| 28 | #include <assert.h> | ||
| 29 | #ifndef _MSC_VER | ||
| 30 | #include <pthread.h> | ||
| 31 | #else // _MSC_VER | ||
| 32 | #include <process.h> // Pierre-Marie Baty -- Win32 threads support | ||
| 33 | __declspec(dllimport) void __stdcall Sleep (unsigned long Timeout); // Pierre-Marie Baty -- for Sleep() | ||
| 34 | #endif // !_MSC_VER | ||
| 35 | #include <time.h> | ||
| 36 | |||
| 37 | /* #define DEBUG_COORDINATION */ | ||
| 38 | |||
| 39 | #ifndef _MSC_VER | ||
| 40 | #define THREAD_RETURN_TYPE void * // Pierre-Marie Baty -- multiplatform threads support | ||
| 41 | #define INVALID_THREAD_ID 0 // Pierre-Marie Baty -- multiplatform threads support | ||
| 42 | pthread_t searchThread[MAX_THREADS]; | ||
| 43 | pthread_t timer; | ||
| 44 | static pthread_mutex_t guiSearchMutex = PTHREAD_MUTEX_INITIALIZER; | ||
| 45 | #else // _MSC_VER | ||
| 46 | #define THREAD_RETURN_TYPE unsigned int // Pierre-Marie Baty -- Win32 threads support | ||
| 47 | #define INVALID_THREAD_ID -1 // Pierre-Marie Baty -- Win32 threads support | ||
| 48 | uintptr_t searchThread[MAX_THREADS]; // Pierre-Marie Baty -- Win32 threads support | ||
| 49 | uintptr_t timer; // Pierre-Marie Baty -- Win32 threads support | ||
| 50 | static bool guiSearchMutex = false; // Pierre-Marie Baty -- Win32 threads support | ||
| 51 | bool terminate_timer = false; // Pierre-Marie Baty -- Win32 threads support | ||
| 52 | #endif // !_MSC_VER | ||
| 53 | long searchThreadId[MAX_THREADS]; | ||
| 54 | |||
| 55 | static int numThreads = 1; | ||
| 56 | static SearchTask dummyTask; | ||
| 57 | static SearchTask *currentTask = &dummyTask; | ||
| 58 | static Variation variations[MAX_THREADS]; | ||
| 59 | static Hashtable sharedHashtable; | ||
| 60 | static PawnHashInfo pawnHashtable[MAX_THREADS][PAWN_HASHTABLE_SIZE]; | ||
| 61 | |||
| 62 | Hashtable *getSharedHashtable() | ||
| 63 | { | ||
| 64 | return &sharedHashtable; | ||
| 65 | } | ||
| 66 | |||
| 67 | int setNumberOfThreads(int _numThreads) | ||
| 68 | { | ||
| 69 | numThreads = max(1, min(MAX_THREADS, _numThreads)); | ||
| 70 | |||
| 71 | return numThreads; | ||
| 72 | } | ||
| 73 | |||
| 74 | int getNumberOfThreads() | ||
| 75 | { | ||
| 76 | return numThreads; | ||
| 77 | } | ||
| 78 | |||
| 79 | UINT64 getNodeCount(void) | ||
| 80 | { | ||
| 81 | int threadCount; | ||
| 82 | UINT64 sum = 0; | ||
| 83 | |||
| 84 | for (threadCount = 0; threadCount < numThreads; threadCount++) | ||
| 85 |    { | ||
| 86 | sum += variations[threadCount].nodes; | ||
| 87 |    } | ||
| 88 | |||
| 89 | return sum; | ||
| 90 | } | ||
| 91 | |||
| 92 | Variation *getCurrentVariation() | ||
| 93 | { | ||
| 94 | return &variations[0]; | ||
| 95 | } | ||
| 96 | |||
| 97 | void getGuiSearchMutex(void) | ||
| 98 | { | ||
| 99 | #ifdef DEBUG_COORDINATION | ||
| 100 | logDebug("aquiring search lock...\n"); | ||
| 101 | #endif | ||
| 102 | |||
| 103 | #ifndef _MSC_VER | ||
| 104 | pthread_mutex_lock(&guiSearchMutex); | ||
| 105 | #else // _MSC_VER | ||
| 106 | while (guiSearchMutex) | ||
| 107 | Sleep (10); // allow context switching | ||
| 108 | guiSearchMutex = true; // Pierre-Marie Baty -- mutex support on Win32 | ||
| 109 | #endif // !_MSC_VER | ||
| 110 | |||
| 111 | #ifdef DEBUG_COORDINATION | ||
| 112 | logDebug("search lock aquired...\n"); | ||
| 113 | #endif | ||
| 114 | } | ||
| 115 | |||
| 116 | void releaseGuiSearchMutex(void) | ||
| 117 | { | ||
| 118 | #ifndef _MSC_VER | ||
| 119 | pthread_mutex_unlock(&guiSearchMutex); | ||
| 120 | #else // _MSC_VER | ||
| 121 | guiSearchMutex = false; // Pierre-Marie Baty -- mutex support on Win32 | ||
| 122 | #endif // !_MSC_VER | ||
| 123 | |||
| 124 | #ifdef DEBUG_COORDINATION | ||
| 125 | logDebug("search lock released...\n"); | ||
| 126 | #endif | ||
| 127 | } | ||
| 128 | |||
| 129 | static int startSearch(Variation * currentVariation) | ||
| 130 | { | ||
| 131 | currentVariation->searchStatus = SEARCH_STATUS_RUNNING; | ||
| 132 | |||
| 133 | #ifdef DEBUG_COORDINATION | ||
| 134 | logDebug("Search with thread #%d started.\n", | ||
| 135 | currentVariation->threadNumber); | ||
| 136 | #endif | ||
| 137 | |||
| 138 | switch (currentTask->type) | ||
| 139 |    { | ||
| 140 | case TASKTYPE_BEST_MOVE: | ||
| 141 | currentTask->bestMove = search(currentVariation, NULL); | ||
| 142 | break; | ||
| 143 | |||
| 144 | case TASKTYPE_TEST_BEST_MOVE: | ||
| 145 | currentTask->bestMove = | ||
| 146 | search(currentVariation, ¤tTask->solutions); | ||
| 147 | break; | ||
| 148 | |||
| 149 | case TASKTYPE_MATE_IN_N: | ||
| 150 | searchForMate(currentVariation, | ||
| 151 | ¤tTask->calculatedSolutions, | ||
| 152 | currentTask->numberOfMoves); | ||
| 153 | break; | ||
| 154 | |||
| 155 | case TASKTYPE_TEST_MATE_IN_N: | ||
| 156 | searchForMate(currentVariation, | ||
| 157 | ¤tTask->calculatedSolutions, | ||
| 158 | currentTask->numberOfMoves); | ||
| 159 | break; | ||
| 160 | |||
| 161 | default: | ||
| 162 | break; | ||
| 163 |    } | ||
| 164 | |||
| 165 | currentTask->nodes = getNodeCount(); | ||
| 166 | |||
| 167 | if (currentVariation->threadNumber == 0) | ||
| 168 |    { | ||
| 169 | int threadCount; | ||
| 170 | |||
| 171 | for (threadCount = 1; threadCount < numThreads; threadCount++) | ||
| 172 |       { | ||
| 173 | variations[threadCount].terminate = TRUE; | ||
| 174 |       } | ||
| 175 | |||
| 176 | #ifndef _MSC_VER | ||
| 177 | pthread_cancel(timer); | ||
| 178 | #else // _MSC_VER | ||
| 179 | terminate_timer = true; // Pierre-Marie Baty -- Win32 threads support | ||
| 180 | #endif // !_MSC_VER | ||
| 181 |    } | ||
| 182 | |||
| 183 | #ifdef DEBUG_COORDINATION | ||
| 184 | logDebug("Search thread #%d terminated.\n", | ||
| 185 | currentVariation->threadNumber); | ||
| 186 | #endif | ||
| 187 | |||
| 188 | currentVariation->searchStatus = SEARCH_STATUS_FINISHED; | ||
| 189 | |||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | long getElapsedTime() | ||
| 194 | { | ||
| 195 | return getTimestamp() - variations[0].startTime; | ||
| 196 | } | ||
| 197 | |||
| 198 | static THREAD_RETURN_TYPE executeSearch(void *arg) | ||
| 199 | { | ||
| 200 | Variation *currentVariation = arg; | ||
| 201 | |||
| 202 | #ifdef _MSC_VER | ||
| 203 | currentVariation->finished = false; // Pierre-Marie Baty -- Win32 thread control | ||
| 204 | #endif // _MSC_VER | ||
| 205 | |||
| 206 | startSearch(currentVariation); | ||
| 207 | |||
| 208 | #ifdef _MSC_VER | ||
| 209 | currentVariation->finished = true; // Pierre-Marie Baty -- Win32 thread control | ||
| 210 | #endif // _MSC_VER | ||
| 211 | return 0; | ||
| 212 | } | ||
| 213 | |||
| 214 | static THREAD_RETURN_TYPE watchTime(void *arg) | ||
| 215 | { | ||
| 216 | Variation *currentVariation = arg; | ||
| 217 | long timeLimit = currentVariation->timeLimit; | ||
| 218 | struct timespec requested;// , remaining; // Pierre-Marie Baty -- unused variable | ||
| 219 | int result; | ||
| 220 | |||
| 221 | requested.tv_sec = timeLimit / 1000; | ||
| 222 | requested.tv_nsec = 1000000 * (timeLimit - 1000 * (long)requested.tv_sec); // Pierre-Marie Baty -- added type cast | ||
| 223 | |||
| 224 |    /* logReport("### Timer thread working sec=%ld nsec=%ld ###\n", | ||
| 225 |       requested.tv_sec, requested.tv_nsec); */ | ||
| 226 | #ifndef _MSC_VER | ||
| 227 | result = nanosleep(&requested, &remaining); | ||
| 228 | if (result != -1) | ||
| 229 | #else // _MSC_VER | ||
| 230 | result = (clock () * 1000 / CLOCKS_PER_SEC); // in milliseconds | ||
| 231 | while (!terminate_timer && ((clock () * 1000 / CLOCKS_PER_SEC) < result + timeLimit)) | ||
| 232 | Sleep (10); // allow context switching if necessary | ||
| 233 | if (!terminate_timer) | ||
| 234 | #endif // !_MSC_VER | ||
| 235 |    { | ||
| 236 | getGuiSearchMutex(); | ||
| 237 | prepareSearchAbort(); | ||
| 238 | releaseGuiSearchMutex(); | ||
| 239 |    } | ||
| 240 | |||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 244 | void startTimerThread(SearchTask * task) | ||
| 245 | { | ||
| 246 | if (task->variation->timeLimit > 0 && task->variation->ponderMode == FALSE) | ||
| 247 |    { | ||
| 248 | #ifndef _MSC_VER | ||
| 249 | if (pthread_create(&timer, NULL, &watchTime, task->variation) == 0) | ||
| 250 | #else // _MSC_VER | ||
| 251 | terminate_timer = false; // Pierre-Marie Baty -- Win32 threads support | ||
| 252 | if ((timer = _beginthread (watchTime, 0, task->variation)) != -1) // Pierre-Marie Baty -- Win32 threads support | ||
| 253 | #endif // !_MSC_VER | ||
| 254 |       { | ||
| 255 | #ifdef DEBUG_COORDINATION | ||
| 256 | logDebug("Timer thread started.\n"); | ||
| 257 | #endif | ||
| 258 |       } | ||
| 259 |       else | ||
| 260 |       { | ||
| 261 | logDebug("### Timer thread could not be started. ###\n"); | ||
| 262 | |||
| 263 | exit(EXIT_FAILURE); | ||
| 264 |       } | ||
| 265 |    } | ||
| 266 | } | ||
| 267 | |||
| 268 | void scheduleTask(SearchTask * task) | ||
| 269 | { | ||
| 270 | const unsigned long startTime = getTimestamp(); | ||
| 271 | int threadCount; | ||
| 272 | |||
| 273 | sharedHashtable.entriesUsed = 0; | ||
| 274 | |||
| 275 | startTimerThread(task); | ||
| 276 | |||
| 277 | for (threadCount = 0; threadCount < numThreads; threadCount++) | ||
| 278 |    { | ||
| 279 | Variation *currentVariation = &variations[threadCount]; | ||
| 280 | |||
| 281 | currentTask = task; | ||
| 282 | *currentVariation = *(currentTask->variation); | ||
| 283 | currentVariation->searchStatus = SEARCH_STATUS_TERMINATE; | ||
| 284 | currentVariation->bestBaseMove = NO_MOVE; | ||
| 285 | currentVariation->terminate = FALSE; | ||
| 286 | currentVariation->pawnHashtable = &(pawnHashtable[threadCount][0]); | ||
| 287 | currentVariation->kingsafetyHashtable = | ||
| 288 | &(kingSafetyHashtable[threadCount][0]); | ||
| 289 | currentVariation->threadNumber = threadCount; | ||
| 290 | currentVariation->startTime = startTime; | ||
| 291 | |||
| 292 | #ifndef _MSC_VER | ||
| 293 | if (pthread_create(&searchThread[threadCount], NULL, | ||
| 294 | &executeSearch, currentVariation) == 0) | ||
| 295 | #else // _MSC_VER | ||
| 296 | if ((searchThread[threadCount] = _beginthread(executeSearch, 0, currentVariation)) != -1) // Pierre-Marie Baty -- Win32 threads support | ||
| 297 | #endif // !_MSC_VER | ||
| 298 |       { | ||
| 299 | #ifdef DEBUG_COORDINATION | ||
| 300 | logDebug("Search thread #%d created.\n", threadCount); | ||
| 301 | #endif | ||
| 302 |       } | ||
| 303 |       else | ||
| 304 |       { | ||
| 305 | logDebug("### Search thread #%d could not be started. ###\n", | ||
| 306 | threadCount); | ||
| 307 | |||
| 308 | exit(EXIT_FAILURE); | ||
| 309 |       } | ||
| 310 |    } | ||
| 311 | } | ||
| 312 | |||
| 313 | void waitForSearchTermination(void) | ||
| 314 | { | ||
| 315 | int threadCount; | ||
| 316 |    bool finished; | ||
| 317 | int count = 0; | ||
| 318 | |||
| 319 |    do | ||
| 320 |    { | ||
| 321 | finished = TRUE; | ||
| 322 | |||
| 323 | if (count > 1000) | ||
| 324 |       { | ||
| 325 | logDebug("waiting for search termination.\n"); | ||
| 326 | count = 0; | ||
| 327 |       } | ||
| 328 | |||
| 329 | for (threadCount = 0; threadCount < numThreads; threadCount++) | ||
| 330 |       { | ||
| 331 | Variation *currentVariation = &variations[threadCount]; | ||
| 332 | |||
| 333 | if (currentVariation->searchStatus != SEARCH_STATUS_FINISHED) | ||
| 334 |          { | ||
| 335 | if (searchThread[threadCount] != INVALID_THREAD_ID) | ||
| 336 |             { | ||
| 337 | #ifndef _MSC_VER | ||
| 338 | const int result = pthread_join (searchThread[threadCount], 0); | ||
| 339 | |||
| 340 | if (result == 0) | ||
| 341 |                { | ||
| 342 | searchThread[threadCount] = INVALID_THREAD_ID; | ||
| 343 |                } | ||
| 344 |                else | ||
| 345 |                { | ||
| 346 | finished = FALSE; | ||
| 347 |                } | ||
| 348 | #else // _MSC_VER | ||
| 349 | while (!currentVariation->finished) | ||
| 350 | Sleep (10); // allow context switching | ||
| 351 | |||
| 352 | searchThread[threadCount] = INVALID_THREAD_ID; | ||
| 353 | #endif // !_MSC_VER | ||
| 354 |             } | ||
| 355 |          } | ||
| 356 |          else | ||
| 357 |          { | ||
| 358 | searchThread[threadCount] = INVALID_THREAD_ID; | ||
| 359 |          } | ||
| 360 | |||
| 361 | #ifdef DEBUG_COORDINATION | ||
| 362 | logDebug("Task %d finished.\n", threadCount); | ||
| 363 | #endif | ||
| 364 |       } | ||
| 365 | |||
| 366 |       count++; | ||
| 367 |    } | ||
| 368 | while (finished == FALSE); | ||
| 369 | } | ||
| 370 | |||
| 371 | void completeTask(SearchTask * task) | ||
| 372 | { | ||
| 373 | scheduleTask(task); | ||
| 374 | |||
| 375 | #ifdef DEBUG_COORDINATION | ||
| 376 | logDebug("Task scheduled. Waiting for completion.\n"); | ||
| 377 | #endif | ||
| 378 | |||
| 379 | waitForSearchTermination(); | ||
| 380 | } | ||
| 381 | |||
| 382 | void prepareSearchAbort(void) | ||
| 383 | { | ||
| 384 | int threadCount; | ||
| 385 | |||
| 386 | for (threadCount = 0; threadCount < numThreads; threadCount++) | ||
| 387 |    { | ||
| 388 | variations[threadCount].terminate = TRUE; | ||
| 389 |    } | ||
| 390 | } | ||
| 391 | |||
| 392 | void unsetPonderMode(void) | ||
| 393 | { | ||
| 394 | int threadCount; | ||
| 395 | |||
| 396 | for (threadCount = 0; threadCount < numThreads; threadCount++) | ||
| 397 |    { | ||
| 398 | Variation *currentVariation = &variations[threadCount]; | ||
| 399 | |||
| 400 | currentVariation->ponderMode = FALSE; | ||
| 401 |    } | ||
| 402 | } | ||
| 403 | |||
| 404 | /*void setTimeLimit(unsigned long timeTarget, unsigned long timeLimit) | ||
| 405 | { | ||
| 406 |    int threadCount; | ||
| 407 | |||
| 408 |    for (threadCount = 0; threadCount < numThreads; threadCount++) | ||
| 409 |    { | ||
| 410 |       Variation *currentVariation = &variations[threadCount]; | ||
| 411 | |||
| 412 |       currentVariation->timeTarget = timeTarget; | ||
| 413 |       currentVariation->timeLimit = timeLimit; | ||
| 414 |    } | ||
| 415 | }*/ // Pierre-Marie Baty -- unused function? | ||
| 416 | |||
| 417 | void setHashtableSizeInMb(unsigned int size) | ||
| 418 | { | ||
| 419 | UINT64 tablesize = 1024 * 1024 * (UINT64) size; | ||
| 420 | |||
| 421 | setHashtableSize(&sharedHashtable, tablesize); | ||
| 422 | resetHashtable(&sharedHashtable); | ||
| 423 | } | ||
| 424 | |||
| 425 | int initializeModuleCoordination() | ||
| 426 | { | ||
| 427 | int threadCount; | ||
| 428 | |||
| 429 | initializeHashtable(&sharedHashtable); | ||
| 430 | setHashtableSize(&sharedHashtable, 16 * 1024 * 1024); | ||
| 431 | resetHashtable(&sharedHashtable); | ||
| 432 | |||
| 433 | for (threadCount = 0; threadCount < MAX_THREADS; threadCount++) | ||
| 434 |    { | ||
| 435 | Variation *currentVariation = &variations[threadCount]; | ||
| 436 | |||
| 437 | currentVariation->searchStatus = SEARCH_STATUS_FINISHED; | ||
| 438 |    } | ||
| 439 | |||
| 440 | return 0; | ||
| 441 | } | ||
| 442 | |||
| 443 | int testModuleCoordination() | ||
| 444 | { | ||
| 445 | return 0; | ||
| 446 | } |