Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

226 lines
5.6KB

  1. // SPDX-FileCopyrightText: 2011-2025 Filipe Coelho <falktx@falktx.com>
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #ifndef CARLA_LOG_THREAD_HPP_INCLUDED
  4. #define CARLA_LOG_THREAD_HPP_INCLUDED
  5. #include "CarlaBackend.h"
  6. #include "CarlaThread.hpp"
  7. #include <fcntl.h>
  8. #ifdef CARLA_OS_WIN
  9. # include <io.h>
  10. # define close _close
  11. # define dup _dup
  12. # define dup2 _dup2
  13. #endif
  14. using CARLA_BACKEND_NAMESPACE::EngineCallbackFunc;
  15. // -----------------------------------------------------------------------
  16. // Log thread
  17. class CarlaLogThread : private CarlaThread
  18. {
  19. public:
  20. CarlaLogThread()
  21. : CarlaThread("CarlaLogThread"),
  22. fStdOut(-1),
  23. fStdErr(-1),
  24. fCallback(nullptr),
  25. fCallbackPtr(nullptr) {}
  26. ~CarlaLogThread()
  27. {
  28. stop();
  29. }
  30. void init()
  31. {
  32. std::fflush(stdout);
  33. std::fflush(stderr);
  34. #ifdef CARLA_OS_WIN
  35. // TODO: use process id instead
  36. const int randint = std::rand();
  37. char strBuf[0xff+1];
  38. strBuf[0xff] = '\0';
  39. std::snprintf(strBuf, 0xff, "\\\\.\\pipe\\carlalogthread-%i", randint);
  40. fPipe[0] = CreateNamedPipeA(strBuf, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_NOWAIT, 2, 4096, 4096, 0, nullptr);
  41. fPipe[1] = CreateFileA(strBuf, GENERIC_WRITE, 0x0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
  42. CARLA_SAFE_ASSERT_RETURN(fPipe[0] != INVALID_HANDLE_VALUE,);
  43. CARLA_SAFE_ASSERT_RETURN(fPipe[1] != INVALID_HANDLE_VALUE,);
  44. const int pipe1 = _open_osfhandle((INT_PTR)fPipe[1], _O_WRONLY | _O_BINARY);
  45. const int stdout_fileno = _fileno(stdout);
  46. const int stderr_fileno = _fileno(stderr);
  47. #else
  48. CARLA_SAFE_ASSERT_RETURN(pipe(fPipe) == 0,);
  49. if (fcntl(fPipe[0], F_SETFL, O_NONBLOCK) != 0)
  50. {
  51. close(fPipe[0]);
  52. close(fPipe[1]);
  53. return;
  54. }
  55. const int pipe1 = fPipe[1];
  56. const int stdout_fileno = STDOUT_FILENO;
  57. const int stderr_fileno = STDERR_FILENO;
  58. #endif
  59. fStdOut = dup(stdout_fileno);
  60. fStdErr = dup(stderr_fileno);
  61. dup2(pipe1, stdout_fileno);
  62. dup2(pipe1, stderr_fileno);
  63. startThread();
  64. }
  65. void stop()
  66. {
  67. if (fStdOut == -1)
  68. return;
  69. stopThread(5000);
  70. std::fflush(stdout);
  71. std::fflush(stderr);
  72. #ifdef CARLA_OS_WIN
  73. CloseHandle(fPipe[0]);
  74. CloseHandle(fPipe[1]);
  75. const int stdout_fileno = _fileno(stdout);
  76. const int stderr_fileno = _fileno(stderr);
  77. #else
  78. close(fPipe[0]);
  79. close(fPipe[1]);
  80. const int stdout_fileno = STDOUT_FILENO;
  81. const int stderr_fileno = STDERR_FILENO;
  82. #endif
  83. dup2(fStdOut, stdout_fileno);
  84. dup2(fStdErr, stderr_fileno);
  85. close(fStdOut);
  86. close(fStdErr);
  87. fStdOut = -1;
  88. fStdErr = -1;
  89. }
  90. void setCallback(EngineCallbackFunc callback, void* callbackPtr)
  91. {
  92. CARLA_SAFE_ASSERT_RETURN(callback != nullptr,);
  93. fCallback = callback;
  94. fCallbackPtr = callbackPtr;
  95. }
  96. protected:
  97. void run()
  98. {
  99. CARLA_SAFE_ASSERT_RETURN(fCallback != nullptr,);
  100. size_t k, bufTempPos;
  101. ssize_t r, lastRead;
  102. char bufTemp[1024+1];
  103. char bufRead[1024+1];
  104. char bufSend[2048+1];
  105. bufTemp[0] = '\0';
  106. bufTempPos = 0;
  107. while (! shouldThreadExit())
  108. {
  109. bufRead[0] = '\0';
  110. while ((r = read(fPipe[0], bufRead, 1024)) > 0)
  111. {
  112. CARLA_SAFE_ASSERT_CONTINUE(r <= 1024);
  113. bufRead[r] = '\0';
  114. lastRead = 0;
  115. for (ssize_t i=0; i<r; ++i)
  116. {
  117. CARLA_SAFE_ASSERT_BREAK(bufRead[i] != '\0');
  118. if (bufRead[i] != '\n')
  119. continue;
  120. k = static_cast<size_t>(i-lastRead);
  121. if (bufTempPos != 0)
  122. {
  123. std::memcpy(bufSend, bufTemp, bufTempPos);
  124. std::memcpy(bufSend+bufTempPos, bufRead+lastRead, k);
  125. k += bufTempPos;
  126. }
  127. else
  128. {
  129. std::memcpy(bufSend, bufRead+lastRead, k);
  130. }
  131. lastRead = i+1;
  132. bufSend[k] = '\0';
  133. bufTemp[0] = '\0';
  134. bufTempPos = 0;
  135. fCallback(fCallbackPtr, CARLA_BACKEND_NAMESPACE::ENGINE_CALLBACK_DEBUG, 0, 0, 0, 0, 0.0f, bufSend);
  136. }
  137. if (lastRead > 0 && lastRead != r)
  138. {
  139. k = static_cast<size_t>(r-lastRead);
  140. std::memcpy(bufTemp, bufRead+lastRead, k);
  141. bufTemp[k] = '\0';
  142. bufTempPos = k;
  143. }
  144. }
  145. d_msleep(20);
  146. }
  147. }
  148. private:
  149. #ifdef CARLA_OS_WIN
  150. HANDLE fPipe[2];
  151. #else
  152. int fPipe[2];
  153. #endif
  154. int fStdOut;
  155. int fStdErr;
  156. EngineCallbackFunc fCallback;
  157. void* fCallbackPtr;
  158. #ifdef CARLA_OS_WIN
  159. ssize_t read(const HANDLE pipeh, void* const buf, DWORD numBytes)
  160. {
  161. if (ReadFile(pipeh, buf, numBytes, &numBytes, nullptr) != FALSE)
  162. return numBytes;
  163. return -1;
  164. }
  165. #endif
  166. //CARLA_PREVENT_HEAP_ALLOCATION
  167. CARLA_DECLARE_NON_COPYABLE(CarlaLogThread)
  168. };
  169. #ifdef CARLA_OS_WIN
  170. # undef close
  171. # undef dup
  172. # undef dup2
  173. #endif
  174. // -----------------------------------------------------------------------
  175. #endif // CARLA_LOG_THREAD_HPP_INCLUDED