Browse Source

Rework CarlaPipeUtils code, kinda working win32 implementation

tags/1.9.6
falkTX 10 years ago
parent
commit
376306563d
9 changed files with 1440 additions and 661 deletions
  1. +2
    -0
      .gitignore
  2. +1
    -0
      source/backend/CarlaStandalone.cpp
  3. +19
    -25
      source/backend/engine/CarlaEngineNative.cpp
  4. +9
    -7
      source/externalui.py
  5. +12
    -19
      source/modules/CarlaNativeExtUI.hpp
  6. +97
    -0
      source/tests/CarlaPipeUtils.cpp
  7. +3
    -2
      source/utils/CarlaExternalUI.hpp
  8. +1226
    -0
      source/utils/CarlaPipeUtils.cpp
  9. +71
    -608
      source/utils/CarlaPipeUtils.hpp

+ 2
- 0
.gitignore View File

@@ -18,6 +18,7 @@
*.dll.def
*.dylib
*.exe
*.msi
*.so

# Project files
@@ -92,6 +93,7 @@ source/bridges/jackplugin/libjack.so.0
source/frontend/Makefile
source/tests/ansi-pedantic-test_*
source/tests/CarlaRingBuffer
source/tests/CarlaPipeUtils
source/tests/CarlaString
source/tests/CarlaUtils1
source/tests/CarlaUtils2


+ 1
- 0
source/backend/CarlaStandalone.cpp View File

@@ -2163,6 +2163,7 @@ const char* carla_get_host_osc_url_udp()

#include "CarlaPluginUI.cpp"
#include "CarlaDssiUtils.cpp"
#include "CarlaPipeUtils.cpp"
#include "CarlaStateUtils.cpp"
#include "CarlaJuceEvents.cpp"



+ 19
- 25
source/backend/engine/CarlaEngineNative.cpp View File

@@ -76,14 +76,9 @@ public:
carla_debug("CarlaEngineNativeUI::~CarlaEngineNativeUI()");
}

CarlaMutex& getWriteLock() noexcept
{
return fWriteLock;
}

void show() noexcept
{
const CarlaMutexLocker cml(fWriteLock);
const CarlaMutexLocker cml(getLock());
writeMsg("show\n", 5);
}

@@ -549,7 +544,7 @@ protected:

if (! ok)
{
const CarlaMutexLocker cml(fWriteLock);
const CarlaMutexLocker cml(getLock());
writeMsg("error\n", 6);
writeAndFixMsg(fEngine->getLastError());
}
@@ -715,7 +710,7 @@ protected:
return;

