bool
ForkJoinShared::check(ForkJoinSlice &slice)
{
    if (abort_)
        return false;

    if (slice.isMainThread()) {
        JS_ASSERT(!cx_->runtime->gcIsNeeded);

        if (cx_->runtime->interrupt) {
            // The GC Needed flag should not be set during parallel
            // execution.  Instead, one of the requestGC() or
            // requestZoneGC() methods should be invoked.
            JS_ASSERT(!cx_->runtime->gcIsNeeded);

            // If interrupt is requested, bring worker threads to a halt,
            // service the interrupt, then let them start back up again.
            // AutoRendezvous autoRendezvous(slice);
            // if (!js_HandleExecutionInterrupt(cx_))
            //     return setFatal();
            setAbortFlag();
            return false;
        }
    } else if (rendezvous_) {
        joinRendezvous(slice);
    }

    return true;
}
Example #2
0
void
ForkJoinShared::endRendezvous(ForkJoinSlice &slice)
{
    JS_ASSERT(slice.isMainThread());

    AutoLockMonitor lock(*this);
    rendezvous_ = false;
    blocked_ = 0;
    rendezvousIndex_++;

    // Signal other threads that rendezvous is over.
    PR_NotifyAllCondVar(rendezvousEnd_);
}
Example #3
0
void
ForkJoinShared::initiateRendezvous(ForkJoinSlice &slice)
{
    // The rendezvous protocol is always initiated by the main thread.  The
    // main thread sets the rendezvous flag to true.  Seeing this flag, other
    // threads will invoke |joinRendezvous()|, which causes them to (1) read
    // |rendezvousIndex| and (2) increment the |blocked| counter.  Once the
    // |blocked| counter is equal to |uncompleted|, all parallel threads have
    // joined the rendezvous, and so the main thread is signaled.  That will
    // cause this function to return.
    //
    // Some subtle points:
    //
    // - Worker threads may potentially terminate their work before they see
    //   the rendezvous flag.  In this case, they would decrement
    //   |uncompleted| rather than incrementing |blocked|.  Either way, if the
    //   two variables become equal, the main thread will be notified
    //
    // - The |rendezvousIndex| counter is used to detect the case where the
    //   main thread signals the end of the rendezvous and then starts another
    //   rendezvous before the workers have a chance to exit.  We circumvent
    //   this by having the workers read the |rendezvousIndex| counter as they
    //   enter the rendezvous, and then they only block until that counter is
    //   incremented.  Another alternative would be for the main thread to
    //   block in |endRendezvous()| until all workers have exited, but that
    //   would be slower and involve unnecessary synchronization.
    //
    //   Note that the main thread cannot ever get more than one rendezvous
    //   ahead of the workers, because it must wait for all of them to enter
    //   the rendezvous before it can end it, so the solution of using a
    //   counter is perfectly general and we need not fear rollover.

    JS_ASSERT(slice.isMainThread());
    JS_ASSERT(!rendezvous_ && blocked_ == 0);
    JS_ASSERT(cx_->runtime->interrupt);

    AutoLockMonitor lock(*this);

    // Signal other threads we want to start a rendezvous.
    rendezvous_ = true;

    // Wait until all the other threads blocked themselves.
    while (blocked_ != uncompleted_)
        lock.wait();
}
bool
ForkJoinShared::check(ForkJoinSlice &slice)
{
    if (abort_)
        return false;

    if (slice.isMainThread()) {
        if (cx_->runtime->interrupt) {
            // If interrupt is requested, bring worker threads to a halt,
            // service the interrupt, then let them start back up again.
            AutoRendezvous autoRendezvous(slice);
            if (!js_HandleExecutionInterrupt(cx_))
                return setFatal();
        }
    } else if (rendezvous_) {
        joinRendezvous(slice);
    }

    return true;
}
Example #5
0
void
ForkJoinShared::joinRendezvous(ForkJoinSlice &slice)
{
    JS_ASSERT(!slice.isMainThread());
    JS_ASSERT(rendezvous_);

    AutoLockMonitor lock(*this);
    const uint32_t index = rendezvousIndex_;
    blocked_ += 1;

    // If we're the last to arrive, let the main thread know about it.
    if (blocked_ == uncompleted_)
        lock.notify();

    // Wait until the main thread terminates the rendezvous.  We use a
    // separate condition variable here to distinguish between workers
    // notifying the main thread that they have completed and the main
    // thread notifying the workers to resume.
    while (rendezvousIndex_ == index)
        PR_WaitCondVar(rendezvousEnd_, PR_INTERVAL_NO_TIMEOUT);
}
Example #6
0
bool
ForkJoinShared::check(ForkJoinSlice &slice)
{
    JS_ASSERT(cx_->runtime->interrupt);

    if (abort_)
        return false;

    if (slice.isMainThread()) {
        // We are the main thread: therefore we must
        // (1) initiate the rendezvous;
        // (2) if GC was requested, reinvoke trigger
        //     which will do various non-thread-safe
        //     preparatory steps.  We then invoke
        //     a non-incremental GC manually.
        // (3) run the operation callback, which
        //     would normally run the GC but
        //     incrementally, which we do not want.
        JSRuntime *rt = cx_->runtime;

        // Calls to js::TriggerGC() should have been redirected to
        // requestGC(), and thus the gcIsNeeded flag is not set yet.
        JS_ASSERT(!rt->gcIsNeeded);

        if (gcRequested_ && rt->isHeapBusy()) {
            // Cannot call GCSlice when heap busy, so abort.  Easier
            // right now to abort rather than prove it cannot arise,
            // and safer for short-term than asserting !isHeapBusy.
            setAbortFlag(false);
            records_->setCause(ParallelBailoutHeapBusy, NULL, NULL);
            return false;
        }

        // (1). Initiaize the rendezvous and record stack extents.
        AutoRendezvous autoRendezvous(slice);
        AutoMarkWorldStoppedForGC autoMarkSTWFlag(slice);
        slice.recordStackExtent();
        AutoInstallForkJoinStackExtents extents(rt, &stackExtents_[0]);

        // (2).  Note that because we are in a STW section, calls to
        // js::TriggerGC() etc will not re-invoke
        // ForkJoinSlice::requestGC().
        triggerGCIfRequested();

        // (2b) Run the GC if it is required.  This would occur as
        // part of js_InvokeOperationCallback(), but we want to avoid
        // an incremental GC.
        if (rt->gcIsNeeded) {
            GC(rt, GC_NORMAL, gcReason_);
        }

        // (3). Invoke the callback and abort if it returns false.
        if (!js_InvokeOperationCallback(cx_)) {
            records_->setCause(ParallelBailoutInterrupt, NULL, NULL);
            setAbortFlag(true);
            return false;
        }

        return true;
    } else if (rendezvous_) {
        slice.recordStackExtent();
        joinRendezvous(slice);
    }

    return true;
}