[haiku-commits] haiku: hrev47384 - src/apps/terminal

  • From: ingo_weinhold@xxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Mon, 16 Jun 2014 11:09:16 +0200 (CEST)

hrev47384 adds 1 changeset to branch 'master'
old head: 6563b112643871b3a1d35cdf5127f7766b46be8a
new head: eb718e31d22dded84f10f218d3a380ef5707dee1
overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=eb718e3+%5E6563b11

----------------------------------------------------------------------------

eb718e3: Terminal: Handle SIGCHLD in a dedicated thread
  
  Block SIGCHLD in all threads and spawn a dedicated thread that handles
  the signal in a loop (via sigwait()). This avoids the issue that the
  SIGCHLD could be handled in any of our threads and thus possibly
  interrupt a syscall.
  
  Fixes #10941.

                                    [ Ingo Weinhold <ingo_weinhold@xxxxxx> ]

----------------------------------------------------------------------------

Revision:    hrev47384
Commit:      eb718e31d22dded84f10f218d3a380ef5707dee1
URL:         http://cgit.haiku-os.org/haiku/commit/?id=eb718e3
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Mon Jun 16 09:08:02 2014 UTC

Ticket:      https://dev.haiku-os.org/ticket/10941

----------------------------------------------------------------------------

2 files changed, 68 insertions(+), 39 deletions(-)
src/apps/terminal/TermApp.cpp | 101 ++++++++++++++++++++++++--------------
src/apps/terminal/TermApp.h   |   6 ++-

----------------------------------------------------------------------------

diff --git a/src/apps/terminal/TermApp.cpp b/src/apps/terminal/TermApp.cpp
index 908edc0..bb016af 100644
--- a/src/apps/terminal/TermApp.cpp
+++ b/src/apps/terminal/TermApp.cpp
@@ -56,7 +56,10 @@ main()
 #define B_TRANSLATION_CONTEXT "Terminal TermApp"
 
 TermApp::TermApp()
-       : BApplication(TERM_SIGNATURE),
+       :
+       BApplication(TERM_SIGNATURE),
+       fChildCleanupThread(-1),
+       fTerminating(false),
        fStartFullscreen(false),
        fTermWindow(NULL),
        fArgs(NULL)
@@ -81,24 +84,39 @@ TermApp::ReadyToRun()
                return;
 
        // Install a SIGCHLD signal handler, so that we will be notified, when
-       // a shell exits.
+       // a shell exits. The handler itself will never be executed, since we 
block
+       // the signal in all threads and handle it with sigwaitinfo() in the 
child
+       // cleanup thread.
        struct sigaction action;
-#ifdef __HAIKU__
        action.sa_handler = (__sighandler_t)_SigChildHandler;
-#else
-       action.sa_handler = (__signal_func_ptr)_SigChildHandler;
-#endif
        sigemptyset(&action.sa_mask);
-#ifdef SA_NODEFER
-       action.sa_flags = SA_NODEFER;
-#endif
-       action.sa_userdata = this;
+       action.sa_flags = 0;
        if (sigaction(SIGCHLD, &action, NULL) < 0) {
-               fprintf(stderr, "sigaction() failed: %s\n",
-                       strerror(errno));
+               fprintf(stderr, "sigaction() failed: %s\n", strerror(errno));
                // continue anyway
        }
 
+       // block SIGCHLD and SIGUSR1 -- we send the latter to wake up the child
+       // cleanup thread when quitting.
+       sigset_t blockedSignals;
+       sigemptyset(&blockedSignals);
+       sigaddset(&blockedSignals, SIGCHLD);
+       sigaddset(&blockedSignals, SIGUSR1);
+
+       int error = pthread_sigmask(SIG_BLOCK, &blockedSignals, NULL);
+       if (error != 0)
+               fprintf(stderr, "pthread_sigmask() failed: %s\n", 
strerror(errno));
+
+       // spawn the child cleanup thread
+       fChildCleanupThread = spawn_thread(_ChildCleanupThreadEntry,
+               "child cleanup", B_NORMAL_PRIORITY, this);
+       if (fChildCleanupThread >= 0) {
+               resume_thread(fChildCleanupThread);
+       } else {
+               fprintf(stderr, "Failed to start child cleanup thread: %s\n",
+                       strerror(fChildCleanupThread));
+       }
+
        // init the mouse copy'n'paste clipboard
        gMouseClipboard = new BClipboard(MOUSE_CLIPBOARD_NAME, true);
 
