Skip to content
Permalink
Browse files
Add JSC API configuring GC signals in Linux
https://bugs.webkit.org/show_bug.cgi?id=220641

Reviewed by Mark Lam.

Source/JavaScriptCore:

Add JSConfigureSignalForGC function for Linux and FreeBSD (non Apple, non Windows platforms).

* API/JSBase.cpp:
(JSConfigureSignalForGC):
* API/JSBasePrivate.h:

Source/WTF:

In Linux and FreeBSD, we need to use signals to suspend and resume threads.
By default, we are using SIGUSR1, but it is possible that some embedders want to use
the other signals since they are using SIGUSR1 already. To work-around that, this
patch offers the way for embedders to configure signals.

* wtf/Threading.h:
* wtf/WTFConfig.h:
* wtf/posix/ThreadingPOSIX.cpp:
(WTF::Thread::signalHandlerSuspendResume):
(WTF::Thread::initializePlatformThreading):
(WTF::Thread::initializeCurrentThreadEvenIfNonWTFCreated):
(WTF::Thread::initializeCurrentTLS):
(WTF::Thread::suspend):
(WTF::Thread::resume):
* wtf/threads/Signals.cpp:
(WTF::addSignalHandler):
* wtf/win/ThreadingWin.cpp:
(WTF::Thread::initializeCurrentTLS):

Canonical link: https://commits.webkit.org/233096@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@271560 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Constellation committed Jan 17, 2021
1 parent 22e0c65 commit aacd54f678f5ba81356590fb16497ae187c8052f
Showing 9 changed files with 111 additions and 28 deletions.
@@ -182,6 +182,17 @@ void JSDisableGCTimer(void)
GCActivityCallback::s_shouldCreateGCTimer = false;
}

#if !OS(DARWIN) && !OS(WINDOWS)
bool JSConfigureSignalForGC(int signal)
{
if (g_wtfConfig.isThreadSuspendResumeSignalConfigured)
return false;
g_wtfConfig.sigThreadSuspendResume = signal;
g_wtfConfig.isUserSpecifiedThreadSuspendResumeSignalConfigured = true;
return true;
}
#endif

