From d9530bf7aa7d852bce3a44c4b8f3a48e03fb0273 Mon Sep 17 00:00:00 2001 From: walker0643 <> Date: Wed, 31 Jan 2018 20:20:06 -0500 Subject: [PATCH] use a relatively small hack to keep barriers/barrierc from crashing on unixes when they are killed from the GUI with QProcess's close(). the stdin stream is going completely unused (though the stdout stream is not) so use stdin to send a shutdown command. this solution avoids all the nastiness and overhead of using shared memory, mutexes, condvars, etc. just to communicate "stop" from one process to another --- src/gui/src/MainWindow.cpp | 4 +++ src/gui/src/ShutdownCh.h | 22 ++++++++++++ src/lib/base/EventQueue.cpp | 7 ++++ src/lib/base/EventQueue.h | 2 ++ src/lib/base/NonBlockingStream.cpp | 56 ++++++++++++++++++++++++++++++ src/lib/base/NonBlockingStream.h | 34 ++++++++++++++++++ 6 files changed, 125 insertions(+) create mode 100644 src/gui/src/ShutdownCh.h create mode 100644 src/lib/base/NonBlockingStream.cpp create mode 100644 src/lib/base/NonBlockingStream.h diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 804332e5..ae1fad40 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -32,6 +32,7 @@ #include "QUtility.h" #include "ProcessorArch.h" #include "SslCertificate.h" +#include "ShutdownCh.h" #include #include @@ -773,6 +774,9 @@ void MainWindow::stopDesktop() appendLogInfo("stopping barrier desktop process"); if (barrierProcess()->isOpen()) { + // try to shutdown child gracefully + barrierProcess()->write(&ShutdownCh, 1); + barrierProcess()->waitForFinished(5000); barrierProcess()->close(); } diff --git a/src/gui/src/ShutdownCh.h b/src/gui/src/ShutdownCh.h new file mode 100644 index 00000000..2462cae7 --- /dev/null +++ b/src/gui/src/ShutdownCh.h @@ -0,0 +1,22 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2018 Debauchee Open Source Group + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +// included in both the GUI and the child apps (server & client) +const char ShutdownCh = 'S'; + diff --git a/src/lib/base/EventQueue.cpp b/src/lib/base/EventQueue.cpp index 0a22c9dd..7ccb99fe 100644 --- a/src/lib/base/EventQueue.cpp +++ b/src/lib/base/EventQueue.cpp @@ -27,6 +27,7 @@ #include "base/EventTypes.h" #include "base/Log.h" #include "base/XBase.h" +#include "../gui/src/ShutdownCh.h" EVENT_TYPE_ACCESSOR(Client) EVENT_TYPE_ACCESSOR(IStream) @@ -205,6 +206,12 @@ EventQueue::getEvent(Event& event, double timeout) { Stopwatch timer(true); retry: + // check to see if parent wants us to shutdown now + char ch; + if (m_parentStream.try_read_char(ch) && ch == ShutdownCh) { + event = Event(Event::kQuit); + return false; + } // if no events are waiting then handle timers and then wait while (m_buffer->isEmpty()) { // handle timers first diff --git a/src/lib/base/EventQueue.h b/src/lib/base/EventQueue.h index d4fb6ffd..bbfcfcdf 100644 --- a/src/lib/base/EventQueue.h +++ b/src/lib/base/EventQueue.h @@ -26,6 +26,7 @@ #include "base/Stopwatch.h" #include "common/stdmap.h" #include "common/stdset.h" +#include "base/NonBlockingStream.h" #include @@ -184,6 +185,7 @@ private: Mutex* m_readyMutex; CondVar* m_readyCondVar; std::queue m_pending; + NonBlockingStream m_parentStream; }; #define EVENT_TYPE_ACCESSOR(type_) \ diff --git a/src/lib/base/NonBlockingStream.cpp b/src/lib/base/NonBlockingStream.cpp new file mode 100644 index 00000000..da20cad3 --- /dev/null +++ b/src/lib/base/NonBlockingStream.cpp @@ -0,0 +1,56 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2008 Debauchee Open Source Group + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "base/NonBlockingStream.h" + +#include // tcgetattr/tcsetattr, read +#include // tcgetattr/tcsetattr +#include +#include +#include + +NonBlockingStream::NonBlockingStream(int fd) : + _fd(fd) +{ + // disable ICANON & ECHO so we don't have to wait for a newline + // before we get data (and to keep it from being echoed back out) + termios ta; + tcgetattr(fd, &ta); + _p_ta_previous = new termios(ta); + ta.c_lflag &= ~(ICANON | ECHO); + tcsetattr(fd, TCSANOW, &ta); + + // prevent IO from blocking so we can poll (read()) + int _cntl_previous = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, _cntl_previous | O_NONBLOCK); +} + +NonBlockingStream::~NonBlockingStream() +{ + tcsetattr(_fd, TCSANOW, _p_ta_previous); + fcntl(_fd, F_SETFL, _cntl_previous); + delete _p_ta_previous; +} + +bool NonBlockingStream::try_read_char(char &ch) +{ + int result = read(_fd, &ch, 1); + if (result == 1) + return true; + assert(result == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)); + return false; +} diff --git a/src/lib/base/NonBlockingStream.h b/src/lib/base/NonBlockingStream.h new file mode 100644 index 00000000..61faf3d0 --- /dev/null +++ b/src/lib/base/NonBlockingStream.h @@ -0,0 +1,34 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2008 Debauchee Open Source Group + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +struct termios; + +class NonBlockingStream +{ +public: + explicit NonBlockingStream(int fd = 0); + ~NonBlockingStream(); + + bool try_read_char(char &ch); + +private: + int _fd; + termios * _p_ta_previous; + int _cntl_previous; +};