Esempio n. 1
0
        /// 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.
        internal void idle_loop(SplitPoint sp_master, ManualResetEvent initEvent)
        {
            if (initEvent != null)
            {
                // Signal done
                initEvent.Set();
            }

            bool use_sleeping_threads = Threads.useSleepingThreads;

            // 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 == null) || (sp_master.slavesMask != 0))
            {
                // If we are not searching, wait for a condition to be signaled
                // instead of wasting CPU time polling for work.
                while (do_sleep ||
                       do_exit ||
                       (!is_searching && use_sleeping_threads))
                {
                    if (do_exit)
                    {
                        Debug.Assert(sp_master == null);
                        return;
                    }

                    // Grab the lock to avoid races with Thread::wake_up()
                    ThreadHelper.lock_grab(sleepLock);

                    // If we are master and all slaves have finished don't go to sleep
                    if ((sp_master != null) && (sp_master.slavesMask == 0))
                    {
                        ThreadHelper.lock_release(sleepLock);
                        break;
                    }

                    // Do sleep after retesting sleep conditions under lock protection, in
                    // particular we need to avoid a deadlock in case a master thread has,
                    // 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)
                    {
                        ThreadHelper.cond_wait(sleepCond, sleepLock);
                    }

                    ThreadHelper.lock_release(sleepLock);
                }

                // If this thread has been assigned work, launch a search
                if (is_searching)
                {
                    Debug.Assert(!do_sleep && !do_exit);

                    ThreadHelper.lock_grab(Threads.splitLock);

                    Debug.Assert(is_searching);
                    SplitPoint sp = curSplitPoint;

                    ThreadHelper.lock_release(Threads.splitLock);

                    LoopStack ls    = LoopStackBroker.GetObject();
                    Stack[]   ss    = ls.ss;
                    int       ssPos = 0;

                    Position pos = PositionBroker.GetObject();
                    pos.copy(sp.pos, this);

                    Array.Copy(sp.ss, sp.ssPos - 1, ss, ssPos, 4);
                    ss[ssPos + 1].sp = sp;

                    ThreadHelper.lock_grab(sp.Lock);

                    if (sp.nodeType == NodeTypeC.Root)
                    {
                        Search.search(NodeTypeC.SplitPointRoot, pos, ss, ssPos + 1, sp.alpha, sp.beta, sp.depth);
                    }
                    else if (sp.nodeType == NodeTypeC.PV)
                    {
                        Search.search(NodeTypeC.SplitPointPV, pos, ss, ssPos + 1, sp.alpha, sp.beta, sp.depth);
                    }
                    else if (sp.nodeType == NodeTypeC.NonPV)
                    {
                        Search.search(NodeTypeC.SplitPointNonPV, pos, ss, ssPos + 1, sp.alpha, sp.beta, sp.depth);
                    }
                    else
                    {
                        Debug.Assert(false);
                    }

                    Debug.Assert(is_searching);

                    is_searching = false;
#if ACTIVE_REPARENT
                    sp.allSlavesRunning = false;
#endif
                    sp.slavesMask &= ~(1UL << idx);
                    sp.nodes      += pos.nodes;

                    // 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 (use_sleeping_threads &&
                        this != sp.master &&
                        !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.
                    ThreadHelper.lock_release(sp.Lock);

#if ACTIVE_REPARENT
                    // Try to reparent to the first split point, with still all slaves
                    // running, where we are available as a possible slave.
                    for (int i = 0; i < Threads.size(); i++)
                    {
                        Thread     th     = Threads.threads[i];
                        int        spCnt  = th.splitPointsCnt;
                        SplitPoint latest = th.splitPoints[spCnt != 0 ? spCnt - 1 : 0];

                        if (this.is_available_to(th) &&
                            spCnt > 0 &&
                            !th.cutoff_occurred() &&
                            latest.allSlavesRunning &&
                            Utils.more_than_one(latest.slavesMask))
                        {
                            ThreadHelper.lock_grab(latest.Lock);
                            ThreadHelper.lock_grab(Threads.splitLock);

                            // Retest all under lock protection, we are in the middle
                            // of a race storm here !
                            if (this.is_available_to(th) &&
                                spCnt == th.splitPointsCnt &&
                                !th.cutoff_occurred() &&
                                latest.allSlavesRunning &&
                                Utils.more_than_one(latest.slavesMask))
                            {
                                latest.slavesMask |= 1UL << idx;
                                curSplitPoint      = latest;
                                is_searching       = true;
                            }

                            ThreadHelper.lock_release(Threads.splitLock);
                            ThreadHelper.lock_release(latest.Lock);

                            break; // Exit anyhow, only one try (enough in 99% of cases)
                        }
                    }
#endif

                    pos.startState = null;
                    pos.st         = null;
                    PositionBroker.Free();
                    LoopStackBroker.Free(ls);
                }
            }
        }
