VirtualBox

Changeset 95199 in vbox


Ignore:
Timestamp:
Jun 5, 2022 8:15:44 PM (3 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
151707
Message:

VBoxHeadless: Reworked the signal handling for macOS to make it work right, given that processEventQueue won't return VERR_INTERRUPTED after a Ctrl-C was delivered. bugref:9898

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp

    r95140 r95199  
    5959
    6060#if !defined(RT_OS_WINDOWS)
    61 #include <signal.h>
    62 static void HandleSignal(int sig);
     61# include <signal.h>
     62# include <unistd.h>
     63# include <sys/uio.h>
    6364#endif
    6465
     
    396397
    397398#if !defined(RT_OS_WINDOWS)
    398 static void
    399 HandleSignal(int sig)
     399
     400/** Signals we handle. */
     401static int const g_aiSigs[] = { SIGHUP, SIGINT, SIGTERM, SIGUSR1 };
     402
     403/** The signal handler. */
     404static void HandleSignal(int sig)
    400405{
    401     RT_NOREF(sig);
    402     LogRel(("VBoxHeadless: received singal %d\n", sig));
     406# if 1
     407    struct iovec aSegs[8];
     408    int          cSegs = 0;
     409    aSegs[cSegs++].iov_base = (char *)"VBoxHeadless: signal ";
     410    aSegs[cSegs++].iov_base = (char *)strsignal(sig);
     411    const char *pszThread = RTThreadSelfName();
     412    if (pszThread)
     413    {
     414        aSegs[cSegs++].iov_base = (char *)" on thread ";
     415        aSegs[cSegs++].iov_base = (char *)pszThread;       
     416    }
     417    aSegs[cSegs++].iov_base = (char *)"\n";
     418    for (int i = 0; i < cSegs; i++)
     419        aSegs[i].iov_len = strlen((const char *)aSegs[i].iov_base);
     420    writev(2, aSegs, cSegs);
     421# else
     422    LogRel(("VBoxHeadless: received signal %d\n", sig)); /** @todo r=bird: This is not at all safe. */
     423# endif   
    403424    g_fTerminateFE = true;
    404425}
     426
     427# ifdef RT_OS_DARWIN
     428
     429/* For debugging. */
     430uint32_t GetSignalMask(void)
     431{
     432    /* For some totally messed up reason, the xnu sigprocmask actually returns
     433       the signal mask of the calling thread rather than the process one
     434       (p_sigmask), so can call sigprocmask just as well as pthread_sigmask here. */
     435    sigset_t Sigs;
     436    RT_ZERO(Sigs);
     437    sigprocmask(SIG_UNBLOCK, NULL, &Sigs);
     438    RTMsgInfo("debug: thread %s mask: %.*Rhxs\n", RTThreadSelfName(), sizeof(Sigs), &Sigs);
     439    for (int i = 0; i < 32; i++)
     440        if (sigismember(&Sigs, i)) RTMsgInfo("debug: sig %2d blocked: %s\n", i, strsignal(i));
     441    return *(uint32_t const *)&Sigs;
     442}
     443
     444/**
     445 * Blocks or unblocks the signals we handle.
     446 *
     447 * @note Only for darwin does fProcess make a difference, all others always
     448 *       work on the calling thread regardless of the flag value.
     449 */
     450static void SetSignalMask(bool fBlock, bool fProcess)
     451{
     452    sigset_t Sigs;
     453    sigemptyset(&Sigs);
     454    for (unsigned i = 0; i < RT_ELEMENTS(g_aiSigs); i++)
     455        sigaddset(&Sigs, g_aiSigs[i]);
     456    if (fProcess)
     457    {
     458        if (sigprocmask(fBlock ? SIG_BLOCK : SIG_UNBLOCK, &Sigs, NULL) != 0)
     459            RTMsgError("sigprocmask failed: %d", errno);
     460    }
     461    else
     462    {
     463        if (pthread_sigmask(fBlock ? SIG_BLOCK : SIG_UNBLOCK, &Sigs, NULL) != 0)
     464            RTMsgError("pthread_sigmask failed: %d", errno);
     465    }
     466}
     467
     468/**
     469 * @callback_method_impl{FNRTTHREAD, Signal wait thread}
     470 */
     471static DECLCALLBACK(int) SigThreadProc(RTTHREAD hThreadSelf, void *pvUser)
     472{
     473    RT_NOREF(hThreadSelf, pvUser);
     474
     475    /* The signals to wait for: */
     476    sigset_t SigSetWait;
     477    sigemptyset(&SigSetWait);
     478    for (unsigned i = 0; i < RT_ELEMENTS(g_aiSigs); i++)
     479        sigaddset(&SigSetWait, g_aiSigs[i]);
     480   
     481    /* The wait + processing loop: */
     482    for (;;)
     483    {
     484        int iSignal = -1;
     485        if (sigwait(&SigSetWait, &iSignal) == 0)
     486        {
     487            g_fTerminateFE = true;
     488            RTMsgInfo("Caught signal: %s\n", strsignal(iSignal));
     489            LogRel(("VBoxHeadless: Caught signal: %s\n", strsignal(iSignal)));
     490        }
     491
     492        /** @todo this is a little bit racy...   */
     493        if (g_fTerminateFE && gEventQ != NULL)
     494            gEventQ->interruptEventQueueProcessing();
     495    }
     496}
     497
     498/** The handle to the signal wait thread. */
     499static RTTHREAD g_hSigThread = NIL_RTTHREAD;
     500
     501# endif /* RT_OS_DARWIN */
     502
     503static void SetUpSignalHandlers(void)
     504{
     505    signal(SIGPIPE, SIG_IGN);
     506    signal(SIGTTOU, SIG_IGN);
     507
     508    /* Don't touch SIGUSR2 as IPRT could be using it for RTThreadPoke(). */
     509    for (unsigned i = 0; i < RT_ELEMENTS(g_aiSigs); i++)
     510    {
     511        struct sigaction sa;
     512        RT_ZERO(sa);
     513        sa.sa_handler = HandleSignal;
     514        if (sigaction(g_aiSigs[i], &sa, NULL) != 0)
     515            RTMsgError("sigaction failed for signal #%u: %d", g_aiSigs[i], errno);
     516    }
     517   
     518# if defined(RT_OS_DARWIN)
     519    /*
     520     * On darwin processEventQueue() does not return with VERR_INTERRUPTED or
     521     * similar if a signal arrives while we're waiting for events.  So, in
     522     * order to respond promptly to signals after they arrives, we use a
     523     * dedicated thread for fielding the signals and poking the event queue
     524     * after each signal.
     525     *
     526     * We block the signals for all threads (this is fine as the p_sigmask
     527     * isn't actually used for anything at all and wont prevent signal
     528     * delivery).  The signal thread should have them blocked as well, as it
     529     * uses sigwait to do the waiting (better than sigsuspend, as we can safely
     530     * LogRel the signal this way).
     531     */
     532    if (g_hSigThread == NIL_RTTHREAD)
     533    {
     534        SetSignalMask(true /*fBlock */, true /*fProcess*/);
     535        int vrc = RTThreadCreate(&g_hSigThread, SigThreadProc, NULL, 0, RTTHREADTYPE_DEFAULT, 0, "SigWait");
     536        if (RT_FAILURE(vrc))
     537        {
     538            RTMsgError("Failed to create signal waiter thread: %Rrc", vrc);
     539            SetSignalMask(false /*fBlock */, false /*fProcess*/);
     540        }
     541    }
     542# endif
     543}
     544
    405545#endif /* !RT_OS_WINDOWS */
    406546
     
    13021442         */
    13031443#if !defined(RT_OS_WINDOWS)
    1304         signal(SIGPIPE, SIG_IGN);
    1305         signal(SIGTTOU, SIG_IGN);
    1306 
    1307         struct sigaction sa;
    1308         RT_ZERO(sa);
    1309         sa.sa_handler = HandleSignal;
    1310         sigaction(SIGHUP,  &sa, NULL);
    1311         sigaction(SIGINT,  &sa, NULL);
    1312         sigaction(SIGTERM, &sa, NULL);
    1313         sigaction(SIGUSR1, &sa, NULL);
    1314         /* Don't touch SIGUSR2 as IPRT could be using it for RTThreadPoke(). */
    1315 
    1316 #else /* RT_OS_WINDOWS */
     1444        ::SetUpSignalHandlers();
     1445#else
    13171446        /*
    13181447         * Register windows console signal handler to react to Ctrl-C,
     
    13651494        for (;;)
    13661495        {
     1496            if (g_fTerminateFE)
     1497            {
     1498                LogRel(("VBoxHeadless: processEventQueue: %Rrc, termination requested\n", vrc));
     1499                break;
     1500            }
     1501
    13671502            vrc = gEventQ->processEventQueue(RT_INDEFINITE_WAIT);
    13681503
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette