X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=36bd0869301327d5cba8130e20ff824f4eeb246c;hp=9d29d2798737b81f76d8fa64ad130b97eac54ebd;hb=b76c04c0975326d3274d7d7fb6df4edef7a040b5;hpb=f80c50bcddfd02c1b93dcde067d6a7362dda53a2 diff --git a/src/search.cpp b/src/search.cpp index 9d29d279..36bd0869 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include "book.h" #include "evaluate.h" @@ -42,7 +41,7 @@ namespace Search { volatile SignalsType Signals; LimitsType Limits; - std::vector SearchMoves; + std::vector RootMoves; Position RootPosition; } @@ -59,33 +58,6 @@ namespace { // Different node types, used as template parameter enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV }; - // RootMove struct is used for moves at the root of the tree. For each root - // move we store a score, a node count, and a PV (really a refutation in the - // case of moves which fail low). Score is normally set at -VALUE_INFINITE for - // all non-pv moves. - struct RootMove { - - RootMove(){} - RootMove(Move m) { - score = prevScore = -VALUE_INFINITE; - pv.push_back(m); - pv.push_back(MOVE_NONE); - } - - bool operator<(const RootMove& m) const { return score < m.score; } - bool operator==(const Move& m) const { return pv[0] == m; } - - void extract_pv_from_tt(Position& pos); - void insert_pv_in_tt(Position& pos); - - Value score; - Value prevScore; - std::vector pv; - }; - - - /// Constants - // Lookup table to check if a Piece is a slider and its access function const bool Slidings[18] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1 }; inline bool piece_is_slider(Piece p) { return Slidings[p]; } @@ -135,17 +107,14 @@ namespace { return (Depth) Reductions[PvNode][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)]; } - // Easy move margin. An easy move candidate must be at least this much - // better than the second best move. + // Easy move margin. An easy move candidate must be at least this much better + // than the second best move. const Value EasyMoveMargin = Value(0x150); // This is the minimum interval in msec between two check_time() calls const int TimerResolution = 5; - /// Namespace variables - - std::vector RootMoves; size_t MultiPV, UCIMultiPV, PVIdx; TimeManager TimeMgr; int BestMoveChanges; @@ -154,8 +123,6 @@ namespace { History H; - /// Local functions - template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); @@ -200,7 +167,7 @@ namespace { FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) { // Test for a pawn pushed to 7th or a passed pawn move - if (type_of(pos.piece_on(from_sq(m))) == PAWN) + if (type_of(pos.piece_moved(m)) == PAWN) { Color c = pos.side_to_move(); if ( relative_rank(c, to_sq(m)) == RANK_7 @@ -288,13 +255,15 @@ void Search::think() { TimeMgr.init(Limits, pos.startpos_ply_counter()); TT.new_search(); H.clear(); - RootMoves.clear(); - // Populate RootMoves with all the legal moves (default) or, if a SearchMoves - // is given, with the subset of legal moves to search. - for (MoveList ml(pos); !ml.end(); ++ml) - if (SearchMoves.empty() || count(SearchMoves.begin(), SearchMoves.end(), ml.move())) - RootMoves.push_back(RootMove(ml.move())); + if (RootMoves.empty()) + { + cout << "info depth 0 score " + << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl; + + RootMoves.push_back(MOVE_NONE); + goto finalize; + } if (Options["OwnBook"]) { @@ -375,9 +344,9 @@ void Search::think() { finalize: - // When we reach max depth we arrive here even without a StopRequest, but if - // we are pondering or in infinite search, we shouldn't print the best move - // before we are told to do so. + // When we reach max depth we arrive here even without Signals.stop is raised, + // 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(); @@ -406,16 +375,6 @@ namespace { bestValue = delta = -VALUE_INFINITE; ss->currentMove = MOVE_NULL; // Hack to skip update gains - // Handle the special case of a mated/stalemate position - if (RootMoves.empty()) - { - cout << "info depth 0 score " - << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl; - - RootMoves.push_back(MOVE_NONE); - return; - } - // Iterative deepening loop until requested to stop or target depth reached while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.maxDepth || depth <= Limits.maxDepth)) { @@ -531,15 +490,15 @@ namespace { stop = true; // Stop search early if one move seems to be much better than others - if ( depth >= 10 + if ( depth >= 12 && !stop - && ( bestMoveNeverChanged + && ( (bestMoveNeverChanged && pos.captured_piece_type()) || elapsed_time() > (TimeMgr.available_time() * 40) / 100)) { Value rBeta = bestValue - EasyMoveMargin; (ss+1)->excludedMove = RootMoves[0].pv[0]; (ss+1)->skipNullMove = true; - Value v = search(pos, ss+1, rBeta - 1, rBeta, (depth * ONE_PLY) / 2); + Value v = search(pos, ss+1, rBeta - 1, rBeta, (depth - 3) * ONE_PLY); (ss+1)->skipNullMove = false; (ss+1)->excludedMove = MOVE_NONE; @@ -585,7 +544,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()); @@ -595,7 +554,7 @@ namespace { Key posKey; Move ttMove, move, excludedMove, threatMove; Depth ext, newDepth; - ValueType vt; + Bound bt; Value bestValue, value, oldAlpha; Value refinedValue, nullValue, futilityBase, futilityValue; bool isPvMove, inCheck, singularExtensionNode, givesCheck; @@ -614,22 +573,32 @@ 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; 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 = 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; + + } // 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) @@ -661,7 +630,7 @@ 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); @@ -693,7 +662,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 @@ -701,7 +670,7 @@ namespace { if ( (move = (ss-1)->currentMove) != MOVE_NULL && (ss-1)->eval != VALUE_NONE && ss->eval != VALUE_NONE - && pos.captured_piece_type() == NO_PIECE_TYPE + && !pos.captured_piece_type() && !is_special(move)) { Square to = to_sq(move); @@ -812,6 +781,7 @@ namespace { Depth rdepth = depth - ONE_PLY - 3 * ONE_PLY; assert(rdepth >= ONE_PLY); + assert((ss-1)->currentMove != MOVE_NONE); MovePicker mp(pos, ttMove, H, pos.captured_piece_type()); CheckInfo ci(pos); @@ -819,6 +789,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); @@ -853,16 +824,8 @@ split_point_start: // At split points actual search starts from here && 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 @@ -889,7 +852,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++; @@ -960,7 +923,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; } @@ -970,12 +933,12 @@ split_point_start: // At split points actual search starts from here // but fixing this made program slightly weaker. Depth predictedDepth = newDepth - reduction(depth, moveCount); futilityValue = futilityBase + futility_margin(predictedDepth, moveCount) - + H.gain(pos.piece_on(from_sq(move)), to_sq(move)); + + H.gain(pos.piece_moved(move), to_sq(move)); if (futilityValue < beta) { if (SpNode) - lock_grab(&(sp->lock)); + lock_grab(sp->lock); continue; } @@ -985,7 +948,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; } @@ -1016,11 +979,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; @@ -1051,12 +1013,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. @@ -1117,7 +1079,7 @@ split_point_start: // At split points actual search starts from here // 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) @@ -1136,10 +1098,10 @@ split_point_start: // At split points actual search starts from here 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; + 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 @@ -1154,25 +1116,17 @@ split_point_start: // At split points actual search starts from here // Increase history value of the cut-off move Value bonus = Value(int(depth) * int(depth)); - H.add(pos.piece_on(from_sq(move)), to_sq(move), bonus); + H.add(pos.piece_moved(move), to_sq(move), bonus); // Decrease history of all the other played non-capture moves for (int i = 0; i < playedMoveCount - 1; i++) { Move m = movesSearched[i]; - H.add(pos.piece_on(from_sq(m)), to_sq(m), -bonus); + H.add(pos.piece_moved(m), to_sq(m), -bonus); } } } - 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; @@ -1190,7 +1144,7 @@ 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()); @@ -1200,7 +1154,7 @@ split_point_start: // At split points actual search starts from here bool inCheck, enoughMaterial, givesCheck, evasionPrunable; const TTEntry* tte; Depth ttDepth; - ValueType vt; + Bound bt; Value oldAlpha = alpha; ss->bestMove = ss->currentMove = MOVE_NONE; @@ -1250,7 +1204,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; } @@ -1368,10 +1322,10 @@ split_point_start: // At split points actual search starts from here // Update transposition table move = bestValue <= oldAlpha ? MOVE_NONE : ss->bestMove; - vt = bestValue <= oldAlpha ? VALUE_TYPE_UPPER - : bestValue >= beta ? VALUE_TYPE_LOWER : VALUE_TYPE_EXACT; + 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); @@ -1393,7 +1347,7 @@ split_point_start: // At split points actual search starts from here from = from_sq(move); to = to_sq(move); - them = flip(pos.side_to_move()); + them = ~pos.side_to_move(); ksq = pos.king_square(them); kingAtt = pos.attacks_from(ksq); pc = pos.piece_on(from); @@ -1571,8 +1525,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)); } @@ -1585,8 +1539,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; @@ -1801,105 +1755,105 @@ split_point_start: // At split points actual search starts from here return best; } +} // namespace - // extract_pv_from_tt() builds a PV by adding moves from the transposition table. - // We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes. This - // allow to always have a ponder move even when we fail high at root and also a - // long PV to print that is important for position analysis. - void RootMove::extract_pv_from_tt(Position& pos) { +/// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table. +/// 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. - StateInfo state[MAX_PLY_PLUS_2], *st = state; - TTEntry* tte; - int ply = 1; - Move m = pv[0]; - - assert(m != MOVE_NONE && pos.is_pseudo_legal(m)); - - pv.clear(); - pv.push_back(m); - 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()) - && ply < MAX_PLY - && (!pos.is_draw() || ply < 2)) - { - pv.push_back(tte->move()); - pos.do_move(tte->move(), *st++); - ply++; - } - pv.push_back(MOVE_NONE); +void RootMove::extract_pv_from_tt(Position& pos) { + + StateInfo state[MAX_PLY_PLUS_2], *st = state; + TTEntry* tte; + int ply = 1; + Move m = pv[0]; + + assert(m != MOVE_NONE && pos.is_pseudo_legal(m)); + + pv.clear(); + pv.push_back(m); + pos.do_move(m, *st++); - do pos.undo_move(pv[--ply]); while (ply); + 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()) + && ply < MAX_PLY + && (!pos.is_draw() || ply < 2)) + { + pv.push_back(tte->move()); + pos.do_move(tte->move(), *st++); + ply++; } + pv.push_back(MOVE_NONE); + do pos.undo_move(pv[--ply]); while (ply); +} - // insert_pv_in_tt() is called at the end of a search iteration, and inserts - // the PV back into the TT. This makes sure the old PV moves are searched - // first, even if the old TT entries have been overwritten. - void RootMove::insert_pv_in_tt(Position& pos) { +/// RootMove::insert_pv_in_tt() is called at the end of a search iteration, and +/// inserts the PV back into the TT. This makes sure the old PV moves are searched +/// first, even if the old TT entries have been overwritten. - StateInfo state[MAX_PLY_PLUS_2], *st = state; - TTEntry* tte; - Key k; - Value v, m = VALUE_NONE; - int ply = 0; +void RootMove::insert_pv_in_tt(Position& pos) { - assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply])); + StateInfo state[MAX_PLY_PLUS_2], *st = state; + TTEntry* tte; + Key k; + Value v, m = VALUE_NONE; + int ply = 0; - do { - k = pos.key(); - tte = TT.probe(k); + assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply])); - // Don't overwrite existing correct entries - 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); - } - pos.do_move(pv[ply], *st++); + do { + k = pos.key(); + tte = TT.probe(k); - } while (pv[++ply] != MOVE_NONE); + // Don't overwrite existing correct entries + if (!tte || tte->move() != pv[ply]) + { + v = (pos.in_check() ? VALUE_NONE : evaluate(pos, m)); + TT.store(k, VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], v, m); + } + pos.do_move(pv[ply], *st++); - do pos.undo_move(pv[--ply]); while (ply); - } + } while (pv[++ply] != MOVE_NONE); -} // namespace + do pos.undo_move(pv[--ply]); while (ply); +} /// 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; } @@ -1908,54 +1862,50 @@ 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); // 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); + SplitPoint* sp = splitPoint; + Position pos(*sp->pos, threadID); + + 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); + // We return from search with lock held + sp->slavesMask &= ~(1ULL << threadID); + sp->nodes += pos.nodes_searched(); + lock_release(sp->lock); + is_searching = false; // 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 != sp->master + && !Threads[sp->master].is_searching) + Threads[sp->master].wake_up(); } } } @@ -1983,11 +1933,10 @@ void check_time() { && !Signals.failedLowAtRoot && e > TimeMgr.available_time(); - bool noMoreTime = e > TimeMgr.maximum_time() - TimerResolution + bool noMoreTime = e > TimeMgr.maximum_time() - 2 * TimerResolution || stillAtFirstMove; if ( (Limits.use_time_management() && noMoreTime) - || (Limits.maxTime && e >= Limits.maxTime) - /* missing nodes limit */ ) // FIXME + || (Limits.maxTime && e >= Limits.maxTime)) Signals.stop = true; }