JSObjectRef JSGetMemoryUsageStatistics(JSContextRef ctx)
{
if (!ctx) {
@@ -47,6 +47,17 @@ JS_EXPORT void JSReportExtraMemoryCost(JSContextRef ctx, size_t size) JSC_API_AV

JS_EXPORT void JSDisableGCTimer(void);

#if !defined(__APPLE__) && !defined(WIN32) && !defined(_WIN32)
/*!
@function JSConfigureSignalForGC
@abstract Configure signals for GC in non-Apple and non-Windows platforms.
@param signal The signal number to use.
@result true if the signal is successfully configured, otherwise false.
@discussion Call this function before any of JSC initialization starts. Otherwise, it fails.
*/
JS_EXPORT bool JSConfigureSignalForGC(int signal);
#endif

/*!
@function
@abstract Produces an object with various statistics about current memory usage.
@@ -1,3 +1,16 @@
2021-01-17 Yusuke Suzuki <ysuzuki@apple.com>

Add JSC API configuring GC signals in Linux
https://bugs.webkit.org/show_bug.cgi?id=220641

Reviewed by Mark Lam.

Add JSConfigureSignalForGC function for Linux and FreeBSD (non Apple, non Windows platforms).

* API/JSBase.cpp:
(JSConfigureSignalForGC):
* API/JSBasePrivate.h:

2021-01-15 Alexey Proskuryakov <ap@apple.com>

Build fixes with newer clang
@@ -1,3 +1,29 @@
2021-01-17 Yusuke Suzuki <ysuzuki@apple.com>

Add JSC API configuring GC signals in Linux
https://bugs.webkit.org/show_bug.cgi?id=220641

Reviewed by Mark Lam.

In Linux and FreeBSD, we need to use signals to suspend and resume threads.
By default, we are using SIGUSR1, but it is possible that some embedders want to use
the other signals since they are using SIGUSR1 already. To work-around that, this
patch offers the way for embedders to configure signals.

* wtf/Threading.h:
* wtf/WTFConfig.h:
* wtf/posix/ThreadingPOSIX.cpp:
(WTF::Thread::signalHandlerSuspendResume):
(WTF::Thread::initializePlatformThreading):
(WTF::Thread::initializeCurrentThreadEvenIfNonWTFCreated):
(WTF::Thread::initializeCurrentTLS):
(WTF::Thread::suspend):
(WTF::Thread::resume):
* wtf/threads/Signals.cpp:
(WTF::addSignalHandler):
* wtf/win/ThreadingWin.cpp:
(WTF::Thread::initializeCurrentTLS):

2021-01-15 Chris Dumez <cdumez@apple.com>

[GPUProcess] Move DOM / Canvas rendering off the main thread in the GPUProcess
@@ -72,13 +72,6 @@ class PrintStream;

WTF_EXPORT_PRIVATE void initialize();

#if USE(PTHREADS)

// We use SIGUSR1 to suspend and resume machine threads in JavaScriptCore.
constexpr const int SigThreadSuspendResume = SIGUSR1;

#endif

enum class GCThreadType : uint8_t {
None = 0,
Main,
@@ -70,6 +70,11 @@ struct Config {

bool isPermanentlyFrozen;

#if USE(PTHREADS)
bool isUserSpecifiedThreadSuspendResumeSignalConfigured;
bool isThreadSuspendResumeSignalConfigured;
int sigThreadSuspendResume;
#endif
#if OS(UNIX)
SignalHandlers signalHandlers;
#endif
@@ -38,6 +38,7 @@
#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
#include <wtf/ThreadingPrimitives.h>
#include <wtf/WTFConfig.h>
#include <wtf/WordLock.h>

#if OS(LINUX)
@@ -158,11 +159,11 @@ void Thread::signalHandlerSuspendResume(int, siginfo_t*, void* ucontext)
// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_11
globalSemaphoreForSuspendResume->post();

// Reaching here, SigThreadSuspendResume is blocked in this handler (this is configured by sigaction's sa_mask).
// So before calling sigsuspend, SigThreadSuspendResume to this thread is deferred. This ensures that the handler is not executed recursively.
// Reaching here, sigThreadSuspendResume is blocked in this handler (this is configured by sigaction's sa_mask).
// So before calling sigsuspend, sigThreadSuspendResume to this thread is deferred. This ensures that the handler is not executed recursively.
sigset_t blockedSignalSet;
sigfillset(&blockedSignalSet);
sigdelset(&blockedSignalSet, SigThreadSuspendResume);
sigdelset(&blockedSignalSet, g_wtfConfig.sigThreadSuspendResume);
sigsuspend(&blockedSignalSet);

thread->m_platformRegisters = nullptr;
@@ -175,19 +176,39 @@ void Thread::signalHandlerSuspendResume(int, siginfo_t*, void* ucontext)

void Thread::initializePlatformThreading()
{
if (!g_wtfConfig.isUserSpecifiedThreadSuspendResumeSignalConfigured)
g_wtfConfig.sigThreadSuspendResume = SIGUSR1;
g_wtfConfig.isThreadSuspendResumeSignalConfigured = true;

#if !OS(DARWIN)
globalSemaphoreForSuspendResume.construct(0);

// Signal handlers are process global configuration.
// Intentionally block SigThreadSuspendResume in the handler.
// SigThreadSuspendResume will be allowed in the handler by sigsuspend.
struct sigaction action;
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SigThreadSuspendResume);

action.sa_sigaction = &signalHandlerSuspendResume;
action.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SigThreadSuspendResume, &action, 0);
// Intentionally block sigThreadSuspendResume in the handler.
// sigThreadSuspendResume will be allowed in the handler by sigsuspend.
auto attemptToSetSignal = [](int signal) -> bool {
struct sigaction action;
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, signal);

action.sa_sigaction = &signalHandlerSuspendResume;
action.sa_flags = SA_RESTART | SA_SIGINFO;

// Theoretically, this can have race conditions but currently, there is no way to deal with it,
// plus, we do not expect that this initialization is executed concurrently with the other
// initialization which also installs specific signals. If this is the problem, applications should
// change how to initialize things.
struct sigaction oldAction;
if (sigaction(signal, nullptr, &oldAction))
return false;
// It has signal already.
if (oldAction.sa_handler != SIG_DFL || bitwise_cast<void*>(oldAction.sa_sigaction) != bitwise_cast<void*>(SIG_DFL))
return false;
return !sigaction(signal, &action, 0);
};

bool signalIsInstalled = attemptToSetSignal(g_wtfConfig.sigThreadSuspendResume);
RELEASE_ASSERT(signalIsInstalled);
#endif
}

@@ -201,9 +222,10 @@ ThreadIdentifier Thread::currentID()
void Thread::initializeCurrentThreadEvenIfNonWTFCreated()
{
#if !OS(DARWIN)
RELEASE_ASSERT(g_wtfConfig.isThreadSuspendResumeSignalConfigured);
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SigThreadSuspendResume);
sigaddset(&mask, g_wtfConfig.sigThreadSuspendResume);
pthread_sigmask(SIG_UNBLOCK, &mask, 0);
#endif
}
@@ -323,6 +345,7 @@ void Thread::detach()
Thread& Thread::initializeCurrentTLS()
{
// Not a WTF-created thread, Thread is not established yet.
WTF::initialize();
Ref<Thread> thread = adoptRef(*new Thread());
thread->establishPlatformSpecificHandle(pthread_self());
thread->initializeInThread();
@@ -362,13 +385,11 @@ auto Thread::suspend() -> Expected<void, PlatformSuspendError>
return { };
#else
if (!m_suspendCount) {
// Ideally, we would like to use pthread_sigqueue. It allows us to pass the argument to the signal handler.
// But it can be used in a few platforms, like Linux.
// Instead, we use Thread* stored in a global variable to pass it to the signal handler.
targetThread.store(this);

while (true) {
int result = pthread_kill(m_handle, SigThreadSuspendResume);
// We must use pthread_kill to avoid queue-overflow problem with real-time signals.
int result = pthread_kill(m_handle, g_wtfConfig.sigThreadSuspendResume);
if (result)
return makeUnexpected(result);
globalSemaphoreForSuspendResume->wait();
@@ -392,15 +413,16 @@ void Thread::resume()
thread_resume(m_platformThread);
#else
if (m_suspendCount == 1) {
// When allowing SigThreadSuspendResume interrupt in the signal handler by sigsuspend and SigThreadSuspendResume is actually issued,
// When allowing sigThreadSuspendResume interrupt in the signal handler by sigsuspend and SigThreadSuspendResume is actually issued,
// the signal handler itself will be called once again.
// There are several ways to distinguish the handler invocation for suspend and resume.
// 1. Use different signal numbers. And check the signal number in the handler.
// 2. Use some arguments to distinguish suspend and resume in the handler. If pthread_sigqueue can be used, we can take this.
// 2. Use some arguments to distinguish suspend and resume in the handler.
// 3. Use thread's flag.
// In this implementaiton, we take (3). m_suspendCount is used to distinguish it.
// Note that we must use pthread_kill to avoid queue-overflow problem with real-time signals.
targetThread.store(this);
if (pthread_kill(m_handle, SigThreadSuspendResume) == ESRCH)
if (pthread_kill(m_handle, g_wtfConfig.sigThreadSuspendResume) == ESRCH)
return;
globalSemaphoreForSuspendResume->wait();
}
@@ -305,7 +305,8 @@ void addSignalHandler(Signal signal, SignalHandler&& handler)
auto result = sigfillset(&action.sa_mask);
RELEASE_ASSERT(!result);
// Do not block this signal since it is used on non-Darwin systems to suspend and resume threads.
result = sigdelset(&action.sa_mask, SigThreadSuspendResume);
RELEASE_ASSERT(g_wtfConfig.isThreadSuspendResumeSignalConfigured);
result = sigdelset(&action.sa_mask, g_wtfConfig.sigThreadSuspendResume);
RELEASE_ASSERT(!result);
action.sa_flags = SA_SIGINFO;
auto systemSignals = toSystemSignal(signal);
@@ -239,6 +239,7 @@ size_t Thread::getRegisters(PlatformRegisters& registers)
Thread& Thread::initializeCurrentTLS()
{
// Not a WTF-created thread, ThreadIdentifier is not established yet.
WTF::initialize();
Ref<Thread> thread = adoptRef(*new Thread());

HANDLE handle;

0 comments on commit aacd54f

Please sign in to comment.