Esempio n. 2
0
        // split() does the actual work of distributing the work at a node between
        // several available threads. If it does not succeed in splitting the node
        // (because no idle threads are available, or because we have no unused split
        // point objects), the function immediately returns. If splitting is possible, a
        // SplitPoint object is initialized with all the data that must be copied to the
        // helper threads and then helper threads are told that they have been assigned
        // work. This will cause them to instantly leave their idle loops and call
        // search(). When all threads have returned from search() then split() returns.
        internal static Value split(bool Fake, Position pos, Stack[] ss, int ssPos, Value alpha, Value beta,
                                    Value bestValue, ref Move bestMove, Depth depth, Move threatMove,
                                    int moveCount, MovePicker mp, int nodeType)
        {
            Debug.Assert(pos.pos_is_ok());
            Debug.Assert(bestValue > -ValueC.VALUE_INFINITE);
            Debug.Assert(bestValue <= alpha);
            Debug.Assert(alpha < beta);
            Debug.Assert(beta <= ValueC.VALUE_INFINITE);
            Debug.Assert(depth > DepthC.DEPTH_ZERO);

            Thread master = pos.this_thread();

            if (master.splitPointsCnt >= Constants.MAX_SPLITPOINTS_PER_THREAD)
            {
                return(bestValue);
            }

            // Pick the next available split point from the split point stack
            SplitPoint sp = master.splitPoints[master.splitPointsCnt];

            sp.parent     = master.curSplitPoint;
            sp.master     = master;
            sp.cutoff     = false;
            sp.slavesMask = 1UL << master.idx;
#if ACTIVE_REPARENT
            sp.allSlavesRunning = true;
#endif

            sp.depth      = depth;
            sp.bestMove   = bestMove;
            sp.threatMove = threatMove;
            sp.alpha      = alpha;
            sp.beta       = beta;
            sp.nodeType   = nodeType;
            sp.bestValue  = bestValue;
            sp.mp         = mp;
            sp.moveCount  = moveCount;
            sp.pos        = pos;
            sp.nodes      = 0;
            sp.ss         = ss;
            sp.ssPos      = ssPos;

            Debug.Assert(master.is_searching);
            master.curSplitPoint = sp;

            int slavesCnt = 0;

            ThreadHelper.lock_grab(sp.Lock);
            ThreadHelper.lock_grab(splitLock);

            for (int i = 0; i < size() && !Fake; ++i)
            {
                if (threads[i].is_available_to(master))
                {
                    sp.slavesMask           |= 1UL << i;
                    threads[i].curSplitPoint = sp;
                    threads[i].is_searching  = true; // Slave leaves idle_loop()

                    if (useSleepingThreads)
                    {
                        threads[i].wake_up();
                    }

                    if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included
                    {
                        break;
                    }
                }
            }

            master.splitPointsCnt++;

            ThreadHelper.lock_release(splitLock);
            ThreadHelper.lock_release(sp.Lock);

            // Everything is set up. The master thread enters the idle loop, from which
            // it will instantly launch a search, because its is_searching flag is set.
            // We pass the split point as a parameter to the idle loop, which means that
            // the thread will return from the idle loop when all slaves have finished
            // their work at this split point.
            if (slavesCnt != 0 || Fake)
            {
                master.idle_loop(sp, null);
                // In helpful master concept a master can help only a sub-tree of its split
                // point, and because here is all finished is not possible master is booked.
                Debug.Assert(!master.is_searching);
            }

            // We have returned from the idle loop, which means that all threads are
            // finished. Note that setting is_searching and decreasing activeSplitPoints is
            // done under lock protection to avoid a race with Thread::is_available_to().
            ThreadHelper.lock_grab(sp.Lock); // To protect sp->nodes
            ThreadHelper.lock_grab(splitLock);

            master.is_searching = true;
            master.splitPointsCnt--;
            master.curSplitPoint = sp.parent;
            pos.nodes           += sp.nodes;
            bestMove             = sp.bestMove;

            ThreadHelper.lock_release(splitLock);
            ThreadHelper.lock_release(sp.Lock);

            return(sp.bestValue);
        }
Esempio n. 3
0
 internal void wake_up()
 {
     ThreadHelper.lock_grab(sleepLock);
     ThreadHelper.cond_signal(sleepCond);
     ThreadHelper.lock_release(sleepLock);
 }