blob: 982cbe35e47db41e9bb7d1bd7621b8a4015ad88b [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SafePoint_h
#define SafePoint_h
#include "platform/heap/ThreadState.h"
#include "wtf/ThreadingPrimitives.h"
namespace blink {
class SafePointScope final {
WTF_MAKE_NONCOPYABLE(SafePointScope);
public:
explicit SafePointScope(ThreadState::StackState stackState, ThreadState* state = ThreadState::current())
: m_state(state)
{
if (m_state) {
RELEASE_ASSERT(!m_state->isAtSafePoint());
m_state->enterSafePoint(stackState, this);
}
}
~SafePointScope()
{
if (m_state)
m_state->leaveSafePoint();
}
private:
ThreadState* m_state;
};
// The SafePointAwareMutexLocker is used to enter a safepoint while waiting for
// a mutex lock. It also ensures that the lock is not held while waiting for a GC
// to complete in the leaveSafePoint method, by releasing the lock if the
// leaveSafePoint method cannot complete without blocking, see
// SafePointBarrier::checkAndPark.
class SafePointAwareMutexLocker final {
WTF_MAKE_NONCOPYABLE(SafePointAwareMutexLocker);
public:
explicit SafePointAwareMutexLocker(MutexBase& mutex, ThreadState::StackState stackState = ThreadState::HeapPointersOnStack)
: m_mutex(mutex)
, m_locked(false)
{
ThreadState* state = ThreadState::current();
do {
bool leaveSafePoint = false;
// We cannot enter a safepoint if we are currently sweeping. In that
// case we just try to acquire the lock without being at a safepoint.
// If another thread tries to do a GC at that time it might time out
// due to this thread not being at a safepoint and waiting on the lock.
if (!state->sweepForbidden() && !state->isAtSafePoint()) {
state->enterSafePoint(stackState, this);
leaveSafePoint = true;
}
m_mutex.lock();
m_locked = true;
if (leaveSafePoint) {
// When leaving the safepoint we might end up release the mutex
// if another thread is requesting a GC, see
// SafePointBarrier::checkAndPark. This is the case where we
// loop around to reacquire the lock.
state->leaveSafePoint(this);
}
} while (!m_locked);
}
~SafePointAwareMutexLocker()
{
ASSERT(m_locked);
m_mutex.unlock();
}
private:
friend class SafePointBarrier;
void reset()
{
ASSERT(m_locked);
m_mutex.unlock();
m_locked = false;
}
MutexBase& m_mutex;
bool m_locked;
};
class SafePointBarrier final {
WTF_MAKE_NONCOPYABLE(SafePointBarrier);
public:
SafePointBarrier();
~SafePointBarrier();
// Request other attached threads that are not at safe points to park
// themselves on safepoints.
bool parkOthers();
// Resume executions of other attached threads that are parked at
// the safe points.
void resumeOthers(bool barrierLocked = false);
// Park this thread if there exists a request to park attached threads.
// This method must be called at a safe point.
void checkAndPark(ThreadState*, SafePointAwareMutexLocker* = nullptr);
void enterSafePoint(ThreadState*);
void leaveSafePoint(ThreadState*, SafePointAwareMutexLocker* = nullptr);
private:
void doPark(ThreadState*, intptr_t* stackEnd);
static void parkAfterPushRegisters(SafePointBarrier* barrier, ThreadState* state, intptr_t* stackEnd)
{
barrier->doPark(state, stackEnd);
}
void doEnterSafePoint(ThreadState*, intptr_t* stackEnd);
static void enterSafePointAfterPushRegisters(SafePointBarrier* barrier, ThreadState* state, intptr_t* stackEnd)
{
barrier->doEnterSafePoint(state, stackEnd);
}
volatile int m_canResume;
volatile int m_unparkedThreadCount;
Mutex m_mutex;
ThreadCondition m_parked;
ThreadCondition m_resume;
};
} // namespace blink
#endif