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 | } |