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