@@ -145,6 +163,13 @@ TermApp::QuitRequested()
 void
 TermApp::Quit()
 {
+       fTerminating = true;
+
+       if (fChildCleanupThread >= 0) {
+               send_signal(fChildCleanupThread, SIGUSR1);
+               wait_for_thread(fChildCleanupThread, NULL);
+       }
+
        BApplication::Quit();
 }
 
@@ -157,10 +182,6 @@ TermApp::MessageReceived(BMessage* message)
                        fTermWindow->Activate();
                        break;
 
-               case MSG_CHECK_CHILDREN:
-                       _HandleChildCleanup();
-                       break;
-
                default:
                        BApplication::MessageReceived(message);
                        break;
@@ -264,35 +285,41 @@ TermApp::_MakeTermWindow()
 //}
 
 
-void
-TermApp::_HandleChildCleanup()
-{
-}
-
-
 /*static*/ void
 TermApp::_SigChildHandler(int signal, void* data)
 {
-       // Spawing a thread that does the actual signal handling is pretty much
-       // the only safe thing to do in a multi-threaded application. The
-       // interrupted thread might have been anywhere, e.g. in a critical 
section,
-       // holding locks. If we do anything that does require locking at any 
point
-       // (e.g. memory allocation, messaging), we risk a dead-lock or data
-       // structure corruption. Spawing a thread is safe though, since its only
-       // a system call.
-       thread_id thread = spawn_thread(_ChildCleanupThread, "child cleanup",
-               B_NORMAL_PRIORITY, ((TermApp*)data)->fTermWindow);
-       if (thread >= 0)
-               resume_thread(thread);
+       fprintf(stderr, "Terminal: _SigChildHandler() called! That should never 
"
+               "happen!\n");
 }
 
 
 /*static*/ status_t
-TermApp::_ChildCleanupThread(void* data)
+TermApp::_ChildCleanupThreadEntry(void* data)
+{
+       return ((TermApp*)data)->_ChildCleanupThread();
+}
+
+       
+status_t
+TermApp::_ChildCleanupThread()
 {
-       // Just drop the windowa message and let it do the actual work. This
-       // saves us additional synchronization measures.
-       return ((TermWindow*)data)->PostMessage(MSG_CHECK_CHILDREN);
+       sigset_t waitForSignals;
+       sigemptyset(&waitForSignals);
+       sigaddset(&waitForSignals, SIGCHLD);
+       sigaddset(&waitForSignals, SIGUSR1);
+       
+       for (;;) {
+               int signal;
+               int error = sigwait(&waitForSignals, &signal);
+
+               if (fTerminating)
+                       break;
+
+               if (error == 0 && signal == SIGCHLD)
+                       fTermWindow->PostMessage(MSG_CHECK_CHILDREN);
+       }
+
+       return B_OK;
 }
 
 
diff --git a/src/apps/terminal/TermApp.h b/src/apps/terminal/TermApp.h
index 45fbd83..1a68c3a 100644
--- a/src/apps/terminal/TermApp.h
+++ b/src/apps/terminal/TermApp.h
@@ -43,13 +43,15 @@ protected:
 private:
                        status_t                        _MakeTermWindow();
 
-                       void                            _HandleChildCleanup();
        static  void                            _SigChildHandler(int signal, 
void* data);
-       static  status_t                        _ChildCleanupThread(void* data);
+       static  status_t                        _ChildCleanupThreadEntry(void* 
data);
+                       status_t                        _ChildCleanupThread();
 
                        void                            _Usage(char *name);
                        void                            _InitDefaultPalette();
 private:
+                       thread_id                       fChildCleanupThread;
+                       bool                            fTerminating;
                        bool                            fStartFullscreen;
                        BString                         fWindowTitle;
 


Other related posts: