Fix last commit
[carla.git] / source / utils / CarlaLogThread.hpp
blob9e01908d1c75df5d9b8c0ffd201c81e996330f29
1 /*
2 * Carla Log Thread
3 * Copyright (C) 2013-2023 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #ifndef CARLA_LOG_THREAD_HPP_INCLUDED
19 #define CARLA_LOG_THREAD_HPP_INCLUDED
21 #include "CarlaBackend.h"
22 #include "CarlaString.hpp"
23 #include "CarlaThread.hpp"
25 #include <fcntl.h>
27 #ifdef CARLA_OS_WIN
28 # include <io.h>
29 # define close _close
30 # define dup _dup
31 # define dup2 _dup2
32 #endif
34 using CARLA_BACKEND_NAMESPACE::EngineCallbackFunc;
36 // -----------------------------------------------------------------------
37 // Log thread
39 class CarlaLogThread : private CarlaThread
41 public:
42 CarlaLogThread()
43 : CarlaThread("CarlaLogThread"),
44 fStdOut(-1),
45 fStdErr(-1),
46 fCallback(nullptr),
47 fCallbackPtr(nullptr) {}
49 ~CarlaLogThread()
51 stop();
54 void init()
56 std::fflush(stdout);
57 std::fflush(stderr);
59 #ifdef CARLA_OS_WIN
60 // TODO: use process id instead
61 const int randint = std::rand();
63 char strBuf[0xff+1];
64 strBuf[0xff] = '\0';
65 std::snprintf(strBuf, 0xff, "\\\\.\\pipe\\carlalogthread-%i", randint);
67 fPipe[0] = CreateNamedPipeA(strBuf, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_NOWAIT, 2, 4096, 4096, 0, nullptr);
68 fPipe[1] = CreateFileA(strBuf, GENERIC_WRITE, 0x0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
70 CARLA_SAFE_ASSERT_RETURN(fPipe[0] != INVALID_HANDLE_VALUE,);
71 CARLA_SAFE_ASSERT_RETURN(fPipe[1] != INVALID_HANDLE_VALUE,);
73 const int pipe1 = _open_osfhandle((INT_PTR)fPipe[1], _O_WRONLY | _O_BINARY);
74 const int stdout_fileno = _fileno(stdout);
75 const int stderr_fileno = _fileno(stderr);
76 #else
77 CARLA_SAFE_ASSERT_RETURN(pipe(fPipe) == 0,);
79 if (fcntl(fPipe[0], F_SETFL, O_NONBLOCK) != 0)
81 close(fPipe[0]);
82 close(fPipe[1]);
83 return;
86 const int pipe1 = fPipe[1];
87 const int stdout_fileno = STDOUT_FILENO;
88 const int stderr_fileno = STDERR_FILENO;
89 #endif
91 fStdOut = dup(stdout_fileno);
92 fStdErr = dup(stderr_fileno);
94 dup2(pipe1, stdout_fileno);
95 dup2(pipe1, stderr_fileno);
97 startThread();
100 void stop()
102 if (fStdOut == -1)
103 return;
105 stopThread(5000);
107 std::fflush(stdout);
108 std::fflush(stderr);
110 #ifdef CARLA_OS_WIN
111 CloseHandle(fPipe[0]);
112 CloseHandle(fPipe[1]);
114 const int stdout_fileno = _fileno(stdout);
115 const int stderr_fileno = _fileno(stderr);
116 #else
117 close(fPipe[0]);
118 close(fPipe[1]);
120 const int stdout_fileno = STDOUT_FILENO;
121 const int stderr_fileno = STDERR_FILENO;
122 #endif
124 dup2(fStdOut, stdout_fileno);
125 dup2(fStdErr, stderr_fileno);
126 close(fStdOut);
127 close(fStdErr);
128 fStdOut = -1;
129 fStdErr = -1;
132 void setCallback(EngineCallbackFunc callback, void* callbackPtr)
134 CARLA_SAFE_ASSERT_RETURN(callback != nullptr,);
136 fCallback = callback;
137 fCallbackPtr = callbackPtr;
140 protected:
141 void run()
143 CARLA_SAFE_ASSERT_RETURN(fCallback != nullptr,);
145 size_t k, bufTempPos;
146 ssize_t r, lastRead;
147 char bufTemp[1024+1];
148 char bufRead[1024+1];
149 char bufSend[2048+1];
151 bufTemp[0] = '\0';
152 bufTempPos = 0;
154 while (! shouldThreadExit())
156 bufRead[0] = '\0';
158 while ((r = read(fPipe[0], bufRead, 1024)) > 0)
160 CARLA_SAFE_ASSERT_CONTINUE(r <= 1024);
162 bufRead[r] = '\0';
163 lastRead = 0;
165 for (ssize_t i=0; i<r; ++i)
167 CARLA_SAFE_ASSERT_BREAK(bufRead[i] != '\0');
169 if (bufRead[i] != '\n')
170 continue;
172 k = static_cast<size_t>(i-lastRead);
174 if (bufTempPos != 0)
176 std::memcpy(bufSend, bufTemp, bufTempPos);
177 std::memcpy(bufSend+bufTempPos, bufRead+lastRead, k);
178 k += bufTempPos;
180 else
182 std::memcpy(bufSend, bufRead+lastRead, k);
185 lastRead = i+1;
186 bufSend[k] = '\0';
187 bufTemp[0] = '\0';
188 bufTempPos = 0;
190 fCallback(fCallbackPtr, CARLA_BACKEND_NAMESPACE::ENGINE_CALLBACK_DEBUG, 0, 0, 0, 0, 0.0f, bufSend);
193 if (lastRead > 0 && lastRead != r)
195 k = static_cast<size_t>(r-lastRead);
196 std::memcpy(bufTemp, bufRead+lastRead, k);
197 bufTemp[k] = '\0';
198 bufTempPos = k;
202 carla_msleep(20);
206 private:
207 #ifdef CARLA_OS_WIN
208 HANDLE fPipe[2];
209 #else
210 int fPipe[2];
211 #endif
213 int fStdOut;
214 int fStdErr;
216 EngineCallbackFunc fCallback;
217 void* fCallbackPtr;
219 #ifdef CARLA_OS_WIN
220 ssize_t read(const HANDLE pipeh, void* const buf, DWORD numBytes)
222 if (ReadFile(pipeh, buf, numBytes, &numBytes, nullptr) != FALSE)
223 return numBytes;
224 return -1;
226 #endif
228 //CARLA_PREVENT_HEAP_ALLOCATION
229 CARLA_DECLARE_NON_COPYABLE(CarlaLogThread)
232 #ifdef CARLA_OS_WIN
233 # undef close
234 # undef dup
235 # undef dup2
236 #endif
238 // -----------------------------------------------------------------------
240 #endif // CARLA_LOG_THREAD_HPP_INCLUDED