VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_core / native / juce_posix_NamedPipe.cpp
blobdde81fab3aca77a2710a80d36bbd0c9f22b018c5
1 /*
2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
20 ==============================================================================
23 namespace juce
26 #if ! JUCE_WASM
28 class NamedPipe::Pimpl
30 public:
31 Pimpl (const String& pipePath, bool createPipe)
32 : pipeInName (pipePath + "_in"),
33 pipeOutName (pipePath + "_out"),
34 createdPipe (createPipe)
36 signal (SIGPIPE, signalHandler);
37 juce_siginterrupt (SIGPIPE, 1);
40 ~Pimpl()
42 pipeIn .close();
43 pipeOut.close();
45 if (createdPipe)
47 if (createdFifoIn) unlink (pipeInName.toUTF8());
48 if (createdFifoOut) unlink (pipeOutName.toUTF8());
52 bool connect (int timeOutMilliseconds)
54 return openPipe (true, getTimeoutEnd (timeOutMilliseconds)) != invalidPipe;
57 int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds)
59 auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds);
60 int bytesRead = 0;
62 while (bytesRead < maxBytesToRead)
64 const auto pipe = pipeIn.get();
66 auto bytesThisTime = maxBytesToRead - bytesRead;
67 auto numRead = (int) ::read (pipe, destBuffer, (size_t) bytesThisTime);
69 if (numRead <= 0)
71 const auto error = errno;
73 if (! (error == EWOULDBLOCK || error == EAGAIN) || stopReadOperation.load() || hasExpired (timeoutEnd))
74 return -1;
76 const int maxWaitingTime = 30;
77 waitForInput (pipe, timeoutEnd == 0 ? maxWaitingTime
78 : jmin (maxWaitingTime,
79 (int) (timeoutEnd - Time::getMillisecondCounter())));
80 continue;
83 bytesRead += numRead;
84 destBuffer += numRead;
87 return bytesRead;
90 int write (const char* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds)
92 auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds);
94 const auto pipe = openPipe (false, timeoutEnd);
96 if (pipe == invalidPipe)
97 return -1;
99 int bytesWritten = 0;
101 while (bytesWritten < numBytesToWrite && ! hasExpired (timeoutEnd))
103 auto bytesThisTime = numBytesToWrite - bytesWritten;
104 auto numWritten = (int) ::write (pipe, sourceBuffer, (size_t) bytesThisTime);
106 if (numWritten < 0)
108 const auto error = errno;
109 const int maxWaitingTime = 30;
111 if (error == EWOULDBLOCK || error == EAGAIN)
112 waitToWrite (pipe, timeoutEnd == 0 ? maxWaitingTime
113 : jmin (maxWaitingTime,
114 (int) (timeoutEnd - Time::getMillisecondCounter())));
115 else
116 return -1;
118 numWritten = 0;
121 bytesWritten += numWritten;
122 sourceBuffer += numWritten;
125 return bytesWritten;
128 static bool createFifo (const String& name, bool mustNotExist)
130 return mkfifo (name.toUTF8(), 0666) == 0 || ((! mustNotExist) && errno == EEXIST);
133 bool createFifos (bool mustNotExist)
135 createdFifoIn = createFifo (pipeInName, mustNotExist);
136 createdFifoOut = createFifo (pipeOutName, mustNotExist);
138 return createdFifoIn && createdFifoOut;
141 static constexpr auto invalidPipe = -1;
143 class PipeDescriptor
145 public:
146 template <typename Fn>
147 int get (Fn&& fn)
150 const ScopedReadLock l (mutex);
152 if (descriptor != invalidPipe)
153 return descriptor;
156 const ScopedWriteLock l (mutex);
157 return descriptor = fn();
160 void close()
163 const ScopedReadLock l (mutex);
165 if (descriptor == invalidPipe)
166 return;
169 const ScopedWriteLock l (mutex);
170 ::close (descriptor);
171 descriptor = invalidPipe;
174 int get()
176 const ScopedReadLock l (mutex);
177 return descriptor;
180 private:
181 ReadWriteLock mutex;
182 int descriptor = invalidPipe;
185 const String pipeInName, pipeOutName;
186 PipeDescriptor pipeIn, pipeOut;
187 bool createdFifoIn = false, createdFifoOut = false;
189 const bool createdPipe;
190 std::atomic<bool> stopReadOperation { false };
192 private:
193 static void signalHandler (int) {}
195 static uint32 getTimeoutEnd (int timeOutMilliseconds)
197 return timeOutMilliseconds >= 0 ? Time::getMillisecondCounter() + (uint32) timeOutMilliseconds : 0;
200 static bool hasExpired (uint32 timeoutEnd)
202 return timeoutEnd != 0 && Time::getMillisecondCounter() >= timeoutEnd;
205 int openPipe (const String& name, int flags, uint32 timeoutEnd)
207 for (;;)
209 auto p = ::open (name.toUTF8(), flags);
211 if (p != invalidPipe || hasExpired (timeoutEnd) || stopReadOperation.load())
212 return p;
214 Thread::sleep (2);
218 int openPipe (bool isInput, uint32 timeoutEnd)
220 auto& pipe = isInput ? pipeIn : pipeOut;
221 const auto flags = (isInput ? O_RDWR : O_WRONLY) | O_NONBLOCK;
223 const String& pipeName = isInput ? (createdPipe ? pipeInName : pipeOutName)
224 : (createdPipe ? pipeOutName : pipeInName);
226 return pipe.get ([this, &pipeName, &flags, &timeoutEnd]
228 return openPipe (pipeName, flags, timeoutEnd);
232 static void waitForInput (int handle, int timeoutMsecs) noexcept
234 pollfd pfd { handle, POLLIN, 0 };
235 poll (&pfd, 1, timeoutMsecs);
238 static void waitToWrite (int handle, int timeoutMsecs) noexcept
240 pollfd pfd { handle, POLLOUT, 0 };
241 poll (&pfd, 1, timeoutMsecs);
244 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
247 void NamedPipe::close()
250 const ScopedReadLock sl (lock);
252 if (pimpl != nullptr)
254 pimpl->stopReadOperation = true;
256 const char buffer[] { 0 };
257 const auto done = ::write (pimpl->pipeIn.get(), buffer, numElementsInArray (buffer));
258 ignoreUnused (done);
263 const ScopedWriteLock sl (lock);
264 pimpl.reset();
268 bool NamedPipe::openInternal (const String& pipeName, bool createPipe, bool mustNotExist)
270 #if JUCE_IOS
271 pimpl.reset (new Pimpl (File::getSpecialLocation (File::tempDirectory)
272 .getChildFile (File::createLegalFileName (pipeName)).getFullPathName(), createPipe));
273 #else
274 auto file = pipeName;
276 if (! File::isAbsolutePath (file))
277 file = "/tmp/" + File::createLegalFileName (file);
279 pimpl.reset (new Pimpl (file, createPipe));
280 #endif
282 if (createPipe && ! pimpl->createFifos (mustNotExist))
284 pimpl.reset();
285 return false;
288 if (! pimpl->connect (200))
290 pimpl.reset();
291 return false;
294 return true;
297 int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds)
299 ScopedReadLock sl (lock);
300 return pimpl != nullptr ? pimpl->read (static_cast<char*> (destBuffer), maxBytesToRead, timeOutMilliseconds) : -1;
303 int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds)
305 ScopedReadLock sl (lock);
306 return pimpl != nullptr ? pimpl->write (static_cast<const char*> (sourceBuffer), numBytesToWrite, timeOutMilliseconds) : -1;
309 #endif
311 } // namespace juce