X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=40f14a509d2e0bc940023354897f15a9a0f14530;hp=48cb4d6bdd9df3a36bacff4d04b3ce0dfd5dac34;hb=5bb766e826af935b159cbd2ab9c59b279930dc5e;hpb=103b368ab7f5fd696e0c6925917344d15a3c2d9c diff --git a/src/search.cpp b/src/search.cpp index 48cb4d6b..40f14a50 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -130,7 +130,7 @@ namespace { Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); void id_loop(Position& pos); - bool check_is_dangerous(Position &pos, Move move, Value futilityBase, Value beta, Value *bValue); + bool check_is_dangerous(Position &pos, Move move, Value futilityBase, Value beta); bool connected_moves(const Position& pos, Move m1, Move m2); Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); @@ -249,7 +249,6 @@ void Search::think() { static Book book; // Defined static to initialize the PRNG only once - Move bm; Position& pos = RootPosition; Chess960 = pos.is_chess960(); elapsed_time(true); @@ -266,12 +265,15 @@ void Search::think() { goto finalize; } - if ( Options["OwnBook"] - && (bm = book.probe(pos, Options["Book File"], Options["Best Book Move"])) != MOVE_NONE - && count(RootMoves.begin(), RootMoves.end(), bm)) + if (Options["OwnBook"]) { - std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bm)); - goto finalize; + Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]); + + if (bookMove && count(RootMoves.begin(), RootMoves.end(), bookMove)) + { + std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove)); + goto finalize; + } } // Read UCI options: GUI could change UCI parameters during the game @@ -305,11 +307,7 @@ void Search::think() { << endl; } - for (int i = 0; i < Threads.size(); i++) - { - Threads[i].maxPly = 0; - Threads[i].wake_up(); - } + Threads.set_size(Options["Threads"]); // Set best timer interval to avoid lagging under time pressure. Timer is // used to check for remaining available thinking time. @@ -346,7 +344,7 @@ finalize: // but if we are pondering or in infinite search, we shouldn't print the best // move before we are told to do so. if (!Signals.stop && (Limits.ponder || Limits.infinite)) - Threads.wait_for_stop_or_ponderhit(); + Threads[pos.thread()].wait_for_stop_or_ponderhit(); // Best move could be MOVE_NONE when searching on a stalemate position cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], Chess960) @@ -465,7 +463,7 @@ namespace { if (SkillLevelEnabled && depth == 1 + SkillLevel) skillBest = do_skill_level(); - if (Options["Use Search Log"]) + if (!Signals.stop && Options["Use Search Log"]) pv_info_to_log(pos, depth, bestValue, elapsed_time(), &RootMoves[0].pv[0]); // Filter out startup noise when monitoring best move stability @@ -542,7 +540,7 @@ namespace { const bool RootNode = (NT == Root || NT == SplitPointRoot); assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); - assert(PvNode == (alpha != beta - 1)); + assert((alpha == beta - 1) || PvNode); assert(depth > DEPTH_ZERO); assert(pos.thread() >= 0 && pos.thread() < Threads.size()); @@ -550,9 +548,9 @@ namespace { StateInfo st; const TTEntry *tte; Key posKey; - Move ttMove, move, excludedMove, threatMove; + Move ttMove, move, excludedMove, bestMove, threatMove; Depth ext, newDepth; - ValueType vt; + Bound bt; Value bestValue, value, oldAlpha; Value refinedValue, nullValue, futilityBase, futilityValue; bool isPvMove, inCheck, singularExtensionNode, givesCheck; @@ -571,22 +569,33 @@ namespace { thread.maxPly = ss->ply; // Step 1. Initialize node - if (!SpNode) - { - ss->currentMove = ss->bestMove = threatMove = (ss+1)->excludedMove = MOVE_NONE; - (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; - (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; - } - else + if (SpNode) { - sp = ss->sp; tte = NULL; ttMove = excludedMove = MOVE_NONE; + sp = ss->sp; + bestMove = sp->bestMove; threatMove = sp->threatMove; + bestValue = sp->bestValue; + moveCount = sp->moveCount; // Lock must be held here + + assert(bestValue > -VALUE_INFINITE && moveCount > 0); + goto split_point_start; } + else + { + ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; + (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; + (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; + + } // Step 2. Check for aborted search and immediate draw + // Enforce node limit here. FIXME: This only works with 1 search thread. + if (Limits.maxNodes && pos.nodes_searched() >= Limits.maxNodes) + Signals.stop = true; + if (( Signals.stop || pos.is_draw() || ss->ply > MAX_PLY) && !RootNode) @@ -618,20 +627,20 @@ namespace { // a fail high/low. Biggest advantage at probing at PV nodes is to have a // smooth experience in analysis mode. We don't probe at Root nodes otherwise // we should also update RootMoveList to avoid bogus output. - if (!RootNode && tte && (PvNode ? tte->depth() >= depth && tte->type() == VALUE_TYPE_EXACT + if (!RootNode && tte && (PvNode ? tte->depth() >= depth && tte->type() == BOUND_EXACT : can_return_tt(tte, depth, beta, ss->ply))) { TT.refresh(tte); - ss->bestMove = move = ttMove; // Can be MOVE_NONE + ss->currentMove = ttMove; // Can be MOVE_NONE value = value_from_tt(tte->value(), ss->ply); if ( value >= beta - && move - && !pos.is_capture_or_promotion(move) - && move != ss->killers[0]) + && ttMove + && !pos.is_capture_or_promotion(ttMove) + && ttMove != ss->killers[0]) { ss->killers[1] = ss->killers[0]; - ss->killers[0] = move; + ss->killers[0] = ttMove; } return value; } @@ -650,7 +659,7 @@ namespace { else { refinedValue = ss->eval = evaluate(pos, ss->evalMargin); - TT.store(posKey, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, MOVE_NONE, ss->eval, ss->evalMargin); + TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->eval, ss->evalMargin); } // Update gain for the parent non-capture move given the static position @@ -744,7 +753,7 @@ namespace { // move which was reduced. If a connection is found, return a fail // low score (which will cause the reduced move to fail high in the // parent node, which will trigger a re-search with full depth). - threatMove = (ss+1)->bestMove; + threatMove = (ss+1)->currentMove; if ( depth < ThreatDepth && (ss-1)->reduction @@ -769,6 +778,8 @@ namespace { Depth rdepth = depth - ONE_PLY - 3 * ONE_PLY; assert(rdepth >= ONE_PLY); + assert((ss-1)->currentMove != MOVE_NONE); + assert((ss-1)->currentMove != MOVE_NULL); MovePicker mp(pos, ttMove, H, pos.captured_piece_type()); CheckInfo ci(pos); @@ -776,6 +787,7 @@ namespace { while ((move = mp.next_move()) != MOVE_NONE) if (pos.pl_move_is_legal(move, ci.pinned)) { + ss->currentMove = move; pos.do_move(move, st, ci, pos.move_gives_check(move, ci)); value = -search(pos, ss+1, -rbeta, -rbeta+1, rdepth); pos.undo_move(move); @@ -803,23 +815,14 @@ split_point_start: // At split points actual search starts from here MovePickerExt mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta); CheckInfo ci(pos); - ss->bestMove = MOVE_NONE; futilityBase = ss->eval + ss->evalMargin; singularExtensionNode = !RootNode && !SpNode && depth >= SingularExtensionDepth[PvNode] && ttMove != MOVE_NONE && !excludedMove // Recursive singular search is not allowed - && (tte->type() & VALUE_TYPE_LOWER) + && (tte->type() & BOUND_LOWER) && tte->depth() >= depth - 3 * ONE_PLY; - if (SpNode) - { - lock_grab(&(sp->lock)); - bestValue = sp->bestValue; - moveCount = sp->moveCount; - - assert(bestValue > -VALUE_INFINITE && moveCount > 0); - } // Step 11. Loop through moves // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs @@ -846,7 +849,7 @@ split_point_start: // At split points actual search starts from here if (SpNode) { moveCount = ++sp->moveCount; - lock_release(&(sp->lock)); + lock_release(sp->lock); } else moveCount++; @@ -894,7 +897,6 @@ split_point_start: // At split points actual search starts from here value = search(pos, ss, rBeta - 1, rBeta, depth / 2); ss->skipNullMove = false; ss->excludedMove = MOVE_NONE; - ss->bestMove = MOVE_NONE; if (value < rBeta) ext = ONE_PLY; } @@ -917,7 +919,7 @@ split_point_start: // At split points actual search starts from here && (!threatMove || !connected_threat(pos, move, threatMove))) { if (SpNode) - lock_grab(&(sp->lock)); + lock_grab(sp->lock); continue; } @@ -932,7 +934,7 @@ split_point_start: // At split points actual search starts from here if (futilityValue < beta) { if (SpNode) - lock_grab(&(sp->lock)); + lock_grab(sp->lock); continue; } @@ -942,7 +944,7 @@ split_point_start: // At split points actual search starts from here && pos.see_sign(move) < 0) { if (SpNode) - lock_grab(&(sp->lock)); + lock_grab(sp->lock); continue; } @@ -973,11 +975,10 @@ split_point_start: // At split points actual search starts from here && ss->killers[1] != move) { ss->reduction = reduction(depth, moveCount); - Depth d = newDepth - ss->reduction; + Depth d = std::max(newDepth - ss->reduction, ONE_PLY); alpha = SpNode ? sp->alpha : alpha; - value = d < ONE_PLY ? -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -(alpha+1), -alpha, d); + value = -search(pos, ss+1, -(alpha+1), -alpha, d); doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO); ss->reduction = DEPTH_ZERO; @@ -1008,12 +1009,12 @@ split_point_start: // At split points actual search starts from here // Step 18. Check for new best move if (SpNode) { - lock_grab(&(sp->lock)); + lock_grab(sp->lock); bestValue = sp->bestValue; alpha = sp->alpha; } - // Finished searching the move. If StopRequest is true, the search + // Finished searching the move. If Signals.stop 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 // be trusted, and we don't update the best move and/or PV. @@ -1044,7 +1045,7 @@ split_point_start: // At split points actual search starts from here if (value > bestValue) { bestValue = value; - ss->bestMove = move; + bestMove = move; if ( PvNode && value > alpha @@ -1054,9 +1055,11 @@ split_point_start: // At split points actual search starts from here if (SpNode && !thread.cutoff_occurred()) { sp->bestValue = value; - sp->ss->bestMove = move; + sp->bestMove = move; sp->alpha = alpha; - sp->is_betaCutoff = (value >= beta); + + if (value >= beta) + sp->cutoff = true; } } @@ -1067,14 +1070,14 @@ split_point_start: // At split points actual search starts from here && Threads.available_slave_exists(pos.thread()) && !Signals.stop && !thread.cutoff_occurred()) - bestValue = Threads.split(pos, ss, alpha, beta, bestValue, depth, - threatMove, moveCount, &mp, NT); + bestValue = Threads.split(pos, ss, alpha, beta, bestValue, &bestMove, + depth, threatMove, moveCount, &mp, NT); } // Step 20. Check for mate and stalemate // All legal moves have been searched and if there are no legal moves, it // must be mate or stalemate. Note that we can have a false positive in - // case of StopRequest or thread.cutoff_occurred() are set, but this is + // case of Signals.stop or thread.cutoff_occurred() are set, but this is // harmless because return value is discarded anyhow in the parent nodes. // If we are in a singular extension search then return a fail low score. if (!moveCount) @@ -1085,18 +1088,18 @@ split_point_start: // At split points actual search starts from here { assert(!playedMoveCount); - bestValue = alpha; + bestValue = oldAlpha; } // Step 21. Update tables // Update transposition table entry, killers and history if (!SpNode && !Signals.stop && !thread.cutoff_occurred()) { - move = bestValue <= oldAlpha ? MOVE_NONE : ss->bestMove; - vt = bestValue <= oldAlpha ? VALUE_TYPE_UPPER - : bestValue >= beta ? VALUE_TYPE_LOWER : VALUE_TYPE_EXACT; + move = bestValue <= oldAlpha ? MOVE_NONE : bestMove; + bt = bestValue <= oldAlpha ? BOUND_UPPER + : bestValue >= beta ? BOUND_LOWER : BOUND_EXACT; - TT.store(posKey, value_to_tt(bestValue, ss->ply), vt, depth, move, ss->eval, ss->evalMargin); + TT.store(posKey, value_to_tt(bestValue, ss->ply), bt, depth, move, ss->eval, ss->evalMargin); // Update killers and history for non capture cut-off moves if ( bestValue >= beta @@ -1122,14 +1125,6 @@ split_point_start: // At split points actual search starts from here } } - if (SpNode) - { - // Here we have the lock still grabbed - sp->is_slave[pos.thread()] = false; - sp->nodes += pos.nodes_searched(); - lock_release(&(sp->lock)); - } - assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); return bestValue; @@ -1147,20 +1142,20 @@ split_point_start: // At split points actual search starts from here assert(NT == PV || NT == NonPV); assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); - assert(PvNode == (alpha != beta - 1)); + assert((alpha == beta - 1) || PvNode); assert(depth <= DEPTH_ZERO); assert(pos.thread() >= 0 && pos.thread() < Threads.size()); StateInfo st; - Move ttMove, move; + Move ttMove, move, bestMove; Value bestValue, value, evalMargin, futilityValue, futilityBase; bool inCheck, enoughMaterial, givesCheck, evasionPrunable; const TTEntry* tte; Depth ttDepth; - ValueType vt; + Bound bt; Value oldAlpha = alpha; - ss->bestMove = ss->currentMove = MOVE_NONE; + ss->currentMove = bestMove = MOVE_NONE; ss->ply = (ss-1)->ply + 1; // Check for an instant draw or maximum ply reached @@ -1180,7 +1175,7 @@ split_point_start: // At split points actual search starts from here if (!PvNode && tte && can_return_tt(tte, ttDepth, beta, ss->ply)) { - ss->bestMove = ttMove; // Can be MOVE_NONE + ss->currentMove = ttMove; // Can be MOVE_NONE return value_from_tt(tte->value(), ss->ply); } @@ -1207,7 +1202,7 @@ split_point_start: // At split points actual search starts from here if (bestValue >= beta) { if (!tte) - TT.store(pos.key(), value_to_tt(bestValue, ss->ply), VALUE_TYPE_LOWER, DEPTH_NONE, MOVE_NONE, ss->eval, evalMargin); + TT.store(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER, DEPTH_NONE, MOVE_NONE, ss->eval, evalMargin); return bestValue; } @@ -1284,13 +1279,8 @@ split_point_start: // At split points actual search starts from here && move != ttMove && !pos.is_capture_or_promotion(move) && ss->eval + PawnValueMidgame / 4 < beta - && !check_is_dangerous(pos, move, futilityBase, beta, &bestValue)) - { - if (ss->eval + PawnValueMidgame / 4 > bestValue) - bestValue = ss->eval + PawnValueMidgame / 4; - + && !check_is_dangerous(pos, move, futilityBase, beta)) continue; - } // Check for legality only before to do the move if (!pos.pl_move_is_legal(move, ci.pinned)) @@ -1309,7 +1299,7 @@ split_point_start: // At split points actual search starts from here if (value > bestValue) { bestValue = value; - ss->bestMove = move; + bestMove = move; if ( PvNode && value > alpha @@ -1324,11 +1314,11 @@ split_point_start: // At split points actual search starts from here return mated_in(ss->ply); // Plies to mate from the root // Update transposition table - move = bestValue <= oldAlpha ? MOVE_NONE : ss->bestMove; - vt = bestValue <= oldAlpha ? VALUE_TYPE_UPPER - : bestValue >= beta ? VALUE_TYPE_LOWER : VALUE_TYPE_EXACT; + move = bestValue <= oldAlpha ? MOVE_NONE : bestMove; + bt = bestValue <= oldAlpha ? BOUND_UPPER + : bestValue >= beta ? BOUND_LOWER : BOUND_EXACT; - TT.store(pos.key(), value_to_tt(bestValue, ss->ply), vt, ttDepth, move, ss->eval, evalMargin); + TT.store(pos.key(), value_to_tt(bestValue, ss->ply), bt, ttDepth, move, ss->eval, evalMargin); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1340,20 +1330,19 @@ split_point_start: // At split points actual search starts from here // bestValue is updated only when returning false because in that case move // will be pruned. - bool check_is_dangerous(Position &pos, Move move, Value futilityBase, Value beta, Value *bestValue) + bool check_is_dangerous(Position &pos, Move move, Value futilityBase, Value beta) { Bitboard b, occ, oldAtt, newAtt, kingAtt; - Square from, to, ksq, victimSq; + Square from, to, ksq; Piece pc; Color them; - Value futilityValue, bv = *bestValue; from = from_sq(move); to = to_sq(move); them = ~pos.side_to_move(); ksq = pos.king_square(them); kingAtt = pos.attacks_from(ksq); - pc = pos.piece_on(from); + pc = pos.piece_moved(move); occ = pos.occupied_squares() & ~(1ULL << from) & ~(1ULL << ksq); oldAtt = pos.attacks_from(pc, from, occ); @@ -1366,29 +1355,18 @@ split_point_start: // At split points actual search starts from here return true; // Rule 2. Queen contact check is very dangerous - if ( type_of(pc) == QUEEN - && bit_is_set(kingAtt, to)) + if (type_of(pc) == QUEEN && (kingAtt & to)) return true; // Rule 3. Creating new double threats with checks b = pos.pieces(them) & newAtt & ~oldAtt & ~(1ULL << ksq); - while (b) { - victimSq = pop_1st_bit(&b); - futilityValue = futilityBase + PieceValueEndgame[pos.piece_on(victimSq)]; - // Note that here we generate illegal "double move"! - if ( futilityValue >= beta - && pos.see_sign(make_move(from, victimSq)) >= 0) + if (futilityBase + PieceValueEndgame[pos.piece_on(pop_1st_bit(&b))] >= beta) return true; - - if (futilityValue > bv) - bv = futilityValue; } - // Update bestValue only if check is not dangerous (because we will prune the move) - *bestValue = bv; return false; } @@ -1422,25 +1400,21 @@ split_point_start: // At split points actual search starts from here // Case 3: Moving through the vacated square p2 = pos.piece_on(f2); - if ( piece_is_slider(p2) - && bit_is_set(squares_between(f2, t2), f1)) + if (piece_is_slider(p2) && (squares_between(f2, t2) & f1)) return true; // Case 4: The destination square for m2 is defended by the moving piece in m1 p1 = pos.piece_on(t1); - if (bit_is_set(pos.attacks_from(p1, t1), t2)) + if (pos.attacks_from(p1, t1) & t2) return true; // Case 5: Discovered check, checking piece is the piece moved in m1 ksq = pos.king_square(pos.side_to_move()); if ( piece_is_slider(p1) - && bit_is_set(squares_between(t1, ksq), f2)) - { - Bitboard occ = pos.occupied_squares(); - clear_bit(&occ, f2); - if (bit_is_set(pos.attacks_from(p1, t1, occ), ksq)) - return true; - } + && (squares_between(t1, ksq) & f2) + && (pos.attacks_from(p1, t1, pos.occupied_squares() ^ f2) & ksq)) + return true; + return false; } @@ -1508,9 +1482,9 @@ split_point_start: // At split points actual search starts from here // 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) - && pos.see_sign(m) >= 0) + if ( piece_is_slider(pos.piece_on(tfrom)) + && (squares_between(tfrom, tto) & mto) + && pos.see_sign(m) >= 0) return true; return false; @@ -1528,8 +1502,8 @@ split_point_start: // At split points actual search starts from here || v >= std::max(VALUE_MATE_IN_MAX_PLY, beta) || v < std::min(VALUE_MATED_IN_MAX_PLY, beta)) - && ( ((tte->type() & VALUE_TYPE_LOWER) && v >= beta) - || ((tte->type() & VALUE_TYPE_UPPER) && v < beta)); + && ( ((tte->type() & BOUND_LOWER) && v >= beta) + || ((tte->type() & BOUND_UPPER) && v < beta)); } @@ -1542,8 +1516,8 @@ split_point_start: // At split points actual search starts from here Value v = value_from_tt(tte->value(), ply); - if ( ((tte->type() & VALUE_TYPE_LOWER) && v >= defaultEval) - || ((tte->type() & VALUE_TYPE_UPPER) && v < defaultEval)) + if ( ((tte->type() & BOUND_LOWER) && v >= defaultEval) + || ((tte->type() & BOUND_UPPER) && v < defaultEval)) return v; return defaultEval; @@ -1762,9 +1736,9 @@ split_point_start: // At split points actual search starts from here /// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table. -/// We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes so -/// to allow to always have a ponder move even when we fail high at root, and -/// a long PV to print that is important for position analysis. +/// We consider also failing high nodes and not only BOUND_EXACT nodes so to +/// allow to always have a ponder move even when we fail high at root, and a +/// long PV to print that is important for position analysis. void RootMove::extract_pv_from_tt(Position& pos) { @@ -1780,14 +1754,14 @@ void RootMove::extract_pv_from_tt(Position& pos) { pos.do_move(m, *st++); while ( (tte = TT.probe(pos.key())) != NULL - && tte->move() != MOVE_NONE - && pos.is_pseudo_legal(tte->move()) - && pos.pl_move_is_legal(tte->move(), pos.pinned_pieces()) + && (m = tte->move()) != MOVE_NONE // Local copy, TT entry could change + && pos.is_pseudo_legal(m) + && pos.pl_move_is_legal(m, pos.pinned_pieces()) && ply < MAX_PLY && (!pos.is_draw() || ply < 2)) { - pv.push_back(tte->move()); - pos.do_move(tte->move(), *st++); + pv.push_back(m); + pos.do_move(m, *st++); ply++; } pv.push_back(MOVE_NONE); @@ -1818,7 +1792,7 @@ void RootMove::insert_pv_in_tt(Position& pos) { if (!tte || tte->move() != pv[ply]) { v = (pos.in_check() ? VALUE_NONE : evaluate(pos, m)); - TT.store(k, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[ply], v, m); + TT.store(k, VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], v, m); } pos.do_move(pv[ply], *st++); @@ -1829,34 +1803,34 @@ void RootMove::insert_pv_in_tt(Position& pos) { /// Thread::idle_loop() is where the thread is parked when it has no work to do. -/// The parameter 'sp', if non-NULL, is a pointer to an active SplitPoint object -/// for which the thread is the master. +/// The parameter 'master_sp', if non-NULL, is a pointer to an active SplitPoint +/// object for which the thread is the master. -void Thread::idle_loop(SplitPoint* sp) { +void Thread::idle_loop(SplitPoint* sp_master) { - while (true) + // If this thread is the master of a split point and all slaves have + // finished their work at this split point, return from the idle loop. + while (!sp_master || sp_master->slavesMask) { // If we are not searching, wait for a condition to be signaled // instead of wasting CPU time polling for work. while ( do_sleep - || do_terminate - || (Threads.use_sleeping_threads() && !is_searching)) + || do_exit + || (!is_searching && Threads.use_sleeping_threads())) { - assert((!sp && threadID) || Threads.use_sleeping_threads()); - - if (do_terminate) + if (do_exit) { - assert(!sp); + assert(!sp_master); return; } // Grab the lock to avoid races with Thread::wake_up() - lock_grab(&sleepLock); + lock_grab(sleepLock); // If we are master and all slaves have finished don't go to sleep - if (sp && Threads.split_point_finished(sp)) + if (sp_master && !sp_master->slavesMask) { - lock_release(&sleepLock); + lock_release(sleepLock); break; } @@ -1865,54 +1839,58 @@ void Thread::idle_loop(SplitPoint* sp) { // in the meanwhile, allocated us and sent the wake_up() call before we // had the chance to grab the lock. if (do_sleep || !is_searching) - cond_wait(&sleepCond, &sleepLock); + cond_wait(sleepCond, sleepLock); - lock_release(&sleepLock); + lock_release(sleepLock); } // If this thread has been assigned work, launch a search if (is_searching) { - assert(!do_terminate); + assert(!do_sleep && !do_exit); + + lock_grab(Threads.splitLock); + + assert(is_searching); + SplitPoint* sp = curSplitPoint; + + lock_release(Threads.splitLock); - // Copy split point position and search stack and call search() Stack ss[MAX_PLY_PLUS_2]; - SplitPoint* tsp = splitPoint; - Position pos(*tsp->pos, threadID); - - memcpy(ss, tsp->ss - 1, 4 * sizeof(Stack)); - (ss+1)->sp = tsp; - - if (tsp->nodeType == Root) - search(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth); - else if (tsp->nodeType == PV) - search(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth); - else if (tsp->nodeType == NonPV) - search(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth); + Position pos(*sp->pos, threadID); + int master = sp->master; + + memcpy(ss, sp->ss - 1, 4 * sizeof(Stack)); + (ss+1)->sp = sp; + + lock_grab(sp->lock); + + if (sp->nodeType == Root) + search(pos, ss+1, sp->alpha, sp->beta, sp->depth); + else if (sp->nodeType == PV) + search(pos, ss+1, sp->alpha, sp->beta, sp->depth); + else if (sp->nodeType == NonPV) + search(pos, ss+1, sp->alpha, sp->beta, sp->depth); else assert(false); assert(is_searching); is_searching = false; + sp->slavesMask &= ~(1ULL << threadID); + sp->nodes += pos.nodes_searched(); + + // After releasing the lock we cannot access anymore any SplitPoint + // related data in a reliably way becuase it could have been released + // under our feet by the sp master. + lock_release(sp->lock); // Wake up master thread so to allow it to return from the idle loop in // case we are the last slave of the split point. if ( Threads.use_sleeping_threads() - && threadID != tsp->master - && !Threads[tsp->master].is_searching) - Threads[tsp->master].wake_up(); - } - - // If this thread is the master of a split point and all slaves have - // finished their work at this split point, return from the idle loop. - if (sp && Threads.split_point_finished(sp)) - { - // Because sp->is_slave[] is reset under lock protection, - // be sure sp->lock has been released before to return. - lock_grab(&(sp->lock)); - lock_release(&(sp->lock)); - return; + && threadID != master + && !Threads[master].is_searching) + Threads[master].wake_up(); } } } @@ -1944,7 +1922,6 @@ void check_time() { || stillAtFirstMove; if ( (Limits.use_time_management() && noMoreTime) - || (Limits.maxTime && e >= Limits.maxTime) - /* missing nodes limit */ ) // FIXME + || (Limits.maxTime && e >= Limits.maxTime)) Signals.stop = true; }