{
const CarlaMutexLocker cml(fUiServer.getWriteLock());
const CarlaMutexLocker cml(fUiServer.getLock());

fUiServer.writeAndFixMsg("buffer-size");
std::sprintf(fTmpBuf, "%i\n", newBufferSize);
@@ -732,7 +727,7 @@ protected:
return;

{
const CarlaMutexLocker cml(fUiServer.getWriteLock());
const CarlaMutexLocker cml(fUiServer.getLock());
const ScopedLocale csl;

fUiServer.writeAndFixMsg("sample-rate");
@@ -748,7 +743,7 @@ protected:

void uiServerSendPluginInfo(CarlaPlugin* const plugin)
{
const CarlaMutexLocker cml(fUiServer.getWriteLock());
const CarlaMutexLocker cml(fUiServer.getLock());

const uint pluginId(plugin->getId());

@@ -803,7 +798,7 @@ protected:

void uiServerSendPluginParameters(CarlaPlugin* const plugin)
{
const CarlaMutexLocker cml(fUiServer.getWriteLock());
const CarlaMutexLocker cml(fUiServer.getLock());
const ScopedLocale csl;

const uint pluginId(plugin->getId());
@@ -850,7 +845,7 @@ protected:

void uiServerSendPluginPrograms(CarlaPlugin* const plugin)
{
const CarlaMutexLocker cml(fUiServer.getWriteLock());
const CarlaMutexLocker cml(fUiServer.getLock());

const uint pluginId(plugin->getId());

@@ -887,7 +882,7 @@ protected:
{
if (! fIsRunning)
return;
if (! fUiServer.isOk())
if (! fUiServer.isRunning())
return;

CarlaPlugin* plugin;
@@ -941,7 +936,7 @@ protected:
break;
}

const CarlaMutexLocker cml(fUiServer.getWriteLock());
const CarlaMutexLocker cml(fUiServer.getLock());
const ScopedLocale csl;

std::sprintf(fTmpBuf, "ENGINE_CALLBACK_%i\n", int(action));
@@ -965,9 +960,9 @@ protected:
void uiServerInfo()
{
CARLA_SAFE_ASSERT_RETURN(fIsRunning,);
CARLA_SAFE_ASSERT_RETURN(fUiServer.isOk(),);
CARLA_SAFE_ASSERT_RETURN(fUiServer.isRunning(),);

const CarlaMutexLocker cml(fUiServer.getWriteLock());
const CarlaMutexLocker cml(fUiServer.getLock());

fUiServer.writeAndFixMsg("complete-license");
fUiServer.writeAndFixMsg(carla_get_complete_license_text());
@@ -996,10 +991,10 @@ protected:
void uiServerOptions()
{
CARLA_SAFE_ASSERT_RETURN(fIsRunning,);
CARLA_SAFE_ASSERT_RETURN(fUiServer.isOk(),);
CARLA_SAFE_ASSERT_RETURN(fUiServer.isRunning(),);

const EngineOptions& options(pData->options);
const CarlaMutexLocker cml(fUiServer.getWriteLock());
const CarlaMutexLocker cml(fUiServer.getLock());

std::sprintf(fTmpBuf, "ENGINE_OPTION_%i\n", ENGINE_OPTION_PROCESS_MODE);
fUiServer.writeMsg(fTmpBuf);
@@ -1401,7 +1396,7 @@ protected:
{
if (show)
{
if (fUiServer.isOk())
if (fUiServer.isRunning())
return;

CarlaString path(pHost->resourceDir);
@@ -1437,7 +1432,7 @@ protected:
}
else
{
fUiServer.stop();
fUiServer.stop(5000);
}
}

@@ -1446,12 +1441,10 @@ protected:
CarlaEngine::idle();
fUiServer.idle();

if (! fUiServer.isOk())
return;

if (fUiServer.isRunning())
{
const EngineTimeInfo& timeInfo(pData->timeInfo);
const CarlaMutexLocker cml(fUiServer.getWriteLock());
const CarlaMutexLocker cml(fUiServer.getLock());
const ScopedLocale csl;

// send transport
@@ -1507,7 +1500,7 @@ protected:
break;
case CarlaExternalUI::UiHide:
pHost->ui_closed(pHost->handle);
fUiServer.stop();
fUiServer.stop(2000);
break;
}
}
@@ -1943,6 +1936,7 @@ CARLA_BACKEND_END_NAMESPACE
#include "CarlaHostCommon.cpp"
#include "CarlaPluginUI.cpp"
#include "CarlaDssiUtils.cpp"
#include "CarlaPipeUtils.cpp"
#include "CarlaStateUtils.cpp"
#include "CarlaJuceEvents.cpp"



+ 9
- 7
source/externalui.py View File

@@ -38,14 +38,17 @@ class ExternalUI(object):
if len(argv) > 1:
self.fSampleRate = float(argv[1])
self.fUiName = argv[2]
self.fPipeRecvFd = int(argv[3])
self.fPipeSendFd = int(argv[4])

oldFlags = fcntl(self.fPipeRecvFd, F_GETFL)
fcntl(self.fPipeRecvFd, F_SETFL, oldFlags | O_NONBLOCK)
pipeRecvServer = int(argv[3])
pipeRecvClient = int(argv[4])
pipeSendServer = int(argv[5])
pipeSendClient = int(argv[6])

self.fPipeRecv = fdopen(self.fPipeRecvFd, 'r')
self.fPipeSend = fdopen(self.fPipeSendFd, 'w')
oldFlags = fcntl(pipeRecvServer, F_GETFL)
fcntl(pipeRecvServer, F_SETFL, oldFlags | O_NONBLOCK)

self.fPipeRecv = fdopen(pipeRecvServer, 'r')
self.fPipeSend = fdopen(pipeSendServer, 'w')

else:
self.fSampleRate = 44100.0
@@ -54,7 +57,6 @@ class ExternalUI(object):
self.fPipeRecv = None
self.fPipeSend = None


def ready(self):
if self.fPipeRecv is not None:
# send empty line (just newline char)


+ 12
- 19
source/modules/CarlaNativeExtUI.hpp View File

@@ -35,13 +35,9 @@ class NativePluginAndUiClass : public NativePluginClass,
public:
NativePluginAndUiClass(const NativeHostDescriptor* const host, const char* const extUiPath)
: NativePluginClass(host),
fExtUiPath(extUiPath)
{
}

~NativePluginAndUiClass() override
{
}
CarlaExternalUI(),
fExtUiPath(extUiPath),
leakDetector_NativePluginAndUiClass() {}

protected:
// -------------------------------------------------------------------
@@ -51,7 +47,7 @@ protected:
{
if (show)
{
if (isOk())
if (isRunning())
return;

CarlaString path(getResourceDir() + fExtUiPath);
@@ -62,7 +58,7 @@ protected:
}
else
{
CarlaExternalUI::stop();
CarlaExternalUI::stop(5000);
}
}

@@ -70,9 +66,6 @@ protected:
{
CarlaExternalUI::idle();

if (! CarlaExternalUI::isOk())
return;

switch (CarlaExternalUI::getAndResetUiState())
{
case CarlaExternalUI::UiNone:
@@ -83,7 +76,7 @@ protected:
break;
case CarlaExternalUI::UiHide:
uiClosed();
CarlaExternalUI::stop();
CarlaExternalUI::stop(2000);
break;
}
}
@@ -94,7 +87,7 @@ protected:

char tmpBuf[0xff+1];

const CarlaMutexLocker cml(fWriteLock);
const CarlaMutexLocker cml(getLock());

writeMsg("control\n", 8);
std::sprintf(tmpBuf, "%i\n", index);
@@ -109,7 +102,7 @@ protected:

char tmpBuf[0xff+1];

const CarlaMutexLocker cml(fWriteLock);
const CarlaMutexLocker cml(getLock());

writeMsg("program\n", 8);
std::sprintf(tmpBuf, "%i\n", channel);
@@ -125,7 +118,7 @@ protected:
CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
CARLA_SAFE_ASSERT_RETURN(value != nullptr,);

const CarlaMutexLocker cml(fWriteLock);
const CarlaMutexLocker cml(getLock());

writeMsg("configure\n", 10);
writeAndFixMsg(key);
@@ -150,7 +143,7 @@ protected:

try {
uiParameterChanged(param, value);
} catch(...) {}
} CARLA_SAFE_EXCEPTION("uiParameterChanged");

return true;
}
@@ -166,7 +159,7 @@ protected:

try {
uiMidiProgramChanged(channel, bank, program);
} catch(...) {}
} CARLA_SAFE_EXCEPTION("uiMidiProgramChanged");

return true;
}
@@ -181,7 +174,7 @@ protected:

try {
uiCustomDataChanged(key, value);
} catch(...) {}
} CARLA_SAFE_EXCEPTION("uiCustomDataChanged");

delete[] key;
delete[] value;


+ 97
- 0
source/tests/CarlaPipeUtils.cpp View File

@@ -0,0 +1,97 @@
/*
* Carla Utility Tests
* Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
*
* 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 "../utils/CarlaPipeUtils.cpp"

// -----------------------------------------------------------------------

class CarlaPipeClient2 : public CarlaPipeClient
{
public:
CarlaPipeClient2()
: CarlaPipeClient() {}

bool msgReceived(const char* const msg) noexcept override
{
carla_stdout("CLIENT RECEIVED: \"%s\"", msg);
return true;
}
};

class CarlaPipeServer2 : public CarlaPipeServer
{
public:
CarlaPipeServer2()
: CarlaPipeServer() {}

bool msgReceived(const char* const msg) noexcept override
{
carla_stdout("SERVER RECEIVED: \"%s\"", msg);
return true;
}
};

// -----------------------------------------------------------------------

int main(int argc, char* argv[])
{
if (argc != 1)
{
carla_stdout("CLIENT STARTED %i", argc);

CarlaPipeClient2 p;
const bool ok = p.start(argv);
CARLA_SAFE_ASSERT_RETURN(ok,1);

p.lock();
p.writeMsg("CLIENT=>SERVER\n");
p.writeAndFixMsg("heheheheheh");
p.unlock();

carla_sleep(2);
carla_stderr2("CLIENT idle start");
p.idle();
carla_stderr2("CLIENT idle end");
carla_sleep(2);
}
else
{
carla_stdout("SERVER STARTED %i", argc);

CarlaPipeServer2 p;
#ifdef CARLA_OS_WIN
const bool ok = p.start("H:\\Source\\falkTX\\Carla\\source\\tests\\CarlaPipeUtils.exe", "/home/falktx/Videos", "/home/falktx");
#else
const bool ok = p.start("/home/falktx/Source/falkTX/Carla/source/tests/CarlaPipeUtils", "/home/falktx/Videos", "/home/falktx");
#endif
CARLA_SAFE_ASSERT_RETURN(ok,1);

p.lock();
p.writeMsg("SERVER=>CLIENT\n");
p.unlock();

carla_sleep(2);
carla_stderr2("SERVER idle start");
p.idle();
carla_stderr2("SERVER idle end");
carla_sleep(2);
}

return 0;
}

// -----------------------------------------------------------------------

+ 3
- 2
source/utils/CarlaExternalUI.hpp View File

@@ -19,6 +19,7 @@
#define CARLA_EXTERNAL_UI_HPP_INCLUDED

#include "CarlaPipeUtils.hpp"
#include "CarlaString.hpp"

// -----------------------------------------------------------------------

@@ -65,7 +66,7 @@ public:
if (! show)
return;

const CarlaMutexLocker cml(fWriteLock);
const CarlaMutexLocker cml(getLock());
writeMsg("show\n", 5);
}

@@ -75,7 +76,7 @@ protected:
{
if (std::strcmp(msg, "exiting") == 0)
{
waitChildClose();
close();
fUiState = UiHide;
return true;
}


+ 1226
- 0
source/utils/CarlaPipeUtils.cpp
File diff suppressed because it is too large
View File


+ 71
- 608
source/utils/CarlaPipeUtils.hpp View File

@@ -1,6 +1,5 @@
/*
* Carla Pipe utils based on lv2fil UI code
* Copyright (C) 2009 Nedko Arnaudov <nedko@arnaudov.name>
* Carla Pipe utils
* Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
@@ -19,653 +18,117 @@
#ifndef CARLA_PIPE_UTILS_HPP_INCLUDED
#define CARLA_PIPE_UTILS_HPP_INCLUDED

#include "CarlaJuceUtils.hpp"
#include "CarlaMutex.hpp"
#include "CarlaString.hpp"

#include <cerrno>
#include <clocale>

#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>

#define WAIT_START_TIMEOUT 10000 /* 10 secs */
#define WAIT_ZOMBIE_TIMEOUT 10000 /* 10 secs */
#define WAIT_STEP 100 /* 100 ms */

// -----------------------------------------------------------------------

struct ScopedLocale {
const char* locale;

ScopedLocale() noexcept
: locale(carla_strdup_safe(::setlocale(LC_NUMERIC, nullptr)))
{
::setlocale(LC_NUMERIC, "C");
}

~ScopedLocale() noexcept
{
if (locale != nullptr)
{
::setlocale(LC_NUMERIC, locale);
delete[] locale;
locale = nullptr;
}
}

CARLA_DECLARE_NON_COPY_STRUCT(ScopedLocale)
CARLA_PREVENT_HEAP_ALLOCATION
};

// -----------------------------------------------------------------------

class CarlaPipeServer
class CarlaPipeCommon
{
protected:
CarlaPipeServer()
: fWriteLock(),
fPipeRecv(-1),
fPipeSend(-1),
fIsReading(false),
fPid(-1),
fTmpStr()
{
carla_debug("CarlaPipeServer::CarlaPipeServer()");

carla_zeroChar(fTmpBuf, 0xff+1);
}

// -------------------------------------------------------------------

public:
virtual ~CarlaPipeServer()
{
carla_debug("CarlaPipeServer::~CarlaPipeServer()");

stop();
}

bool isOk() const noexcept
{
return (fPipeRecv != -1 && fPipeSend != -1 && fPid != -1);
}

bool start(const char* const filename, const char* const arg1, const char* const arg2) noexcept
{
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(fWriteLock);

//----------------------------------------------------------------

const char* argv[6];

//----------------------------------------------------------------
// argv[0] => filename
CarlaPipeCommon() noexcept;
virtual ~CarlaPipeCommon() noexcept;

argv[0] = filename;

//----------------------------------------------------------------
// argv[1-2] => args

argv[1] = arg1;
argv[2] = arg2;

//----------------------------------------------------------------
// argv[3-4] => pipes

int pipe1[2]; // written by host process, read by plugin UI process
int pipe2[2]; // written by plugin UI process, read by host process

if (::pipe(pipe1) != 0)
{
fail("pipe1 creation failed");
return false;
}

if (::pipe(pipe2) != 0)
{
try { ::close(pipe1[0]); } catch (...) {}
try { ::close(pipe1[1]); } catch (...) {}
fail("pipe2 creation failed");
return false;
}

char pipeRecv[100+1];
char pipeSend[100+1];

std::snprintf(pipeRecv, 100, "%d", pipe1[0]); // [0] means reading end
std::snprintf(pipeSend, 100, "%d", pipe2[1]); // [1] means writting end

pipeRecv[100] = '\0';
pipeSend[100] = '\0';

argv[3] = pipeRecv; // reading end
argv[4] = pipeSend; // writting end

//----------------------------------------------------------------
// argv[5] => null

argv[5] = nullptr;

//----------------------------------------------------------------
// fork

int ret = -1;

if ((! fork_exec(argv, &ret)) || ret == -1)
{
try { ::close(pipe1[0]); } catch (...) {}
try { ::close(pipe1[1]); } catch (...) {}
try { ::close(pipe2[0]); } catch (...) {}
try { ::close(pipe2[1]); } catch (...) {}
fail("fork_exec() failed");
return false;
}

fPid = ret;

// fork duplicated the handles, close pipe ends that are used by the child process
try { ::close(pipe1[0]); } catch(...) {}
try { ::close(pipe2[1]); } catch(...) {}

fPipeSend = pipe1[1]; // [1] means writting end
fPipeRecv = pipe2[0]; // [0] means reading end

// set non-block
try {
ret = ::fcntl(fPipeRecv, F_SETFL, ::fcntl(fPipeRecv, F_GETFL) | O_NONBLOCK);
} catch (...) {
ret = -1;
fail("failed to set pipe as non-block");
}

//----------------------------------------------------------------
// wait a while for child process to confirm it is alive

if (ret != -1)
{
char ch;
ssize_t ret2;

for (int i=0; ;)
{
try {
ret2 = ::read(fPipeRecv, &ch, 1);
}
catch (...) {
fail("failed to read from pipe");
break;
}

switch (ret2)
{
case -1:
if (errno == EAGAIN)
{
if (i < WAIT_START_TIMEOUT / WAIT_STEP)
{
carla_msleep(WAIT_STEP);
++i;
continue;
}
carla_stderr("we have waited for child with pid %d to appear for %.1f seconds and we are giving up", int(fPid), float(WAIT_START_TIMEOUT)/1000.0f);
}
else
{
CarlaString error(std::strerror(errno));
carla_stderr("read() failed: %s", error.buffer());
}
break;

case 1:
if (ch == '\n') {
// success
return true;
}
carla_stderr("read() has wrong first char '%c'", ch);
break;

default:
carla_stderr("read() returned %i", int(ret2));
break;
}

break;
}
}

carla_stderr("force killing misbehaved child %i (start)", int(fPid));

if (kill(fPid, SIGKILL) == -1)
{
CarlaString error(std::strerror(errno));
carla_stderr("kill() failed: %s (start)\n", error.buffer());
}

// wait a while child to exit, we dont like zombie processes
wait_child(fPid);

// close pipes
try { ::close(fPipeRecv); } catch (...) {}
try { ::close(fPipeSend); } catch (...) {}

fPipeRecv = -1;
fPipeSend = -1;
fPid = -1;

return false;
}
// returns true if msg handled
virtual bool msgReceived(const char* const msg) noexcept = 0;

void stop() noexcept
// to possibly send errors somewhere
virtual void fail(const char* const error) noexcept
{
carla_debug("CarlaPipeServer::stop()");

if (fPipeSend == -1 || fPipeRecv == -1 || fPid == -1)
return;

const CarlaMutexLocker cml(fWriteLock);

try {
ssize_t ignore = ::write(fPipeSend, "quit\n", 5);
(void)ignore;
} CARLA_SAFE_EXCEPTION("CarlaPipeServer::stop");

waitChildClose();

try { ::close(fPipeRecv); } catch (...) {}
try { ::close(fPipeSend); } catch (...) {}

fPipeRecv = -1;
fPipeSend = -1;
fPid = -1;
carla_stderr2(error);
}

void idle()
{
const char* locale = nullptr;

for (;;)
{
const char* const msg(readline());

if (msg == nullptr)
break;

if (locale == nullptr)
{
locale = carla_strdup(::setlocale(LC_NUMERIC, nullptr));
::setlocale(LC_NUMERIC, "C");
}

fIsReading = true;
msgReceived(msg);
fIsReading = false;

delete[] msg;
}

if (locale != nullptr)
{
::setlocale(LC_NUMERIC, locale);
delete[] locale;
}
}
public:
void idle() noexcept;
bool isRunning() const noexcept;

// -------------------------------------------------------------------
// write lock

bool readNextLineAsBool(bool& value) noexcept
{
CARLA_SAFE_ASSERT_RETURN(fIsReading, false);

if (const char* const msg = readline())
{
value = (std::strcmp(msg, "true") == 0);
delete[] msg;
return true;
}

return false;
}

bool readNextLineAsInt(int32_t& value) noexcept
{
CARLA_SAFE_ASSERT_RETURN(fIsReading, false);

if (const char* const msg = readline())
{
value = std::atoi(msg);
delete[] msg;
return true;
}

return false;
}
void lock() const noexcept;
bool tryLock() const noexcept;
void unlock() const noexcept;

bool readNextLineAsUInt(uint32_t& value) noexcept
{
CARLA_SAFE_ASSERT_RETURN(fIsReading, false);

if (const char* const msg = readline())
{
int32_t tmp = std::atoi(msg);
delete[] msg;
CarlaMutex& getLock() noexcept;

if (tmp >= 0)
{
value = static_cast<uint32_t>(tmp);
return true;
}
}

return false;
}

bool readNextLineAsLong(int64_t& value) noexcept
{
CARLA_SAFE_ASSERT_RETURN(fIsReading, false);

if (const char* const msg = readline())
{
value = std::atol(msg);
delete[] msg;
return true;
}

return false;
}

bool readNextLineAsULong(uint64_t& value) noexcept
{
CARLA_SAFE_ASSERT_RETURN(fIsReading, false);

if (const char* const msg = readline())
{
int64_t tmp = std::atol(msg);
delete[] msg;

if (tmp >= 0)
{
value = static_cast<uint64_t>(tmp);
return true;
}
}

return false;
}

bool readNextLineAsFloat(float& value) noexcept
{
CARLA_SAFE_ASSERT_RETURN(fIsReading, false);

if (const char* const msg = readline())
{
const bool ret(std::sscanf(msg, "%f", &value) == 1);
delete[] msg;
return ret;
}

return false;
}

bool readNextLineAsString(const char*& value) noexcept
{
CARLA_SAFE_ASSERT_RETURN(fIsReading, false);

if (const char* const msg = readline())
{
value = msg;
return true;
}
// -------------------------------------------------------------------

return false;
}
bool readNextLineAsBool(bool& value) noexcept;
bool readNextLineAsInt(int32_t& value) noexcept;
bool readNextLineAsUInt(uint32_t& value) noexcept;
bool readNextLineAsLong(int64_t& value) noexcept;
bool readNextLineAsULong(uint64_t& value) noexcept;
bool readNextLineAsFloat(float& value) noexcept;
bool readNextLineAsDouble(double& value) noexcept;
bool readNextLineAsString(const char*& value) noexcept;

// -------------------------------------------------------------------
// must be locked before calling

void writeMsg(const char* const msg) const noexcept
{
CARLA_SAFE_ASSERT_RETURN(fPipeSend != -1,);

// TESTING remove later
const bool wasLocked(! fWriteLock.tryLock());
CARLA_SAFE_ASSERT_RETURN(wasLocked, fWriteLock.unlock());

try {
ssize_t ignore = ::write(fPipeSend, msg, std::strlen(msg));
(void)ignore;
} CARLA_SAFE_EXCEPTION("CarlaPipeServer::writeMsg");
}

void writeMsg(const char* const msg, size_t size) const noexcept
{
CARLA_SAFE_ASSERT_RETURN(fPipeSend != -1,);

// TESTING remove later
const bool wasLocked(! fWriteLock.tryLock());
CARLA_SAFE_ASSERT_RETURN(wasLocked, fWriteLock.unlock());

try {
ssize_t ignore = ::write(fPipeSend, msg, size);
(void)ignore;
} CARLA_SAFE_EXCEPTION("CarlaPipeServer::writeMsg");
}

void writeAndFixMsg(const char* const msg) noexcept
{
CARLA_SAFE_ASSERT_RETURN(fPipeSend != -1,);

// TESTING remove later
const bool wasLocked(! fWriteLock.tryLock());
CARLA_SAFE_ASSERT_RETURN(wasLocked, fWriteLock.unlock());

const size_t size(msg != nullptr ? std::strlen(msg) : 0);

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';
}

try {
ssize_t ignore = ::write(fPipeSend, fixedMsg, size+1);
(void)ignore;
} CARLA_SAFE_EXCEPTION("CarlaPipeServer::writeAndFixMsg");
}
bool writeMsg(const char* const msg) const noexcept;
bool writeMsg(const char* const msg, std::size_t size) const noexcept;
bool writeAndFixMsg(const char* const msg) noexcept;

// -------------------------------------------------------------------

void waitChildClose() noexcept
{
if (! wait_child(fPid))
{
carla_stderr2("force killing misbehaved child %i (exit)", int(fPid));

if (kill(fPid, SIGKILL) == -1)
carla_stderr2("kill() failed: %s (exit)", std::strerror(errno));
else
wait_child(fPid);
}
}
private:
struct PrivateData;
PrivateData* const pData;

// -------------------------------------------------------------------

protected:
// common write lock
CarlaMutex fWriteLock;
friend class CarlaPipeClient;
friend class CarlaPipeServer;

// to possibly send errors somewhere
virtual void fail(const char* const error)
{
carla_stderr2(error);
}
// internal
const char* readline() noexcept;
// internal
bool writeMsgBuffer(const char* const msg, const std::size_t size) const noexcept;

// returns true if msg handled
virtual bool msgReceived(const char* const msg) noexcept = 0;
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPipeCommon)
};

