X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=3309947e1c20f91b1870adde6c8a0e7c5228e504;hp=93014bd0ac8a30bcf2288092192ab38638a35cd9;hb=3df2c01b5769c7ae996fb5b992c06e4a5428ad35;hpb=c19a6ef82d97840f3d5f06d587bce4f8b861b86d diff --git a/src/search.cpp b/src/search.cpp index 93014bd0..3309947e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -30,6 +29,7 @@ #include "history.h" #include "movegen.h" #include "movepick.h" +#include "notation.h" #include "search.h" #include "timeman.h" #include "thread.h" @@ -43,6 +43,7 @@ namespace Search { std::vector RootMoves; Position RootPosition; Time SearchTime; + StateStackPtr SetupStates; } using std::string; @@ -51,11 +52,6 @@ using std::endl; using Eval::evaluate; using namespace Search; -// For some reason argument-dependent lookup (ADL) doesn't work for Android's -// STLPort, so explicitly qualify following functions. -using std::count; -using std::find; - namespace { // Set to true to force running with one thread. Used for debugging @@ -144,48 +140,27 @@ namespace { bool connected_threat(const Position& pos, Move m, Move threat); Value refine_eval(const TTEntry* tte, Value ttValue, Value defaultEval); Move do_skill_level(); - string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE); - void pv_info_to_log(Position& pos, int depth, Value score, int time, Move pv[]); - void pv_info_to_uci(const Position& pos, int depth, Value alpha, Value beta); - - // MovePickerExt class template extends MovePicker and allows to choose at - // compile time the proper moves source according to the type of node. In the - // default case we simply create and use a standard MovePicker object. - template struct MovePickerExt : public MovePicker { - - MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, Stack* ss, Value b) - : MovePicker(p, ttm, d, h, ss, b) {} - }; - - // In case of a SpNode we use split point's shared MovePicker object as moves source - template<> struct MovePickerExt : public MovePicker { - - MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, Stack* ss, Value b) - : MovePicker(p, ttm, d, h, ss, b), mp(ss->sp->mp) {} - - Move next_move() { return mp->next_move(); } - MovePicker* mp; - }; + string uci_pv(const Position& pos, int depth, Value alpha, Value beta); // is_dangerous() checks whether a move belongs to some classes of known // 'dangerous' moves so that we avoid to prune it. 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_moved(m)) == PAWN) - { - Color c = pos.side_to_move(); - if ( relative_rank(c, to_sq(m)) == RANK_7 - || pos.pawn_is_passed(c, to_sq(m))) - return true; - } + // Castle move? + if (type_of(m) == CASTLE) + return true; + + // Passed pawn move? + if ( type_of(pos.piece_moved(m)) == PAWN + && pos.pawn_is_passed(pos.side_to_move(), to_sq(m))) + return true; - // Test for a capture that triggers a pawn endgame + // Entering a pawn endgame? if ( captureOrPromotion && type_of(pos.piece_on(to_sq(m))) != PAWN - && !is_special(m) + && type_of(m) == NORMAL && ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) - - PieceValueMidgame[pos.piece_on(to_sq(m))] == VALUE_ZERO)) + - PieceValue[Mg][pos.piece_on(to_sq(m))] == VALUE_ZERO)) return true; return false; @@ -224,24 +199,23 @@ void Search::init() { /// Search::perft() is our utility to verify move generation. All the leaf nodes /// up to the given depth are generated and counted and the sum returned. -int64_t Search::perft(Position& pos, Depth depth) { - - StateInfo st; - int64_t cnt = 0; - - MoveList ml(pos); +size_t Search::perft(Position& pos, Depth depth) { - // At the last ply just return the number of moves (leaf nodes) + // At the last ply just return the number of legal moves (leaf nodes) if (depth == ONE_PLY) - return ml.size(); + return MoveList(pos).size(); + StateInfo st; + size_t cnt = 0; CheckInfo ci(pos); - for ( ; !ml.end(); ++ml) + + for (MoveList ml(pos); !ml.end(); ++ml) { pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci)); cnt += perft(pos, depth - ONE_PLY); pos.undo_move(ml.move()); } + return cnt; } @@ -274,9 +248,9 @@ void Search::think() { { Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]); - if (bookMove && count(RootMoves.begin(), RootMoves.end(), bookMove)) + if (bookMove && std::count(RootMoves.begin(), RootMoves.end(), bookMove)) { - std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove)); + std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), bookMove)); goto finalize; } } @@ -427,7 +401,7 @@ namespace { // Send full PV info to GUI if we are going to leave the loop or // if we have a fail high/low and we are deep in the search. if ((bestValue > alpha && bestValue < beta) || SearchTime.elapsed() > 2000) - pv_info_to_uci(pos, depth, alpha, beta); + cout << uci_pv(pos, depth, alpha, beta) << endl; // In case of failing high/low increase aspiration window and // research, otherwise exit the fail high/low loop. @@ -457,7 +431,11 @@ namespace { skillBest = do_skill_level(); if (!Signals.stop && Options["Use Search Log"]) - pv_info_to_log(pos, depth, bestValue, SearchTime.elapsed(), &RootMoves[0].pv[0]); + { + Log log(Options["Search Log Filename"]); + log << pretty_pv(pos, depth, bestValue, SearchTime.elapsed(), &RootMoves[0].pv[0]) + << endl; + } // Filter out startup noise when monitoring best move stability if (depth > 2 && BestMoveChanges) @@ -513,7 +491,7 @@ namespace { if (skillBest == MOVE_NONE) // Still unassigned ? skillBest = do_skill_level(); - std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), skillBest)); + std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), skillBest)); } } @@ -661,7 +639,7 @@ namespace { && (ss-1)->eval != VALUE_NONE && ss->eval != VALUE_NONE && !pos.captured_piece_type() - && !is_special(move)) + && type_of(move) == NORMAL) { Square to = to_sq(move); H.update_gain(pos.piece_on(to), to, -(ss-1)->eval - ss->eval); @@ -708,16 +686,16 @@ namespace { ss->currentMove = MOVE_NULL; // Null move dynamic reduction based on depth - int R = 3 + (depth >= 5 * ONE_PLY ? depth / 8 : 0); + Depth R = 3 * ONE_PLY + depth / 4; // Null move dynamic reduction based on value - if (refinedValue - PawnValueMidgame > beta) - R++; + if (refinedValue - PawnValueMg > beta) + R += ONE_PLY; pos.do_null_move(st); (ss+1)->skipNullMove = true; - nullValue = depth-R*ONE_PLY < ONE_PLY ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -beta, -alpha, depth-R*ONE_PLY); + nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) + : - search(pos, ss+1, -beta, -alpha, depth-R); (ss+1)->skipNullMove = false; pos.do_null_move(st); @@ -732,7 +710,7 @@ namespace { // Do verification search at high depths ss->skipNullMove = true; - Value v = search(pos, ss, alpha, beta, depth-R*ONE_PLY); + Value v = search(pos, ss, alpha, beta, depth-R); ss->skipNullMove = false; if (v >= beta) @@ -777,7 +755,7 @@ namespace { MovePicker mp(pos, ttMove, H, pos.captured_piece_type()); CheckInfo ci(pos); - while ((move = mp.next_move()) != MOVE_NONE) + while ((move = mp.next_move()) != MOVE_NONE) if (pos.pl_move_is_legal(move, ci.pinned)) { ss->currentMove = move; @@ -806,7 +784,7 @@ namespace { split_point_start: // At split points actual search starts from here - MovePickerExt mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta); + MovePicker mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta); CheckInfo ci(pos); futilityBase = ss->eval + ss->evalMargin; singularExtensionNode = !RootNode @@ -820,7 +798,7 @@ split_point_start: // At split points actual search starts from here // Step 11. Loop through moves // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs while ( bestValue < beta - && (move = mp.next_move()) != MOVE_NONE + && (move = mp.next_move()) != MOVE_NONE && !thisThread->cutoff_occurred() && !Signals.stop) { @@ -832,7 +810,7 @@ split_point_start: // At split points actual search starts from here // At root obey the "searchmoves" option and skip moves not listed in Root // Move List, as a consequence any illegal move is also skipped. In MultiPV // mode we also skip PV moves which have been already searched. - if (RootNode && !count(RootMoves.begin() + PVIdx, RootMoves.end(), move)) + if (RootNode && !std::count(RootMoves.begin() + PVIdx, RootMoves.end(), move)) continue; // At PV and SpNode nodes we want all moves to be legal since the beginning @@ -842,7 +820,7 @@ split_point_start: // At split points actual search starts from here if (SpNode) { moveCount = ++sp->moveCount; - lock_release(sp->lock); + sp->mutex.unlock(); } else moveCount++; @@ -878,19 +856,18 @@ split_point_start: // At split points actual search starts from here if ( singularExtensionNode && !ext && move == ttMove - && pos.pl_move_is_legal(move, ci.pinned)) + && pos.pl_move_is_legal(move, ci.pinned) + && abs(ttValue) < VALUE_KNOWN_WIN) { - if (abs(ttValue) < VALUE_KNOWN_WIN) - { - Value rBeta = ttValue - int(depth); - ss->excludedMove = move; - ss->skipNullMove = true; - value = search(pos, ss, rBeta - 1, rBeta, depth / 2); - ss->skipNullMove = false; - ss->excludedMove = MOVE_NONE; - if (value < rBeta) - ext = ONE_PLY; - } + Value rBeta = ttValue - int(depth); + ss->excludedMove = move; + ss->skipNullMove = true; + value = search(pos, ss, rBeta - 1, rBeta, depth / 2); + ss->skipNullMove = false; + ss->excludedMove = MOVE_NONE; + + if (value < rBeta) + ext = ONE_PLY; } // Update current move (this must be done after singular extension search) @@ -902,7 +879,6 @@ split_point_start: // At split points actual search starts from here && !inCheck && !dangerous && move != ttMove - && !is_castle(move) && (bestValue > VALUE_MATED_IN_MAX_PLY || bestValue == -VALUE_INFINITE)) { // Move count based pruning @@ -910,7 +886,7 @@ split_point_start: // At split points actual search starts from here && (!threatMove || !connected_threat(pos, move, threatMove))) { if (SpNode) - lock_grab(sp->lock); + sp->mutex.lock(); continue; } @@ -925,7 +901,7 @@ split_point_start: // At split points actual search starts from here if (futilityValue < beta) { if (SpNode) - lock_grab(sp->lock); + sp->mutex.lock(); continue; } @@ -935,7 +911,7 @@ split_point_start: // At split points actual search starts from here && pos.see_sign(move) < 0) { if (SpNode) - lock_grab(sp->lock); + sp->mutex.lock(); continue; } @@ -961,7 +937,6 @@ split_point_start: // At split points actual search starts from here && !isPvMove && !captureOrPromotion && !dangerous - && !is_castle(move) && ss->killers[0] != move && ss->killers[1] != move) { @@ -1000,7 +975,7 @@ split_point_start: // At split points actual search starts from here // Step 18. Check for new best move if (SpNode) { - lock_grab(sp->lock); + sp->mutex.lock(); bestValue = sp->bestValue; alpha = sp->alpha; } @@ -1011,7 +986,7 @@ split_point_start: // At split points actual search starts from here // be trusted, and we don't update the best move and/or PV. if (RootNode && !Signals.stop) { - RootMove& rm = *find(RootMoves.begin(), RootMoves.end(), move); + RootMove& rm = *std::find(RootMoves.begin(), RootMoves.end(), move); // PV move or new best move ? if (isPvMove || value > alpha) @@ -1202,7 +1177,7 @@ split_point_start: // At split points actual search starts from here alpha = bestValue; futilityBase = ss->eval + evalMargin + FutilityMarginQS; - enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame; + enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMg; } // Initialize a MovePicker object for the current position, and prepare @@ -1214,7 +1189,7 @@ split_point_start: // At split points actual search starts from here // Loop through the moves until no moves remain or a beta cutoff occurs while ( bestValue < beta - && (move = mp.next_move()) != MOVE_NONE) + && (move = mp.next_move()) != MOVE_NONE) { assert(is_ok(move)); @@ -1226,12 +1201,12 @@ split_point_start: // At split points actual search starts from here && !givesCheck && move != ttMove && enoughMaterial - && !is_promotion(move) + && type_of(move) != PROMOTION && !pos.is_passed_pawn_push(move)) { futilityValue = futilityBase - + PieceValueEndgame[pos.piece_on(to_sq(move))] - + (is_enpassant(move) ? PawnValueEndgame : VALUE_ZERO); + + PieceValue[Eg][pos.piece_on(to_sq(move))] + + (type_of(move) == ENPASSANT ? PawnValueEg : VALUE_ZERO); if (futilityValue < beta) { @@ -1259,7 +1234,7 @@ split_point_start: // At split points actual search starts from here if ( !PvNode && (!inCheck || evasionPrunable) && move != ttMove - && !is_promotion(move) + && type_of(move) != PROMOTION && pos.see_sign(move) < 0) continue; @@ -1269,7 +1244,7 @@ split_point_start: // At split points actual search starts from here && givesCheck && move != ttMove && !pos.is_capture_or_promotion(move) - && ss->eval + PawnValueMidgame / 4 < beta + && ss->eval + PawnValueMg / 4 < beta && !check_is_dangerous(pos, move, futilityBase, beta)) continue; @@ -1354,7 +1329,7 @@ split_point_start: // At split points actual search starts from here while (b) { // Note that here we generate illegal "double move"! - if (futilityBase + PieceValueEndgame[pos.piece_on(pop_1st_bit(&b))] >= beta) + if (futilityBase + PieceValue[Eg][pos.piece_on(pop_lsb(&b))] >= beta) return true; } @@ -1466,7 +1441,7 @@ split_point_start: // At split points actual search starts from here // Case 2: If the threatened piece has value less than or equal to the // value of the threatening piece, don't prune moves which defend it. if ( pos.is_capture(threat) - && ( PieceValueMidgame[pos.piece_on(tfrom)] >= PieceValueMidgame[pos.piece_on(tto)] + && ( PieceValue[Mg][pos.piece_on(tfrom)] >= PieceValue[Mg][pos.piece_on(tto)] || type_of(pos.piece_on(tfrom)) == KING) && pos.move_attacks_square(m, tto)) return true; @@ -1511,156 +1486,6 @@ split_point_start: // At split points actual search starts from here } - // score_to_uci() converts a value to a string suitable for use with the UCI - // protocol specifications: - // - // cp The score from the engine's point of view in centipawns. - // mate Mate in y moves, not plies. If the engine is getting mated - // use negative values for y. - - string score_to_uci(Value v, Value alpha, Value beta) { - - std::stringstream s; - - if (abs(v) < VALUE_MATE_IN_MAX_PLY) - s << "cp " << v * 100 / int(PawnValueMidgame); - else - s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; - - s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); - - return s.str(); - } - - - // pv_info_to_uci() sends search info to GUI. UCI protocol requires to send all - // the PV lines also if are still to be searched and so refer to the previous - // search score. - - void pv_info_to_uci(const Position& pos, int depth, Value alpha, Value beta) { - - int t = SearchTime.elapsed(); - int selDepth = 0; - - for (int i = 0; i < Threads.size(); i++) - if (Threads[i].maxPly > selDepth) - selDepth = Threads[i].maxPly; - - for (size_t i = 0; i < std::min(UCIMultiPV, RootMoves.size()); i++) - { - bool updated = (i <= PVIdx); - - if (depth == 1 && !updated) - continue; - - int d = (updated ? depth : depth - 1); - Value v = (updated ? RootMoves[i].score : RootMoves[i].prevScore); - std::stringstream s; - - for (int j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++) - s << " " << move_to_uci(RootMoves[i].pv[j], Chess960); - - cout << "info depth " << d - << " seldepth " << selDepth - << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) - << " nodes " << pos.nodes_searched() - << " nps " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0) - << " time " << t - << " multipv " << i + 1 - << " pv" << s.str() << endl; - } - } - - - // pv_info_to_log() writes human-readable search information to the log file - // (which is created when the UCI parameter "Use Search Log" is "true"). It - // uses the two below helpers to pretty format time and score respectively. - - string time_to_string(int millisecs) { - - const int MSecMinute = 1000 * 60; - const int MSecHour = 1000 * 60 * 60; - - int hours = millisecs / MSecHour; - int minutes = (millisecs % MSecHour) / MSecMinute; - int seconds = ((millisecs % MSecHour) % MSecMinute) / 1000; - - std::stringstream s; - - if (hours) - s << hours << ':'; - - s << std::setfill('0') << std::setw(2) << minutes << ':' - << std::setw(2) << seconds; - return s.str(); - } - - string score_to_string(Value v) { - - std::stringstream s; - - if (v >= VALUE_MATE_IN_MAX_PLY) - s << "#" << (VALUE_MATE - v + 1) / 2; - else if (v <= VALUE_MATED_IN_MAX_PLY) - s << "-#" << (VALUE_MATE + v) / 2; - else - s << std::setprecision(2) << std::fixed << std::showpos - << float(v) / PawnValueMidgame; - - return s.str(); - } - - void pv_info_to_log(Position& pos, int depth, Value value, int time, Move pv[]) { - - const int64_t K = 1000; - const int64_t M = 1000000; - - StateInfo state[MAX_PLY_PLUS_2], *st = state; - Move* m = pv; - string san, padding; - size_t length; - std::stringstream s; - - s << std::setw(2) << depth - << std::setw(8) << score_to_string(value) - << std::setw(8) << time_to_string(time); - - if (pos.nodes_searched() < M) - s << std::setw(8) << pos.nodes_searched() / 1 << " "; - - else if (pos.nodes_searched() < K * M) - s << std::setw(7) << pos.nodes_searched() / K << "K "; - - else - s << std::setw(7) << pos.nodes_searched() / M << "M "; - - padding = string(s.str().length(), ' '); - length = padding.length(); - - while (*m != MOVE_NONE) - { - san = move_to_san(pos, *m); - - if (length + san.length() > 80) - { - s << "\n" + padding; - length = padding.length(); - } - - s << san << ' '; - length += san.length() + 1; - - pos.do_move(*m++, *st++); - } - - while (m != pv) - pos.undo_move(*--m); - - Log l(Options["Search Log Filename"]); - l << s.str() << endl; - } - - // When playing with strength handicap choose best move among the MultiPV set // using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen. @@ -1676,7 +1501,7 @@ split_point_start: // At split points actual search starts from here // RootMoves are already sorted by score in descending order size_t size = std::min(MultiPV, RootMoves.size()); - int variance = std::min(RootMoves[0].score - RootMoves[size - 1].score, PawnValueMidgame); + int variance = std::min(RootMoves[0].score - RootMoves[size - 1].score, PawnValueMg); int weakness = 120 - 2 * SkillLevel; int max_s = -VALUE_INFINITE; Move best = MOVE_NONE; @@ -1705,6 +1530,50 @@ split_point_start: // At split points actual search starts from here return best; } + + // uci_pv() formats PV information according to UCI protocol. UCI requires + // to send all the PV lines also if are still to be searched and so refer to + // the previous search score. + + string uci_pv(const Position& pos, int depth, Value alpha, Value beta) { + + std::stringstream s; + int t = SearchTime.elapsed(); + int selDepth = 0; + + for (size_t i = 0; i < Threads.size(); i++) + if (Threads[i].maxPly > selDepth) + selDepth = Threads[i].maxPly; + + for (size_t i = 0; i < std::min(UCIMultiPV, RootMoves.size()); i++) + { + bool updated = (i <= PVIdx); + + if (depth == 1 && !updated) + continue; + + int d = (updated ? depth : depth - 1); + Value v = (updated ? RootMoves[i].score : RootMoves[i].prevScore); + + if (s.rdbuf()->in_avail()) + s << "\n"; + + s << "info depth " << d + << " seldepth " << selDepth + << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) + << " nodes " << pos.nodes_searched() + << " nps " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0) + << " time " << t + << " multipv " << i + 1 + << " pv"; + + for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++) + s << " " << move_to_uci(RootMoves[i].pv[j], Chess960); + } + + return s.str(); + } + } // namespace @@ -1775,11 +1644,15 @@ 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 'master_sp', if non-NULL, is a pointer to an active SplitPoint -/// object for which the thread is the master. +/// Thread::idle_loop() is where the thread is parked when it has no work to do -void Thread::idle_loop(SplitPoint* sp_master) { +void Thread::idle_loop() { + + // Pointer 'sp_master', if non-NULL, points to the active SplitPoint + // object for which the thread is the master. + const SplitPoint* sp_master = splitPointsCnt ? curSplitPoint : NULL; + + assert(!sp_master || (sp_master->master == this && is_searching)); // 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. @@ -1798,12 +1671,12 @@ void Thread::idle_loop(SplitPoint* sp_master) { } // Grab the lock to avoid races with Thread::wake_up() - lock_grab(sleepLock); + mutex.lock(); // If we are master and all slaves have finished don't go to sleep if (sp_master && !sp_master->slavesMask) { - lock_release(sleepLock); + mutex.unlock(); break; } @@ -1812,9 +1685,9 @@ void Thread::idle_loop(SplitPoint* sp_master) { // 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); + sleepCondition.wait(mutex); - lock_release(sleepLock); + mutex.unlock(); } // If this thread has been assigned work, launch a search @@ -1822,12 +1695,12 @@ void Thread::idle_loop(SplitPoint* sp_master) { { assert(!do_sleep && !do_exit); - lock_grab(Threads.splitLock); + Threads.mutex.lock(); assert(is_searching); SplitPoint* sp = curSplitPoint; - lock_release(Threads.splitLock); + Threads.mutex.unlock(); Stack ss[MAX_PLY_PLUS_2]; Position pos(*sp->pos, this); @@ -1835,7 +1708,7 @@ void Thread::idle_loop(SplitPoint* sp_master) { memcpy(ss, sp->ss - 1, 4 * sizeof(Stack)); (ss+1)->sp = sp; - lock_grab(sp->lock); + sp->mutex.lock(); if (sp->nodeType == Root) search(pos, ss+1, sp->alpha, sp->beta, sp->depth); @@ -1856,14 +1729,17 @@ void Thread::idle_loop(SplitPoint* sp_master) { // case we are the last slave of the split point. if ( Threads.use_sleeping_threads() && this != sp->master - && !sp->master->is_searching) + && !sp->slavesMask) + { + assert(!sp->master->is_searching); sp->master->wake_up(); + } // After releasing the lock we cannot access anymore any SplitPoint // related data in a safe way becuase it could have been released under // our feet by the sp master. Also accessing other Thread objects is // unsafe because if we are exiting there is a chance are already freed. - lock_release(sp->lock); + sp->mutex.unlock(); } } }