X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=e205db6fa163d56b5dfccc5efbb8692b6c22a8cf;hp=96b8442343ca7f484459d452f3fdba9d56d5e860;hb=27393ebae2e689c86f77777d3ba9aae39b9c89b8;hpb=81cfd81366cf06dc403476a4782ef34703346c61 diff --git a/src/search.cpp b/src/search.cpp index 96b84423..e205db6f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -214,6 +214,9 @@ namespace { IterationInfoType IterationInfo[PLY_MAX_PLUS_2]; int BestMoveChangesByIteration[PLY_MAX_PLUS_2]; + // Search window management + int AspirationDelta; + // MultiPV mode int MultiPV; @@ -263,11 +266,10 @@ namespace { // History table History H; - /// Functions Value id_loop(const Position& pos, Move searchMoves[]); - Value root_search(Position& pos, SearchStack ss[], RootMoveList& rml, Value alpha, Value beta); + Value root_search(Position& pos, SearchStack ss[], RootMoveList& rml, Value& oldAlpha, Value& beta); Value search_pv(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID); Value search(Position& pos, SearchStack ss[], Value beta, Depth depth, int ply, bool allowNullmove, int threadID, Move excludedMove = MOVE_NONE); Value qsearch(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID); @@ -284,8 +286,10 @@ namespace { bool ok_to_prune(const Position& pos, Move m, Move threat); bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply); Value refine_eval(const TTEntry* tte, Value defaultEval, int ply); + Depth calculate_reduction(double baseReduction, int moveCount, Depth depth, double reductionInhibitor); void update_history(const Position& pos, Move move, Depth depth, Move movesSearched[], int moveCount); void update_killers(Move m, SearchStack& ss); + void update_gains(const Position& pos, Move move, Value before, Value after); bool fail_high_ply_1(); int current_search_time(); @@ -730,7 +734,10 @@ namespace { int prevDelta1 = IterationInfo[Iteration - 1].speculatedValue - IterationInfo[Iteration - 2].speculatedValue; int prevDelta2 = IterationInfo[Iteration - 2].speculatedValue - IterationInfo[Iteration - 3].speculatedValue; - int delta = Max(2 * abs(prevDelta1) + abs(prevDelta2), ProblemMargin); + int delta = Max(abs(prevDelta1) + abs(prevDelta2) / 2, 16); + + delta = (delta + 7) / 8 * 8; // Round to match grainSize + AspirationDelta = delta; alpha = Max(IterationInfo[Iteration - 1].value - delta, -VALUE_INFINITE); beta = Min(IterationInfo[Iteration - 1].value + delta, VALUE_INFINITE); @@ -841,7 +848,7 @@ namespace { // If we are pondering or in infinite search, we shouldn't print the // best move before we are told to do so. - if (!AbortSearch && !ExactMaxTime && (PonderSearch || InfiniteSearch)) + if (!AbortSearch && (PonderSearch || InfiniteSearch)) wait_for_stop_or_ponderhit(); else // Print final search statistics @@ -886,11 +893,23 @@ namespace { // similar to search_pv except that it uses a different move ordering // scheme and prints some information to the standard output. - Value root_search(Position& pos, SearchStack ss[], RootMoveList& rml, Value alpha, Value beta) { + Value root_search(Position& pos, SearchStack ss[], RootMoveList& rml, Value& oldAlpha, Value& beta) { - Value oldAlpha = alpha; - Value value = -VALUE_INFINITE; + Value alpha = oldAlpha; + Value value; CheckInfo ci(pos); + int researchCount = 0; + bool isCheck = pos.is_check(); + + // Evaluate the position statically + EvalInfo ei; + if (!isCheck) + ss[0].eval = evaluate(pos, ei, 0); + else + ss[0].eval = VALUE_NONE; + + while(1) // Fail low loop + { // Loop through all the moves in the root move list for (int i = 0; i < rml.move_count() && !AbortSearch; i++) @@ -933,10 +952,15 @@ namespace { ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, false, false, &dangerous); newDepth = depth + ext; + value = - VALUE_INFINITE; + + while (1) // Fail high loop + { + // Make the move, and search it pos.do_move(move, st, ci, moveIsCheck); - if (i < MultiPV) + if (i < MultiPV || value > alpha) { // Aspiration window is disabled in multi-pv case if (MultiPV > 1) @@ -965,10 +989,9 @@ namespace { && !captureOrPromotion && !move_is_castle(move)) { - double red = 0.5 + ln(RootMoveNumber - MultiPV + 1) * ln(depth / 2) / 6.0; - if (red >= 1.0) + ss[0].reduction = calculate_reduction(0.5, RootMoveNumber - MultiPV + 1, depth, 6.0); + if (ss[0].reduction) { - ss[0].reduction = Depth(int(floor(red * int(OnePly)))); value = -search(pos, ss, -alpha, newDepth-ss[0].reduction, 1, true, 0); doFullDepthSearch = (value > alpha); } @@ -976,6 +999,7 @@ namespace { if (doFullDepthSearch) { + ss[0].reduction = Depth(0); value = -search(pos, ss, -alpha, newDepth, 1, true, 0); if (value > alpha) @@ -992,6 +1016,46 @@ namespace { pos.undo_move(move); + if (AbortSearch || value < beta) + break; // We are not failing high + + // We are failing high and going to do a research. It's important to update score + // before research in case we run out of time while researching. + rml.set_move_score(i, value); + update_pv(ss, 0); + TT.extract_pv(pos, ss[0].pv, PLY_MAX); + rml.set_move_pv(i, ss[0].pv); + + // Print search information to the standard output + cout << "info depth " << Iteration + << " score " << value_to_string(value) + << ((value >= beta) ? " lowerbound" : + ((value <= alpha)? " upperbound" : "")) + << " time " << current_search_time() + << " nodes " << nodes_searched() + << " nps " << nps() + << " pv "; + + for (int j = 0; ss[0].pv[j] != MOVE_NONE && j < PLY_MAX; j++) + cout << ss[0].pv[j] << " "; + + cout << endl; + + if (UseLogFile) + { + ValueType type = (value >= beta ? VALUE_TYPE_LOWER + : (value <= alpha ? VALUE_TYPE_UPPER : VALUE_TYPE_EXACT)); + + LogFile << pretty_pv(pos, current_search_time(), Iteration, + nodes_searched(), value, type, ss[0].pv) << endl; + } + + // Prepare for research + researchCount++; + beta = Min(beta + AspirationDelta * (1 << researchCount), VALUE_INFINITE); + + } // End of fail high loop + // Finished searching the move. If AbortSearch is true, the search // was aborted because the user interrupted the search or because we // ran out of time. In this case, the return value of the search cannot @@ -1086,6 +1150,17 @@ namespace { FailLow = (alpha == oldAlpha); } + + if (AbortSearch || alpha > oldAlpha) + break; // End search, we are not failing low + + // Prepare for research + researchCount++; + alpha = Max(alpha - AspirationDelta * (1 << researchCount), -VALUE_INFINITE); + oldAlpha = alpha; + + } // Fail low loop + return alpha; } @@ -1153,9 +1228,18 @@ namespace { tte = TT.retrieve(pos.get_key()); } + isCheck = pos.is_check(); + if (!isCheck) + { + // Update gain statistics of the previous move that lead + // us in this position. + EvalInfo ei; + ss[ply].eval = evaluate(pos, ei, threadID); + update_gains(pos, ss[ply - 1].currentMove, ss[ply - 1].eval, ss[ply].eval); + } + // Initialize a MovePicker object for the current position, and prepare // to search all moves - isCheck = pos.is_check(); mateThreat = pos.has_mate_threat(opposite_color(pos.side_to_move())); CheckInfo ci(pos); MovePicker mp = MovePicker(pos, ttMove, depth, H, &ss[ply]); @@ -1218,13 +1302,12 @@ namespace { && !move_is_castle(move) && !move_is_killer(move, ss[ply])) { - double red = 0.5 + ln(moveCount) * ln(depth / 2) / 6.0; - if (red >= 1.0) - { - ss[ply].reduction = Depth(int(floor(red * int(OnePly)))); - value = -search(pos, ss, -alpha, newDepth-ss[ply].reduction, ply+1, true, threadID); - doFullDepthSearch = (value > alpha); - } + ss[ply].reduction = calculate_reduction(0.5, moveCount, depth, 6.0); + if (ss[ply].reduction) + { + value = -search(pos, ss, -alpha, newDepth-ss[ply].reduction, ply+1, true, threadID); + doFullDepthSearch = (value > alpha); + } } if (doFullDepthSearch) // Go with full depth non-pv search @@ -1331,7 +1414,7 @@ namespace { Move ttMove, move; Depth ext, newDepth; Value bestValue, staticValue, nullValue, value, futilityValue, futilityValueScaled; - bool isCheck, useFutilityPruning, singleEvasion, moveIsCheck, captureOrPromotion, dangerous; + bool isCheck, singleEvasion, moveIsCheck, captureOrPromotion, dangerous; bool mateThreat = false; int moveCount = 0; futilityValue = staticValue = bestValue = value = -VALUE_INFINITE; @@ -1375,7 +1458,7 @@ namespace { // Calculate depth dependant futility pruning parameters const int FutilityMoveCountMargin = 3 + (1 << (3 * int(depth) / 8)); - const int FutilityValueMargin = 112 * bitScanReverse32(int(depth) * int(depth) / 2); + const int PostFutilityValueMargin = 112 * bitScanReverse32(int(depth) * int(depth) / 2); // Evaluate the position statically if (!isCheck) @@ -1389,10 +1472,20 @@ namespace { } ss[ply].eval = staticValue; - futilityValue = staticValue + FutilityValueMargin; + futilityValue = staticValue + PostFutilityValueMargin; //FIXME: Remove me, only for split staticValue = refine_eval(tte, staticValue, ply); // Enhance accuracy with TT value if possible + update_gains(pos, ss[ply - 1].currentMove, ss[ply - 1].eval, ss[ply].eval); } + // Do a "stand pat". If we are above beta by a good margin then + // return immediately. + // FIXME: test with added condition 'allowNullmove || depth <= OnePly' and !value_is_mate(beta) + // FIXME: test with modified condition 'depth < RazorDepth' + if ( !isCheck + && depth < SelectiveDepth + && staticValue - PostFutilityValueMargin >= beta) + return staticValue - PostFutilityValueMargin; + // Null move search if ( allowNullmove && depth > OnePly @@ -1470,7 +1563,6 @@ namespace { // to search all moves. MovePicker mp = MovePicker(pos, ttMove, depth, H, &ss[ply]); CheckInfo ci(pos); - useFutilityPruning = depth < SelectiveDepth && !isCheck; // Loop through all legal moves until no moves remain or a beta cutoff occurs while ( bestValue < beta @@ -1516,10 +1608,41 @@ namespace { // Update current move movesSearched[moveCount++] = ss[ply].currentMove = move; + // Futility pruning for captures + // FIXME: test disabling 'Futility pruning for captures' + // FIXME: test with 'newDepth < RazorDepth' + Color them = opposite_color(pos.side_to_move()); + + if ( !isCheck + && newDepth < SelectiveDepth + && !dangerous + && pos.move_is_capture(move) + && !pos.move_is_check(move, ci) + && !move_is_promotion(move) + && move != ttMove + && !move_is_ep(move) + && (pos.type_of_piece_on(move_to(move)) != PAWN || !pos.pawn_is_passed(them, move_to(move)))) // Do not prune passed pawn captures + { + int preFutilityValueMargin = 0; + + if (newDepth >= OnePly) + preFutilityValueMargin = 112 * bitScanReverse32(int(newDepth) * int(newDepth) / 2); + + Value futilityCaptureValue = ss[ply].eval + pos.endgame_value_of_piece_on(move_to(move)) + preFutilityValueMargin + ei.futilityMargin + 90; + + if (futilityCaptureValue < beta) + { + if (futilityCaptureValue > bestValue) + bestValue = futilityCaptureValue; + continue; + } + } + // Futility pruning - if ( useFutilityPruning + if ( !isCheck && !dangerous && !captureOrPromotion + && !move_is_castle(move) && move != ttMove) { // Move count based pruning @@ -1529,13 +1652,29 @@ namespace { continue; // Value based pruning - futilityValueScaled = futilityValue - moveCount * IncrementalFutilityMargin; + Depth predictedDepth = newDepth; + + //FIXME: We are ignoring condition: depth >= 3*OnePly, BUG?? + ss[ply].reduction = calculate_reduction(0.5, moveCount, depth, 3.0); + if (ss[ply].reduction) + predictedDepth -= ss[ply].reduction; - if (futilityValueScaled < beta) + if (predictedDepth < SelectiveDepth) { - if (futilityValueScaled > bestValue) - bestValue = futilityValueScaled; - continue; + int preFutilityValueMargin = 0; + if (predictedDepth >= OnePly) + preFutilityValueMargin = 112 * bitScanReverse32(int(predictedDepth) * int(predictedDepth) / 2); + + preFutilityValueMargin += H.gain(pos.piece_on(move_from(move)), move_from(move), move_to(move)) + 45; + + futilityValueScaled = ss[ply].eval + preFutilityValueMargin - moveCount * IncrementalFutilityMargin; + + if (futilityValueScaled < beta) + { + if (futilityValueScaled > bestValue) + bestValue = futilityValueScaled; + continue; + } } } @@ -1550,13 +1689,11 @@ namespace { && !dangerous && !captureOrPromotion && !move_is_castle(move) - && !move_is_killer(move, ss[ply]) - /* && move != ttMove*/) + && !move_is_killer(move, ss[ply])) { - double red = 0.5 + ln(moveCount) * ln(depth / 2) / 3.0; - if (red >= 1.0) + ss[ply].reduction = calculate_reduction(0.5, moveCount, depth, 3.0); + if (ss[ply].reduction) { - ss[ply].reduction = Depth(int(floor(red * int(OnePly)))); value = -search(pos, ss, -(beta-1), newDepth-ss[ply].reduction, ply+1, true, threadID); doFullDepthSearch = (value >= beta); } @@ -1590,7 +1727,7 @@ namespace { && idle_thread_exists(threadID) && !AbortSearch && !thread_should_stop(threadID) - && split(pos, ss, ply, &beta, &beta, &bestValue, futilityValue, + && split(pos, ss, ply, &beta, &beta, &bestValue, futilityValue, //FIXME: SMP & futilityValue depth, &moveCount, &mp, threadID, false)) break; } @@ -1682,6 +1819,12 @@ namespace { else staticValue = evaluate(pos, ei, threadID); + if (!isCheck) + { + ss[ply].eval = staticValue; + update_gains(pos, ss[ply - 1].currentMove, ss[ply - 1].eval, ss[ply].eval); + } + // Initialize "stand pat score", and return it immediately if it is // at least beta. bestValue = staticValue; @@ -1825,28 +1968,29 @@ namespace { SearchStack* ss = sp->sstack[threadID]; Value value = -VALUE_INFINITE; Move move; + int moveCount; bool isCheck = pos.is_check(); bool useFutilityPruning = sp->depth < SelectiveDepth && !isCheck; const int FutilityMoveCountMargin = 3 + (1 << (3 * int(sp->depth) / 8)); - while ( sp->bestValue < sp->beta + while ( lock_grab_bool(&(sp->lock)) + && sp->bestValue < sp->beta && !thread_should_stop(threadID) - && (move = sp->mp->get_next_move(sp->lock)) != MOVE_NONE) + && (move = sp->mp->get_next_move()) != MOVE_NONE) { + moveCount = ++sp->moves; + lock_release(&(sp->lock)); + assert(move_is_ok(move)); bool moveIsCheck = pos.move_is_check(move, ci); bool captureOrPromotion = pos.move_is_capture_or_promotion(move); - lock_grab(&(sp->lock)); - int moveCount = ++sp->moves; - lock_release(&(sp->lock)); - ss[sp->ply].currentMove = move; - // Decide the new search depth. + // Decide the new search depth bool dangerous; Depth ext = extension(pos, move, false, captureOrPromotion, moveIsCheck, false, false, &dangerous); Depth newDepth = sp->depth - OnePly + ext; @@ -1891,10 +2035,9 @@ namespace { && !move_is_castle(move) && !move_is_killer(move, ss[sp->ply])) { - double red = 0.5 + ln(moveCount) * ln(sp->depth / 2) / 3.0; - if (red >= 1.0) + ss[sp->ply].reduction = calculate_reduction(0.5, moveCount, sp->depth, 3.0); + if (ss[sp->ply].reduction) { - ss[sp->ply].reduction = Depth(int(floor(red * int(OnePly)))); value = -search(pos, ss, -(sp->beta-1), newDepth-ss[sp->ply].reduction, sp->ply+1, true, threadID); doFullDepthSearch = (value >= sp->beta); } @@ -1910,7 +2053,10 @@ namespace { assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); if (thread_should_stop(threadID)) + { + lock_grab(&(sp->lock)); break; + } // New best move? if (value > sp->bestValue) // Less then 2% of cases @@ -1933,7 +2079,7 @@ namespace { } } - lock_grab(&(sp->lock)); + /* Here we have the lock still grabbed */ // If this is the master thread and we have been asked to stop because of // a beta cutoff higher up in the tree, stop all slave threads. @@ -1966,24 +2112,25 @@ namespace { CheckInfo ci(pos); SearchStack* ss = sp->sstack[threadID]; Value value = -VALUE_INFINITE; + int moveCount; Move move; - while ( sp->alpha < sp->beta + while ( lock_grab_bool(&(sp->lock)) + && sp->alpha < sp->beta && !thread_should_stop(threadID) - && (move = sp->mp->get_next_move(sp->lock)) != MOVE_NONE) + && (move = sp->mp->get_next_move()) != MOVE_NONE) { - bool moveIsCheck = pos.move_is_check(move, ci); - bool captureOrPromotion = pos.move_is_capture_or_promotion(move); + moveCount = ++sp->moves; + lock_release(&(sp->lock)); assert(move_is_ok(move)); - lock_grab(&(sp->lock)); - int moveCount = ++sp->moves; - lock_release(&(sp->lock)); + bool moveIsCheck = pos.move_is_check(move, ci); + bool captureOrPromotion = pos.move_is_capture_or_promotion(move); ss[sp->ply].currentMove = move; - // Decide the new search depth. + // Decide the new search depth bool dangerous; Depth ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, false, false, &dangerous); Depth newDepth = sp->depth - OnePly + ext; @@ -2001,11 +2148,10 @@ namespace { && !move_is_castle(move) && !move_is_killer(move, ss[sp->ply])) { - double red = 0.5 + ln(moveCount) * ln(sp->depth / 2) / 6.0; - if (red >= 1.0) + ss[sp->ply].reduction = calculate_reduction(0.5, moveCount, sp->depth, 6.0); + if (ss[sp->ply].reduction) { Value localAlpha = sp->alpha; - ss[sp->ply].reduction = Depth(int(floor(red * int(OnePly)))); value = -search(pos, ss, -localAlpha, newDepth-ss[sp->ply].reduction, sp->ply+1, true, threadID); doFullDepthSearch = (value > localAlpha); } @@ -2043,43 +2189,49 @@ namespace { assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); if (thread_should_stop(threadID)) + { + lock_grab(&(sp->lock)); break; + } // New best move? - lock_grab(&(sp->lock)); - if (value > sp->bestValue && !thread_should_stop(threadID)) + if (value > sp->bestValue) // Less then 2% of cases { - sp->bestValue = value; - if (value > sp->alpha) + lock_grab(&(sp->lock)); + if (value > sp->bestValue && !thread_should_stop(threadID)) { - // Ask threads to stop before to modify sp->alpha - if (value >= sp->beta) + sp->bestValue = value; + if (value > sp->alpha) { - for (int i = 0; i < ActiveThreads; i++) - if (i != threadID && (i == sp->master || sp->slaves[i])) - Threads[i].stop = true; + // Ask threads to stop before to modify sp->alpha + if (value >= sp->beta) + { + for (int i = 0; i < ActiveThreads; i++) + if (i != threadID && (i == sp->master || sp->slaves[i])) + Threads[i].stop = true; - sp->finished = true; - } + sp->finished = true; + } - sp->alpha = value; + sp->alpha = value; - sp_update_pv(sp->parentSstack, ss, sp->ply); - if (value == value_mate_in(sp->ply + 1)) - ss[sp->ply].mateKiller = move; - } - // If we are at ply 1, and we are searching the first root move at - // ply 0, set the 'Problem' variable if the score has dropped a lot - // (from the computer's point of view) since the previous iteration. - if ( sp->ply == 1 - && Iteration >= 2 - && -value <= IterationInfo[Iteration-1].value - ProblemMargin) - Problem = true; + sp_update_pv(sp->parentSstack, ss, sp->ply); + if (value == value_mate_in(sp->ply + 1)) + ss[sp->ply].mateKiller = move; + } + // If we are at ply 1, and we are searching the first root move at + // ply 0, set the 'Problem' variable if the score has dropped a lot + // (from the computer's point of view) since the previous iteration. + if ( sp->ply == 1 + && Iteration >= 2 + && -value <= IterationInfo[Iteration-1].value - ProblemMargin) + Problem = true; + } + lock_release(&(sp->lock)); } - lock_release(&(sp->lock)); } - lock_grab(&(sp->lock)); + /* Here we have the lock still grabbed */ // If this is the master thread and we have been asked to stop because of // a beta cutoff higher up in the tree, stop all slave threads. @@ -2458,9 +2610,8 @@ namespace { Square mfrom, mto, tfrom, tto; - // Prune if there isn't any threat move and - // is not a castling move (common case). - if (threat == MOVE_NONE && !move_is_castle(m)) + // Prune if there isn't any threat move + if (threat == MOVE_NONE) return true; mfrom = move_from(m); @@ -2468,15 +2619,11 @@ namespace { tfrom = move_from(threat); tto = move_to(threat); - // Case 1: Castling moves are never pruned - if (move_is_castle(m)) - return false; - - // Case 2: Don't prune moves which move the threatened piece + // Case 1: Don't prune moves which move the threatened piece if (mfrom == tto) return false; - // Case 3: If the threatened piece has value less than or equal to the + // Case 2: If the threatened piece has value less than or equal to the // value of the threatening piece, don't prune move which defend it. if ( pos.move_is_capture(threat) && ( pos.midgame_value_of_piece_on(tfrom) >= pos.midgame_value_of_piece_on(tto) @@ -2484,7 +2631,7 @@ namespace { && pos.move_attacks_square(m, tto)) return false; - // Case 4: If the moving piece in the threatened move is a slider, don't + // Case 3: If the moving piece in the threatened move is a slider, don't // prune safe moves which block its ray. if ( piece_is_slider(pos.piece_on(tfrom)) && bit_is_set(squares_between(tfrom, tto), mto) @@ -2528,6 +2675,20 @@ namespace { return defaultEval; } + // calculate_reduction() returns reduction in plies based on + // moveCount and depth. Reduction is always at least one ply. + + Depth calculate_reduction(double baseReduction, int moveCount, Depth depth, double reductionInhibitor) { + + double red = baseReduction + ln(moveCount) * ln(depth / 2) / reductionInhibitor; + + if (red >= 1.0) + return Depth(int(floor(red * int(OnePly)))); + else + return Depth(0); + + } + // update_history() registers a good move that produced a beta-cutoff // in history and marks as failures all the other moves of that ply. @@ -2565,6 +2726,21 @@ namespace { } + // update_gains() updates the gains table of a non-capture move given + // the static position evaluation before and after the move. + + void update_gains(const Position& pos, Move m, Value before, Value after) { + + if ( m != MOVE_NULL + && before != VALUE_NONE + && after != VALUE_NONE + && pos.captured_piece() == NO_PIECE_TYPE + && !move_is_castle(m) + && !move_is_promotion(m)) + H.set_gain(pos.piece_on(move_to(m)), move_from(m), move_to(m), -(before + after)); + } + + // fail_high_ply_1() checks if some thread is currently resolving a fail // high at ply 1 at the node below the first root node. This information // is used for time management. @@ -2898,7 +3074,10 @@ namespace { if (!Threads[slave].idle || slave == master) return false; - if (Threads[slave].activeSplitPoints == 0) + // Make a local copy to be sure doesn't change under our feet + int localActiveSplitPoints = Threads[slave].activeSplitPoints; + + if (localActiveSplitPoints == 0) // No active split points means that the thread is available as // a slave for any other thread. return true; @@ -2906,8 +3085,10 @@ namespace { if (ActiveThreads == 2) return true; - // Apply the "helpful master" concept if possible - if (SplitPointStack[slave][Threads[slave].activeSplitPoints - 1].slaves[master]) + // Apply the "helpful master" concept if possible. Use localActiveSplitPoints + // that is known to be > 0, instead of Threads[slave].activeSplitPoints that + // could have been set to 0 by another thread leading to an out of bound access. + if (SplitPointStack[slave][localActiveSplitPoints - 1].slaves[master]) return true; return false; @@ -2993,6 +3174,7 @@ namespace { splitPoint->slaves[i] = 0; Threads[master].idle = false; + Threads[master].stop = false; Threads[master].splitPoint = splitPoint; // Allocate available threads setting idle flag to false @@ -3000,6 +3182,7 @@ namespace { if (thread_is_available(i, master)) { Threads[i].idle = false; + Threads[i].stop = false; Threads[i].splitPoint = splitPoint; splitPoint->slaves[i] = 1; splitPoint->cpus++; @@ -3010,22 +3193,14 @@ namespace { // We can release the lock because master and slave threads are already booked lock_release(&MPLock); - // Copy the tail of current search stack to the master thread - memcpy(splitPoint->sstack[master] + ply - 1, sstck + ply - 1, 3 * sizeof(SearchStack)); - // Tell the threads that they have work to do. This will make them leave - // their idle loop. Also copy search stack tail for each slave thread. + // their idle loop. But before copy search stack tail for each thread. for (int i = 0; i < ActiveThreads; i++) - { - if (splitPoint->slaves[i]) - memcpy(splitPoint->sstack[i] + ply - 1, sstck + ply - 1, 3 * sizeof(SearchStack)); - if (i == master || splitPoint->slaves[i]) { - Threads[i].stop = false; + memcpy(splitPoint->sstack[i] + ply - 1, sstck + ply - 1, 3 * sizeof(SearchStack)); Threads[i].workIsWaiting = true; // This makes the slave to exit from idle_loop() } - } // Everything is set up. The master thread enters the idle loop, from // which it will instantly launch a search, because its workIsWaiting