// -------------------------------------------------------------------
// -----------------------------------------------------------------------

private:
int fPipeRecv; // the pipe end that is used for receiving messages from UI
int fPipeSend; // the pipe end that is used for sending messages to UI
bool fIsReading;
pid_t fPid;
class CarlaPipeServer : public CarlaPipeCommon
{
public:
CarlaPipeServer() noexcept;
~CarlaPipeServer() noexcept override;

char fTmpBuf[0xff+1];
CarlaString fTmpStr;
bool start(const char* const filename, const char* const arg1, const char* const arg2) noexcept;
void stop(const uint32_t timeOutMilliseconds) noexcept;
void close() noexcept;

// -------------------------------------------------------------------
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPipeServer)
};

const char* readline() noexcept
{
char ch;
char* ptr = fTmpBuf;
ssize_t ret;

fTmpStr.clear();

for (int i=0; i < 0xff; ++i)
{
try {
ret = ::read(fPipeRecv, &ch, 1);
}
catch (...) {
break;
}

if (ret == 1 && ch != '\n')
{
if (ch == '\r')
ch = '\n';

*ptr++ = ch;

if (i+1 == 0xff)
{
i = 0;
ptr = fTmpBuf;
fTmpStr += fTmpBuf;
}

continue;
}

if (fTmpStr.isNotEmpty() || ptr != fTmpBuf)
{
if (ptr != fTmpBuf)
{
*ptr = '\0';
fTmpStr += fTmpBuf;
}

try {
return fTmpStr.dup();
}
catch(...) {
return nullptr;
}
}

break;
}

return nullptr;
}
// -----------------------------------------------------------------------

