/* * Carla Utility Tests * Copyright (C) 2013-2014 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or any later version. * * This program 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. * * For a full copy of the GNU General Public License see the doc/GPL.txt file. */ #include "CarlaPipeUtils.hpp" #include "CarlaString.hpp" #include "juce_core.h" #include #ifdef CARLA_OS_WIN # include #else # include # include # include # include #endif #ifdef CARLA_OS_WIN # define INVALID_PIPE_VALUE INVALID_HANDLE_VALUE #else # define INVALID_PIPE_VALUE -1 #endif #ifdef CARLA_OS_WIN // ----------------------------------------------------------------------- // win32 stuff struct OverlappedEvent { OVERLAPPED over; OverlappedEvent() : over() { carla_zeroStruct(over); over.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); } ~OverlappedEvent() { ::CloseHandle(over.hEvent); } CARLA_DECLARE_NON_COPY_STRUCT(OverlappedEvent) }; static inline ssize_t ReadFileNonBlock(const HANDLE pipeh, const HANDLE cancelh, void* const buf, const std::size_t numBytes) { DWORD dsize; OverlappedEvent over; if (::ReadFile(pipeh, buf, numBytes, &dsize, &over.over) != FALSE) return static_cast(dsize); if (::GetLastError() == ERROR_IO_PENDING) { HANDLE handles[] = { over.over.hEvent, cancelh }; if (::WaitForMultipleObjects(2, handles, FALSE, 0) != WAIT_OBJECT_0) { ::CancelIo(pipeh); return -1; } if (::GetOverlappedResult(pipeh, &over.over, &dsize, FALSE) != FALSE) return static_cast(dsize); } return -1; } static inline ssize_t WriteFileNonBlock(const HANDLE pipeh, const HANDLE cancelh, const void* const buf, const std::size_t numBytes) { DWORD dsize; if (::WriteFile(pipeh, buf, numBytes, &dsize, nullptr) != FALSE) return static_cast(dsize); return -1; //DWORD dsize; OverlappedEvent over; if (::WriteFile(pipeh, buf, numBytes, &dsize, &over.over) != FALSE) return static_cast(dsize); if (::GetLastError() == ERROR_IO_PENDING) { HANDLE handles[] = { over.over.hEvent, cancelh }; if (::WaitForMultipleObjects(2, handles, FALSE, 0) != WAIT_OBJECT_0) { ::CancelIo(pipeh); return -1; } if (::GetOverlappedResult(pipeh, &over.over, &dsize, FALSE) != FALSE) return static_cast(dsize); } return -1; } #endif // CARLA_OS_WIN // ----------------------------------------------------------------------- // startProcess #ifdef CARLA_OS_WIN static inline bool startProcess(const char* const argv[], PROCESS_INFORMATION& processInfo) { using juce::String; String command; for (int i=0; argv[i] != nullptr; ++i) { String arg(argv[i]); // If there are spaces, surround it with quotes. If there are quotes, // replace them with \" so that CommandLineToArgv will correctly parse them. if (arg.containsAnyOf("\" ")) arg = arg.replace("\"", "\\\"").quoted(); command << arg << ' '; } command = command.trim(); STARTUPINFOW startupInfo; carla_zeroStruct(startupInfo); # if 0 startupInfo.hStdInput = INVALID_HANDLE_VALUE; startupInfo.hStdOutput = INVALID_HANDLE_VALUE; startupInfo.hStdError = INVALID_HANDLE_VALUE; startupInfo.dwFlags = STARTF_USESTDHANDLES; # endif startupInfo.cb = sizeof(STARTUPINFOW); return CreateProcessW(nullptr, const_cast(command.toWideCharPointer()), nullptr, nullptr, TRUE, 0x0, nullptr, nullptr, &startupInfo, &processInfo) != FALSE; } #else static inline bool startProcess(const char* const argv[], pid_t& pidinst) noexcept { const pid_t ret = pidinst = vfork(); switch (ret) { case 0: { // child process execvp(argv[0], const_cast(argv)); CarlaString error(std::strerror(errno)); carla_stderr2("exec failed: %s", error.buffer()); _exit(0); // this is not noexcept safe but doesn't matter anyway } break; case -1: { // error CarlaString error(std::strerror(errno)); carla_stderr2("fork() failed: %s", error.buffer()); _exit(0); // this is not noexcept safe but doesn't matter anyway } break; } return (ret > 0); } #endif // ----------------------------------------------------------------------- // waitForClientFirstMessage template static inline bool waitForClientFirstMessage(const P& pipe, const uint32_t timeOutMilliseconds) noexcept { #ifdef CARLA_OS_WIN CARLA_SAFE_ASSERT_RETURN(pipe.handle != INVALID_HANDLE_VALUE, false); CARLA_SAFE_ASSERT_RETURN(pipe.cancel != INVALID_HANDLE_VALUE, false); #else CARLA_SAFE_ASSERT_RETURN(pipe > 0, false); #endif CARLA_SAFE_ASSERT_RETURN(timeOutMilliseconds > 0, false); char c; ssize_t ret; const uint32_t timeoutEnd(juce::Time::getMillisecondCounter() + timeOutMilliseconds); for (;;) { try { #ifdef CARLA_OS_WIN ret = ::ReadFileNonBlock(pipe.handle, pipe.cancel, &c, 1); #else ret = ::read(pipe, &c, 1); #endif } CARLA_SAFE_EXCEPTION_BREAK("read pipefd"); switch (ret) { case -1: // failed to read #ifndef CARLA_OS_WIN if (errno == EAGAIN) #endif { if (juce::Time::getMillisecondCounter() < timeoutEnd) { carla_msleep(5); continue; } carla_stderr("waitForClientFirstMessage() - timed out"); } #ifndef CARLA_OS_WIN else { carla_stderr("waitForClientFirstMessage() - read failed"); CarlaString error(std::strerror(errno)); carla_stderr("waitForClientFirstMessage() - read failed: %s", error.buffer()); } #endif break; case 1: // read ok if (c == '\n') { // success return true; } else { carla_stderr("waitForClientFirstMessage() - read has wrong first char '%c'", c); } break; default: // ??? carla_stderr("waitForClientFirstMessage() - read returned %i", int(ret)); break; } break; } return false; } // ----------------------------------------------------------------------- // waitForChildToStop / waitForProcessToStop #ifdef CARLA_OS_WIN static inline bool waitForProcessToStop(const PROCESS_INFORMATION& processInfo, const uint32_t timeOutMilliseconds) noexcept { CARLA_SAFE_ASSERT_RETURN(processInfo.hProcess != INVALID_HANDLE_VALUE, false); CARLA_SAFE_ASSERT_RETURN(timeOutMilliseconds > 0, false); // TODO - this code is completly wrong... const uint32_t timeoutEnd(juce::Time::getMillisecondCounter() + timeOutMilliseconds); for (;;) { if (WaitForSingleObject(processInfo.hProcess, 0) == WAIT_OBJECT_0) return true; if (juce::Time::getMillisecondCounter() >= timeoutEnd) break; carla_msleep(5); } return false; } static inline void waitForProcessToStopOrKillIt(const PROCESS_INFORMATION& processInfo, const uint32_t timeOutMilliseconds) noexcept { CARLA_SAFE_ASSERT_RETURN(processInfo.hProcess != INVALID_HANDLE_VALUE,); CARLA_SAFE_ASSERT_RETURN(timeOutMilliseconds > 0,); if (! waitForProcessToStop(processInfo, timeOutMilliseconds)) { carla_stderr("waitForProcessToStopOrKillIt() - process didn't stop, force termination"); if (TerminateProcess(processInfo.hProcess, 0) != FALSE) { // wait for process to stop waitForProcessToStop(processInfo, timeOutMilliseconds); } } } #else static inline bool waitForChildToStop(const pid_t pid, const uint32_t timeOutMilliseconds) noexcept { CARLA_SAFE_ASSERT_RETURN(pid > 0, false); CARLA_SAFE_ASSERT_RETURN(timeOutMilliseconds > 0, false); pid_t ret; const uint32_t timeoutEnd(juce::Time::getMillisecondCounter() + timeOutMilliseconds); for (;;) { try { ret = ::waitpid(pid, nullptr, WNOHANG); } CARLA_SAFE_EXCEPTION_BREAK("waitpid"); switch (ret) { case -1: if (errno == ECHILD) { // success, child doesn't exist return true; } else { CarlaString error(std::strerror(errno)); carla_stderr("waitForChildToStop() - waitpid failed: %s", error.buffer()); return false; } break; case 0: if (juce::Time::getMillisecondCounter() < timeoutEnd) { carla_msleep(5); continue; } carla_stderr("waitForChildToStop() - timed out"); break; default: if (ret == pid) { // success return true; } else { carla_stderr("waitForChildToStop() - got wrong pid %i (requested was %i)", int(ret), int(pid)); return false; } } break; } return false; } static inline void waitForChildToStopOrKillIt(pid_t& pid, const uint32_t timeOutMilliseconds) noexcept { CARLA_SAFE_ASSERT_RETURN(pid > 0,); CARLA_SAFE_ASSERT_RETURN(timeOutMilliseconds > 0,); if (! waitForChildToStop(pid, timeOutMilliseconds)) { carla_stderr("waitForChildToStopOrKillIt() - process didn't stop, force killing"); if (::kill(pid, SIGKILL) != -1) { // wait for killing to take place waitForChildToStop(pid, timeOutMilliseconds); } else { CarlaString error(std::strerror(errno)); carla_stderr("waitForChildToStopOrKillIt() - kill failed: %s", error.buffer()); } } } #endif // ----------------------------------------------------------------------- struct CarlaPipeCommon::PrivateData { // pipes #ifdef CARLA_OS_WIN PROCESS_INFORMATION processInfo; HANDLE cancelEvent; HANDLE pipeRecv; HANDLE pipeSend; #else pid_t pid; int pipeRecv; int pipeSend; #endif // read functions must only be called in context of idle() bool isReading; // common write lock CarlaMutex writeLock; // temporary buffers for readline() char tmpBuf[0xff+1]; CarlaString tmpStr; PrivateData() noexcept #ifdef CARLA_OS_WIN : processInfo(), cancelEvent(INVALID_HANDLE_VALUE), #else : pid(-1), #endif pipeRecv(INVALID_PIPE_VALUE), pipeSend(INVALID_PIPE_VALUE), isReading(false), writeLock(), tmpBuf(), tmpStr() { #ifdef CARLA_OS_WIN carla_zeroStruct(processInfo); try { cancelEvent = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); } CARLA_SAFE_EXCEPTION("CreateEvent"); #endif carla_zeroChar(tmpBuf, 0xff+1); } ~PrivateData() noexcept { #ifdef CARLA_OS_WIN if (cancelEvent != INVALID_HANDLE_VALUE) { ::CloseHandle(cancelEvent); cancelEvent = INVALID_HANDLE_VALUE; } #endif } CARLA_DECLARE_NON_COPY_STRUCT(PrivateData) }; // ----------------------------------------------------------------------- CarlaPipeCommon::CarlaPipeCommon() noexcept : pData(new PrivateData()), leakDetector_CarlaPipeCommon() { carla_debug("CarlaPipeCommon::CarlaPipeCommon()"); } CarlaPipeCommon::~CarlaPipeCommon() noexcept { carla_debug("CarlaPipeCommon::~CarlaPipeCommon()"); delete pData; } // ------------------------------------------------------------------- void CarlaPipeCommon::idle() noexcept { const char* locale = nullptr; for (;;) { const char* const msg(readline()); if (msg == nullptr) break; if (locale == nullptr) { locale = carla_strdup_safe(::setlocale(LC_NUMERIC, nullptr)); ::setlocale(LC_NUMERIC, "C"); } pData->isReading = true; try { msgReceived(msg); } CARLA_SAFE_EXCEPTION("msgReceived"); pData->isReading = false; delete[] msg; } if (locale != nullptr) { ::setlocale(LC_NUMERIC, locale); delete[] locale; } } bool CarlaPipeCommon::isRunning() const noexcept { return (pData->pipeRecv != INVALID_PIPE_VALUE && pData->pipeSend != INVALID_PIPE_VALUE); } // ------------------------------------------------------------------- void CarlaPipeCommon::lock() const noexcept { pData->writeLock.lock(); } bool CarlaPipeCommon::tryLock() const noexcept { return pData->writeLock.tryLock(); } void CarlaPipeCommon::unlock() const noexcept { pData->writeLock.unlock(); } CarlaMutex& CarlaPipeCommon::getLock() noexcept { return pData->writeLock; } // ------------------------------------------------------------------- bool CarlaPipeCommon::readNextLineAsBool(bool& value) noexcept { CARLA_SAFE_ASSERT_RETURN(pData->isReading, false); if (const char* const msg = readlineblock()) { value = (std::strcmp(msg, "true") == 0); delete[] msg; return true; } return false; } bool CarlaPipeCommon::readNextLineAsInt(int32_t& value) noexcept { CARLA_SAFE_ASSERT_RETURN(pData->isReading, false); if (const char* const msg = readlineblock()) { value = std::atoi(msg); delete[] msg; return true; } return false; } bool CarlaPipeCommon::readNextLineAsUInt(uint32_t& value) noexcept { CARLA_SAFE_ASSERT_RETURN(pData->isReading, false); if (const char* const msg = readlineblock()) { int32_t tmp = std::atoi(msg); delete[] msg; if (tmp >= 0) { value = static_cast(tmp); return true; } } return false; } bool CarlaPipeCommon::readNextLineAsLong(int64_t& value) noexcept { CARLA_SAFE_ASSERT_RETURN(pData->isReading, false); if (const char* const msg = readlineblock()) { value = std::atol(msg); delete[] msg; return true; } return false; } bool CarlaPipeCommon::readNextLineAsULong(uint64_t& value) noexcept { CARLA_SAFE_ASSERT_RETURN(pData->isReading, false); if (const char* const msg = readlineblock()) { int64_t tmp = std::atol(msg); delete[] msg; if (tmp >= 0) { value = static_cast(tmp); return true; } } return false; } bool CarlaPipeCommon::readNextLineAsFloat(float& value) noexcept { CARLA_SAFE_ASSERT_RETURN(pData->isReading, false); if (const char* const msg = readlineblock()) { value = static_cast(std::atof(msg)); delete[] msg; return true; } return false; } bool CarlaPipeCommon::readNextLineAsDouble(double& value) noexcept { CARLA_SAFE_ASSERT_RETURN(pData->isReading, false); if (const char* const msg = readlineblock()) { value = std::atof(msg); delete[] msg; return true; } return false; } bool CarlaPipeCommon::readNextLineAsString(const char*& value) noexcept { CARLA_SAFE_ASSERT_RETURN(pData->isReading, false); if (const char* const msg = readlineblock()) { value = msg; return true; } return false; } // ------------------------------------------------------------------- // must be locked before calling bool CarlaPipeCommon::writeMsg(const char* const msg) const noexcept { CARLA_SAFE_ASSERT_RETURN(msg != nullptr && msg[0] != '\0', false); const std::size_t size(std::strlen(msg)); CARLA_SAFE_ASSERT_RETURN(size > 0, false); CARLA_SAFE_ASSERT_RETURN(msg[size-1] == '\n', false); return writeMsgBuffer(msg, size); } bool CarlaPipeCommon::writeMsg(const char* const msg, std::size_t size) const noexcept { CARLA_SAFE_ASSERT_RETURN(msg != nullptr && msg[0] != '\0', false); CARLA_SAFE_ASSERT_RETURN(size > 0, false); CARLA_SAFE_ASSERT_RETURN(msg[size-1] == '\n', false); return writeMsgBuffer(msg, size); } bool CarlaPipeCommon::writeAndFixMsg(const char* const msg) const noexcept { CARLA_SAFE_ASSERT_RETURN(msg != nullptr, false); const std::size_t size(std::strlen(msg)); char fixedMsg[size+2]; if (size > 0) { std::strcpy(fixedMsg, msg); for (size_t i=0; i < size; ++i) { if (fixedMsg[i] == '\n') fixedMsg[i] = '\r'; } if (fixedMsg[size-1] == '\r') { fixedMsg[size-1] = '\n'; fixedMsg[size] = '\0'; fixedMsg[size+1] = '\0'; } else { fixedMsg[size] = '\n'; fixedMsg[size+1] = '\0'; } } else { fixedMsg[0] = '\n'; fixedMsg[1] = '\0'; } return writeMsgBuffer(fixedMsg, size+1); } bool CarlaPipeCommon::flush() const noexcept { // TESTING remove later (replace with trylock scope) if (pData->writeLock.tryLock()) { carla_safe_assert("! pData->writeLock.tryLock()", __FILE__, __LINE__); pData->writeLock.unlock(); return false; } CARLA_SAFE_ASSERT_RETURN(pData->pipeSend != INVALID_PIPE_VALUE, false); try { #ifdef CARLA_OS_WIN return (::FlushFileBuffers(pData->pipeSend) != FALSE); #else return (::fsync(pData->pipeSend) == 0); #endif } CARLA_SAFE_EXCEPTION_RETURN("CarlaPipeCommon::writeMsgBuffer", false); } // ------------------------------------------------------------------- // internal const char* CarlaPipeCommon::readline() noexcept { CARLA_SAFE_ASSERT_RETURN(pData->pipeRecv != INVALID_PIPE_VALUE, nullptr); char c; char* ptr = pData->tmpBuf; ssize_t ret; pData->tmpStr.clear(); for (int i=0; i < 0xff; ++i) { try { #ifdef CARLA_OS_WIN ret = ::ReadFileNonBlock(pData->pipeRecv, pData->cancelEvent, &c, 1); #else ret = ::read(pData->pipeRecv, &c, 1); #endif } CARLA_SAFE_EXCEPTION_BREAK("CarlaPipeCommon::readline() - read"); if (ret == 1 && c != '\n') { if (c == '\r') c = '\n'; *ptr++ = c; if (i+1 == 0xff) { i = 0; ptr = pData->tmpBuf; pData->tmpStr += pData->tmpBuf; } continue; } if (pData->tmpStr.isNotEmpty() || ptr != pData->tmpBuf || ret == 1) { if (ptr != pData->tmpBuf) { *ptr = '\0'; pData->tmpStr += pData->tmpBuf; } try { return pData->tmpStr.dup(); } CARLA_SAFE_EXCEPTION_RETURN("CarlaPipeCommon::readline() - dup", nullptr); } break; } return nullptr; } const char* CarlaPipeCommon::readlineblock(const uint32_t timeOutMilliseconds) noexcept { const uint32_t timeoutEnd(juce::Time::getMillisecondCounter() + timeOutMilliseconds); for (;;) { if (const char* const msg = readline()) return msg; if (juce::Time::getMillisecondCounter() >= timeoutEnd) break; carla_msleep(5); } carla_stderr("readlineblock timed out"); return nullptr; } bool CarlaPipeCommon::writeMsgBuffer(const char* const msg, const std::size_t size) const noexcept { // TESTING remove later (replace with trylock scope) if (pData->writeLock.tryLock()) { carla_safe_assert("! pData->writeLock.tryLock()", __FILE__, __LINE__); pData->writeLock.unlock(); return false; } CARLA_SAFE_ASSERT_RETURN(pData->pipeSend != INVALID_PIPE_VALUE, false); ssize_t ret; try { #ifdef CARLA_OS_WIN ret = ::WriteFileNonBlock(pData->pipeSend, pData->cancelEvent, msg, size); #else ret = ::write(pData->pipeSend, msg, size); #endif } CARLA_SAFE_EXCEPTION_RETURN("CarlaPipeCommon::writeMsgBuffer", false); return (ret == static_cast(size)); } // ----------------------------------------------------------------------- CarlaPipeServer::CarlaPipeServer() noexcept : CarlaPipeCommon(), leakDetector_CarlaPipeServer() { carla_debug("CarlaPipeServer::CarlaPipeServer()"); } CarlaPipeServer::~CarlaPipeServer() noexcept { carla_debug("CarlaPipeServer::~CarlaPipeServer()"); stop(5*1000); } // ----------------------------------------------------------------------- bool CarlaPipeServer::start(const char* const filename, const char* const arg1, const char* const arg2) noexcept { CARLA_SAFE_ASSERT_RETURN(pData->pipeRecv == INVALID_PIPE_VALUE, false); CARLA_SAFE_ASSERT_RETURN(pData->pipeSend == INVALID_PIPE_VALUE, false); #ifdef CARLA_OS_WIN CARLA_SAFE_ASSERT_RETURN(pData->processInfo.hThread == nullptr, false); CARLA_SAFE_ASSERT_RETURN(pData->processInfo.hProcess == nullptr, false); #else CARLA_SAFE_ASSERT_RETURN(pData->pid == -1, false); #endif CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', false); CARLA_SAFE_ASSERT_RETURN(arg1 != nullptr, false); CARLA_SAFE_ASSERT_RETURN(arg2 != nullptr, false); carla_debug("CarlaPipeServer::start(\"%s\", \"%s\", \"%s\")", filename, arg1, arg2); const CarlaMutexLocker cml(pData->writeLock); //---------------------------------------------------------------- // create pipes #ifdef CARLA_OS_WIN HANDLE pipe1[2]; // read by server, written by client HANDLE pipe2[2]; // read by client, written by server SECURITY_ATTRIBUTES sa; carla_zeroStruct(sa); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; std::srand(static_cast(std::time(nullptr))); char strBuf[0xff+1]; strBuf[0xff] = '\0'; static ulong sCounter = 0; ++sCounter; const int randint = std::rand(); std::snprintf(strBuf, 0xff, "\\\\.\\pipe\\carla-pipe1-%i-%li", randint, sCounter); pipe1[0] = ::CreateNamedPipeA(strBuf, PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE|PIPE_WAIT, 1, 4096, 4096, 120*1000, &sa); pipe1[1] = ::CreateFileA(strBuf, GENERIC_WRITE, 0x0, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, nullptr); std::snprintf(strBuf, 0xff, "\\\\.\\pipe\\carla-pipe2-%i-%li", randint, sCounter); pipe2[0] = ::CreateNamedPipeA(strBuf, PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE|PIPE_WAIT, 1, 4096, 4096, 120*1000, &sa); pipe2[1] = ::CreateFileA(strBuf, GENERIC_WRITE, 0x0, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, nullptr); if (pipe1[0] == INVALID_HANDLE_VALUE || pipe1[1] == INVALID_HANDLE_VALUE || pipe2[0] == INVALID_HANDLE_VALUE || pipe2[1] == INVALID_HANDLE_VALUE) { if (pipe1[0] != INVALID_HANDLE_VALUE) { try { ::CloseHandle(pipe1[0]); } CARLA_SAFE_EXCEPTION("CloseHandle(pipe1[0])"); } if (pipe1[1] != INVALID_HANDLE_VALUE) { try { ::CloseHandle(pipe1[1]); } CARLA_SAFE_EXCEPTION("CloseHandle(pipe1[1])"); } if (pipe2[0] != INVALID_HANDLE_VALUE) { try { ::CloseHandle(pipe2[0]); } CARLA_SAFE_EXCEPTION("CloseHandle(pipe2[0])"); } if (pipe2[1] != INVALID_HANDLE_VALUE) { try { ::CloseHandle(pipe2[1]); } CARLA_SAFE_EXCEPTION("CloseHandle(pipe2[1])"); } fail("pipe creation failed"); return false; } HANDLE pipeRecvServer = pipe1[0]; HANDLE pipeRecvClient = pipe2[0]; HANDLE pipeSendClient = pipe1[1]; HANDLE pipeSendServer = pipe2[1]; #else int pipe1[2]; // read by server, written by client int pipe2[2]; // read by client, written by server if (::pipe(pipe1) != 0) { fail("pipe1 creation failed"); return false; } if (::pipe(pipe2) != 0) { try { ::close(pipe1[0]); } CARLA_SAFE_EXCEPTION("close(pipe1[0])"); try { ::close(pipe1[1]); } CARLA_SAFE_EXCEPTION("close(pipe1[1])"); fail("pipe2 creation failed"); return false; } int pipeRecvServer = pipe1[0]; int pipeRecvClient = pipe2[0]; int pipeSendClient = pipe1[1]; int pipeSendServer = pipe2[1]; #endif //---------------------------------------------------------------- // set arguments const char* argv[8]; //---------------------------------------------------------------- // argv[0] => filename argv[0] = filename; //---------------------------------------------------------------- // argv[1-2] => args argv[1] = arg1; argv[2] = arg2; //---------------------------------------------------------------- // argv[3-6] => pipes char pipeRecvServerStr[100+1]; char pipeRecvClientStr[100+1]; char pipeSendServerStr[100+1]; char pipeSendClientStr[100+1]; std::snprintf(pipeRecvServerStr, 100, P_INTPTR, (intptr_t)pipeRecvServer); // pipe1[0] std::snprintf(pipeRecvClientStr, 100, P_INTPTR, (intptr_t)pipeRecvClient); // pipe2[0] std::snprintf(pipeSendServerStr, 100, P_INTPTR, (intptr_t)pipeSendServer); // pipe2[1] std::snprintf(pipeSendClientStr, 100, P_INTPTR, (intptr_t)pipeSendClient); // pipe1[1] pipeRecvServerStr[100] = '\0'; pipeRecvClientStr[100] = '\0'; pipeSendServerStr[100] = '\0'; pipeSendClientStr[100] = '\0'; argv[3] = pipeRecvServerStr; // pipe1[0] close READ argv[4] = pipeRecvClientStr; // pipe2[0] READ argv[5] = pipeSendServerStr; // pipe2[1] close SEND argv[6] = pipeSendClientStr; // pipe1[1] SEND //---------------------------------------------------------------- // argv[7] => null argv[7] = nullptr; //---------------------------------------------------------------- // start process #ifdef CARLA_OS_WIN if (! startProcess(argv, pData->processInfo)) { carla_zeroStruct(pData->processInfo); try { ::CloseHandle(pipe1[0]); } CARLA_SAFE_EXCEPTION("CloseHandle(pipe1[0])"); try { ::CloseHandle(pipe1[1]); } CARLA_SAFE_EXCEPTION("CloseHandle(pipe1[1])"); try { ::CloseHandle(pipe2[0]); } CARLA_SAFE_EXCEPTION("CloseHandle(pipe2[0])"); try { ::CloseHandle(pipe2[1]); } CARLA_SAFE_EXCEPTION("CloseHandle(pipe2[1])"); fail("startProcess() failed"); return false; } // just to make sure CARLA_SAFE_ASSERT(pData->processInfo.hThread != nullptr); CARLA_SAFE_ASSERT(pData->processInfo.hProcess != nullptr); #else if (! startProcess(argv, pData->pid)) { pData->pid = -1; try { ::close(pipe1[0]); } CARLA_SAFE_EXCEPTION("close(pipe1[0])"); try { ::close(pipe1[1]); } CARLA_SAFE_EXCEPTION("close(pipe1[1])"); try { ::close(pipe2[0]); } CARLA_SAFE_EXCEPTION("close(pipe2[0])"); try { ::close(pipe2[1]); } CARLA_SAFE_EXCEPTION("close(pipe2[1])"); fail("startProcess() failed"); return false; } #endif //---------------------------------------------------------------- // close duplicated handles used by the client #ifdef CARLA_OS_WIN try { ::CloseHandle(pipeRecvServer); } CARLA_SAFE_EXCEPTION("CloseHandle(pipeRecvServer)"); try { ::CloseHandle(pipeSendServer); } CARLA_SAFE_EXCEPTION("CloseHandle(pipeSendServer)"); #else try { ::close (pipeRecvServer); } CARLA_SAFE_EXCEPTION("close(pipeRecvServer)"); try { ::close (pipeSendServer); } CARLA_SAFE_EXCEPTION("close(pipeSendServer)"); #endif pipeRecvServer = pipeSendServer = INVALID_PIPE_VALUE; #ifndef CARLA_OS_WIN //---------------------------------------------------------------- // set non-block reading int ret = 0; try { ret = ::fcntl(pipeRecvClient, F_SETFL, ::fcntl(pipeRecvClient, F_GETFL) | O_NONBLOCK); } catch (...) { ret = -1; fail("failed to set pipe as non-block"); } #endif //---------------------------------------------------------------- // wait for client to say something #ifdef CARLA_OS_WIN struct { HANDLE handle; HANDLE cancel; } pipe; pipe.handle = pipeRecvClient; pipe.cancel = pData->cancelEvent; if ( waitForClientFirstMessage(pipe, 10*1000 /* 10 secs */)) #else if (ret != -1 && waitForClientFirstMessage(pipeRecvClient, 10*1000 /* 10 secs */)) #endif { pData->pipeRecv = pipeRecvClient; pData->pipeSend = pipeSendClient; carla_stdout("ALL OK!"); return true; } //---------------------------------------------------------------- // failed to set non-block or get first child message, cannot continue #ifdef CARLA_OS_WIN if (TerminateProcess(pData->processInfo.hProcess, 0) != FALSE) { // wait for process to stop waitForProcessToStop(pData->processInfo, 2*1000); } // clear pData->processInfo try { CloseHandle(pData->processInfo.hThread); } CARLA_SAFE_EXCEPTION("CloseHandle(pData->processInfo.hThread)"); try { CloseHandle(pData->processInfo.hProcess); } CARLA_SAFE_EXCEPTION("CloseHandle(pData->processInfo.hProcess)"); carla_zeroStruct(pData->processInfo); #else if (::kill(pData->pid, SIGKILL) != -1) { // wait for killing to take place waitForChildToStop(pData->pid, 2*1000); } pData->pid = -1; #endif //---------------------------------------------------------------- // close pipes #ifdef CARLA_OS_WIN try { ::CloseHandle(pipeRecvServer); } CARLA_SAFE_EXCEPTION("CloseHandle(pipeRecvServer)"); try { ::CloseHandle(pipeSendServer); } CARLA_SAFE_EXCEPTION("CloseHandle(pipeSendServer)"); #else try { ::close (pipeRecvServer); } CARLA_SAFE_EXCEPTION("close(pipeRecvServer)"); try { ::close (pipeSendServer); } CARLA_SAFE_EXCEPTION("close(pipeSendServer)"); #endif return false; } void CarlaPipeServer::stop(const uint32_t timeOutMilliseconds) noexcept { carla_debug("CarlaPipeServer::stop(%i)", timeOutMilliseconds); #ifdef CARLA_OS_WIN if (pData->processInfo.hProcess != INVALID_HANDLE_VALUE) { const CarlaMutexLocker cml(pData->writeLock); if (pData->pipeSend != INVALID_PIPE_VALUE) writeMsgBuffer("quit\n", 5); waitForProcessToStopOrKillIt(pData->processInfo, timeOutMilliseconds); try { CloseHandle(pData->processInfo.hThread); } CARLA_SAFE_EXCEPTION("CloseHandle(pData->processInfo.hThread)"); try { CloseHandle(pData->processInfo.hProcess); } CARLA_SAFE_EXCEPTION("CloseHandle(pData->processInfo.hProcess)"); carla_zeroStruct(pData->processInfo); } #else if (pData->pid != -1) { const CarlaMutexLocker cml(pData->writeLock); if (pData->pipeSend != INVALID_PIPE_VALUE) writeMsgBuffer("quit\n", 5); waitForChildToStopOrKillIt(pData->pid, timeOutMilliseconds); pData->pid = -1; } #endif close(); } void CarlaPipeServer::close() noexcept { carla_debug("CarlaPipeServer::close()"); const CarlaMutexLocker cml(pData->writeLock); if (pData->pipeRecv != INVALID_PIPE_VALUE) { #ifdef CARLA_OS_WIN try { ::CloseHandle(pData->pipeRecv); } CARLA_SAFE_EXCEPTION("CloseHandle(pData->pipeRecv)"); #else try { ::close (pData->pipeRecv); } CARLA_SAFE_EXCEPTION("close(pData->pipeRecv)"); #endif pData->pipeRecv = INVALID_PIPE_VALUE; } if (pData->pipeSend != INVALID_PIPE_VALUE) { #ifdef CARLA_OS_WIN try { ::CloseHandle(pData->pipeSend); } CARLA_SAFE_EXCEPTION("CloseHandle(pData->pipeSend)"); #else try { ::close (pData->pipeSend); } CARLA_SAFE_EXCEPTION("close(pData->pipeSend)"); #endif pData->pipeSend = INVALID_PIPE_VALUE; } } // ----------------------------------------------------------------------- CarlaPipeClient::CarlaPipeClient() noexcept : CarlaPipeCommon(), leakDetector_CarlaPipeClient() { carla_debug("CarlaPipeClient::CarlaPipeClient()"); } CarlaPipeClient::~CarlaPipeClient() noexcept { carla_debug("CarlaPipeClient::~CarlaPipeClient()"); close(); } bool CarlaPipeClient::init(const char* argv[]) noexcept { CARLA_SAFE_ASSERT_RETURN(pData->pipeRecv == INVALID_PIPE_VALUE, false); CARLA_SAFE_ASSERT_RETURN(pData->pipeSend == INVALID_PIPE_VALUE, false); const CarlaMutexLocker cml(pData->writeLock); //---------------------------------------------------------------- // read arguments #ifdef CARLA_OS_WIN HANDLE pipeRecvServer = (HANDLE)std::atol(argv[3]); // READ HANDLE pipeRecvClient = (HANDLE)std::atol(argv[4]); HANDLE pipeSendServer = (HANDLE)std::atol(argv[5]); // SEND HANDLE pipeSendClient = (HANDLE)std::atol(argv[6]); CARLA_SAFE_ASSERT_RETURN(pipeRecvServer != INVALID_HANDLE_VALUE, false); CARLA_SAFE_ASSERT_RETURN(pipeRecvClient != INVALID_HANDLE_VALUE, false); CARLA_SAFE_ASSERT_RETURN(pipeSendServer != INVALID_HANDLE_VALUE, false); CARLA_SAFE_ASSERT_RETURN(pipeSendClient != INVALID_HANDLE_VALUE, false); #else int pipeRecvServer = std::atoi(argv[3]); // READ int pipeRecvClient = std::atoi(argv[4]); int pipeSendServer = std::atoi(argv[5]); // SEND int pipeSendClient = std::atoi(argv[6]); CARLA_SAFE_ASSERT_RETURN(pipeRecvServer > 0, false); CARLA_SAFE_ASSERT_RETURN(pipeRecvClient > 0, false); CARLA_SAFE_ASSERT_RETURN(pipeSendServer > 0, false); CARLA_SAFE_ASSERT_RETURN(pipeSendClient > 0, false); #endif //---------------------------------------------------------------- // close duplicated handles used by the client #ifdef CARLA_OS_WIN try { ::CloseHandle(pipeRecvClient); } CARLA_SAFE_EXCEPTION("CloseHandle(pipeRecvClient)"); try { ::CloseHandle(pipeSendClient); } CARLA_SAFE_EXCEPTION("CloseHandle(pipeSendClient)"); #else try { ::close (pipeRecvClient); } CARLA_SAFE_EXCEPTION("close(pipeRecvClient)"); try { ::close (pipeSendClient); } CARLA_SAFE_EXCEPTION("close(pipeSendClient)"); #endif pipeRecvClient = pipeSendClient = INVALID_PIPE_VALUE; #ifndef CARLA_OS_WIN //---------------------------------------------------------------- // set non-block reading int ret = 0; try { ret = ::fcntl(pipeRecvServer, F_SETFL, ::fcntl(pipeRecvServer, F_GETFL) | O_NONBLOCK); } catch (...) { ret = -1; } CARLA_SAFE_ASSERT_RETURN(ret != -1, false); #endif //---------------------------------------------------------------- // done pData->pipeRecv = pipeRecvServer; pData->pipeSend = pipeSendServer; writeMsg("\n"); flush(); return true; } void CarlaPipeClient::close() noexcept { carla_debug("CarlaPipeClient::close()"); const CarlaMutexLocker cml(pData->writeLock); if (pData->pipeRecv != INVALID_PIPE_VALUE) { #ifdef CARLA_OS_WIN try { ::CloseHandle(pData->pipeRecv); } CARLA_SAFE_EXCEPTION("CloseHandle(pData->pipeRecv)"); #else try { ::close (pData->pipeRecv); } CARLA_SAFE_EXCEPTION("close(pData->pipeRecv)"); #endif pData->pipeRecv = INVALID_PIPE_VALUE; } if (pData->pipeSend != INVALID_PIPE_VALUE) { #ifdef CARLA_OS_WIN try { ::CloseHandle(pData->pipeSend); } CARLA_SAFE_EXCEPTION("CloseHandle(pData->pipeSend)"); #else try { ::close (pData->pipeSend); } CARLA_SAFE_EXCEPTION("close(pData->pipeSend)"); #endif pData->pipeSend = INVALID_PIPE_VALUE; } } // ----------------------------------------------------------------------- ScopedLocale::ScopedLocale() noexcept : fLocale(carla_strdup_safe(::setlocale(LC_NUMERIC, nullptr))) { ::setlocale(LC_NUMERIC, "C"); } ScopedLocale::~ScopedLocale() noexcept { if (fLocale != nullptr) { ::setlocale(LC_NUMERIC, fLocale); delete[] fLocale; } } // ----------------------------------------------------------------------- #undef INVALID_PIPE_VALUE