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
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
20 ==============================================================================
28 class NamedPipe::Pimpl
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);
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
);
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
);
71 const auto error
= errno
;
73 if (! (error
== EWOULDBLOCK
|| error
== EAGAIN
) || stopReadOperation
.load() || hasExpired (timeoutEnd
))
76 const int maxWaitingTime
= 30;
77 waitForInput (pipe
, timeoutEnd
== 0 ? maxWaitingTime
78 : jmin (maxWaitingTime
,
79 (int) (timeoutEnd
- Time::getMillisecondCounter())));
84 destBuffer
+= numRead
;
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
)
101 while (bytesWritten
< numBytesToWrite
&& ! hasExpired (timeoutEnd
))
103 auto bytesThisTime
= numBytesToWrite
- bytesWritten
;
104 auto numWritten
= (int) ::write (pipe
, sourceBuffer
, (size_t) bytesThisTime
);
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())));
121 bytesWritten
+= numWritten
;
122 sourceBuffer
+= numWritten
;
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;
146 template <typename Fn
>
150 const ScopedReadLock
l (mutex
);
152 if (descriptor
!= invalidPipe
)
156 const ScopedWriteLock
l (mutex
);
157 return descriptor
= fn();
163 const ScopedReadLock
l (mutex
);
165 if (descriptor
== invalidPipe
)
169 const ScopedWriteLock
l (mutex
);
170 ::close (descriptor
);
171 descriptor
= invalidPipe
;
176 const ScopedReadLock
l (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 };
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
)
209 auto p
= ::open (name
.toUTF8(), flags
);
211 if (p
!= invalidPipe
|| hasExpired (timeoutEnd
) || stopReadOperation
.load())
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
));
263 const ScopedWriteLock
sl (lock
);
268 bool NamedPipe::openInternal (const String
& pipeName
, bool createPipe
, bool mustNotExist
)
271 pimpl
.reset (new Pimpl (File::getSpecialLocation (File::tempDirectory
)
272 .getChildFile (File::createLegalFileName (pipeName
)).getFullPathName(), createPipe
));
274 auto file
= pipeName
;
276 if (! File::isAbsolutePath (file
))
277 file
= "/tmp/" + File::createLegalFileName (file
);
279 pimpl
.reset (new Pimpl (file
, createPipe
));
282 if (createPipe
&& ! pimpl
->createFifos (mustNotExist
))
288 if (! pimpl
->connect (200))
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;