// -------------------------------------------------------------------
class CarlaPipeClient : public CarlaPipeCommon
{
public:
CarlaPipeClient() noexcept;
~CarlaPipeClient() noexcept override;

static bool fork_exec(const char* const argv[6], int* const retp) noexcept
{
const pid_t ret = *retp = vfork();
bool init(char* argv[]) noexcept;
void close() noexcept;

switch (ret)
{
case 0: // child process
execvp(argv[0], const_cast<char* const*>(argv));
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPipeClient)
};

carla_stderr2("exec failed: %s", std::strerror(errno));
_exit(0); // this is not noexcept safe but doesn't matter anyway
return false;
// -----------------------------------------------------------------------

case -1: // error
carla_stderr2("fork() failed: %s", std::strerror(errno));
_exit(0); // this is not noexcept safe but doesn't matter anyway
return false;
}
class ScopedLocale {
public:
ScopedLocale() noexcept;
~ScopedLocale() noexcept;

return true;
}
private:
const char* const fLocale;

static bool wait_child(const pid_t pid) noexcept
{
if (pid <= 0)
{
carla_stderr2("Can't wait for pid %i", int(pid));
return false;
}

pid_t ret;

for (int i=0, maxTime=WAIT_ZOMBIE_TIMEOUT/WAIT_STEP; i < maxTime; ++i)
{
try {
ret = ::waitpid(pid, nullptr, WNOHANG);
} CARLA_SAFE_EXCEPTION_BREAK("wait_child");

if (ret != 0)
{
if (ret == pid)
return true;

if (ret == -1)
{
if (errno == ECHILD)
return true;

CarlaString error(std::strerror(errno));
carla_stderr2("waitpid(%i) failed: %s", int(pid), error.buffer());
return false;
}

carla_stderr2("we waited for child pid %i to exit but we got pid %i instead", int(pid), int(ret));
return false;
}

carla_msleep(WAIT_STEP); /* wait 100 ms */
}

carla_stderr2("we waited for child with pid %i to exit for %.1f seconds and we are giving up", int(pid), float(WAIT_ZOMBIE_TIMEOUT)/1000.0f);
return false;
}
CARLA_DECLARE_NON_COPY_CLASS(ScopedLocale)
CARLA_PREVENT_HEAP_ALLOCATION
};

// -----------------------------------------------------------------------


Loading…
Cancel
Save