diff --git a/extras/JuceDemo/Source/demos/InterprocessCommsDemo.cpp b/extras/JuceDemo/Source/demos/InterprocessCommsDemo.cpp index 2460620ef9..715ec07498 100644 --- a/extras/JuceDemo/Source/demos/InterprocessCommsDemo.cpp +++ b/extras/JuceDemo/Source/demos/InterprocessCommsDemo.cpp @@ -211,7 +211,7 @@ public: { ScopedPointer newConnection (new DemoInterprocessConnection (*this)); - openedOk = newConnection->createPipe (pipeName.getText()); + openedOk = newConnection->createPipe (pipeName.getText(), 2000); if (openedOk) { diff --git a/modules/juce_core/memory/juce_Memory.h b/modules/juce_core/memory/juce_Memory.h index 7c21a63301..1209c66fbb 100644 --- a/modules/juce_core/memory/juce_Memory.h +++ b/modules/juce_core/memory/juce_Memory.h @@ -67,6 +67,31 @@ inline int getAddressDifference (Type1* pointer1, Type2* pointer2) noexcept { r template inline Type* createCopyIfNotNull (Type* pointer) { return pointer != nullptr ? new Type (*pointer) : nullptr; } +//============================================================================== +#if JUCE_MAC || JUCE_IOS || DOXYGEN + + /** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII. + You should use the JUCE_AUTORELEASEPOOL macro to create a local auto-release pool on the stack. + */ + class JUCE_API ScopedAutoReleasePool + { + public: + ScopedAutoReleasePool(); + ~ScopedAutoReleasePool(); + + private: + void* pool; + + JUCE_DECLARE_NON_COPYABLE (ScopedAutoReleasePool); + }; + + /** A macro that can be used to easily declare a local ScopedAutoReleasePool object for RAII-based obj-C autoreleasing. */ + #define JUCE_AUTORELEASEPOOL const juce::ScopedAutoReleasePool JUCE_JOIN_MACRO (autoReleasePool_, __LINE__); + +#else + #define JUCE_AUTORELEASEPOOL +#endif + //============================================================================== /* In a Windows DLL build, we'll expose some malloc/free functions that live inside the DLL, and use these for allocating all the objects - that way all juce objects in the DLL and in the host will live in the same heap, diff --git a/modules/juce_core/native/juce_posix_NamedPipe.cpp b/modules/juce_core/native/juce_posix_NamedPipe.cpp index 603e0dcb35..36b4fa18cc 100644 --- a/modules/juce_core/native/juce_posix_NamedPipe.cpp +++ b/modules/juce_core/native/juce_posix_NamedPipe.cpp @@ -49,15 +49,28 @@ public: } } - int read (char* destBuffer, int maxBytesToRead) + static int openPipe (const String& name, int flags, const uint32 timeoutEnd) + { + for (;;) + { + const int p = ::open (name.toUTF8(), flags); + + if (p != -1 || hasExpired (timeoutEnd)) + return p; + + Thread::sleep (2); + } + } + + int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds) { int bytesRead = -1; blocked = true; + const uint32 timeoutEnd = getTimeoutEnd (timeOutMilliseconds); if (pipeIn == -1) { - pipeIn = ::open ((createdPipe ? pipeInName - : pipeOutName).toUTF8(), O_RDWR); + pipeIn = openPipe (createdPipe ? pipeInName : pipeOutName, O_RDWR | O_NONBLOCK, timeoutEnd); if (pipeIn == -1) { @@ -73,10 +86,16 @@ public: const int bytesThisTime = maxBytesToRead - bytesRead; const int numRead = (int) ::read (pipeIn, destBuffer, bytesThisTime); - if (numRead <= 0 || stopReadOperation) + if (numRead <= 0) { - bytesRead = -1; - break; + if (errno != EWOULDBLOCK || stopReadOperation || hasExpired (timeoutEnd)) + { + bytesRead = -1; + break; + } + + Thread::yield(); + continue; } bytesRead += numRead; @@ -90,21 +109,19 @@ public: int write (const char* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) { int bytesWritten = -1; + const uint32 timeoutEnd = getTimeoutEnd (timeOutMilliseconds); if (pipeOut == -1) { - pipeOut = ::open ((createdPipe ? pipeOutName - : pipeInName).toUTF8(), O_WRONLY); + pipeOut = openPipe (createdPipe ? pipeOutName : pipeInName, O_WRONLY, timeoutEnd); if (pipeOut == -1) return -1; } bytesWritten = 0; - const uint32 timeOutTime = Time::getMillisecondCounter() + timeOutMilliseconds; - while (bytesWritten < numBytesToWrite - && (timeOutMilliseconds < 0 || Time::getMillisecondCounter() < timeOutTime)) + while (bytesWritten < numBytesToWrite && ! hasExpired (timeoutEnd)) { const int bytesThisTime = numBytesToWrite - bytesWritten; const int numWritten = (int) ::write (pipeOut, sourceBuffer, bytesThisTime); @@ -137,6 +154,16 @@ public: private: static void signalHandler (int) {} + static uint32 getTimeoutEnd (const int timeOutMilliseconds) + { + return timeOutMilliseconds >= 0 ? Time::getMillisecondCounter() + timeOutMilliseconds : 0; + } + + static bool hasExpired (const uint32 timeoutEnd) + { + return timeoutEnd != 0 && Time::getMillisecondCounter() >= timeoutEnd; + } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl); }; @@ -193,9 +220,9 @@ bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) return true; } -int NamedPipe::read (void* destBuffer, int maxBytesToRead, int /*timeOutMilliseconds*/) +int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds) { - return pimpl != nullptr ? pimpl->read (static_cast (destBuffer), maxBytesToRead) : -1; + return pimpl != nullptr ? pimpl->read (static_cast (destBuffer), maxBytesToRead, timeOutMilliseconds) : -1; } int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) diff --git a/modules/juce_core/network/juce_NamedPipe.h b/modules/juce_core/network/juce_NamedPipe.h index 0864e9de51..dee2f64b6b 100644 --- a/modules/juce_core/network/juce_NamedPipe.h +++ b/modules/juce_core/network/juce_NamedPipe.h @@ -31,7 +31,7 @@ /** A cross-process pipe that can have data written to and read from it. - Two or more processes can use these for inter-process communication. + Two processes can use NamedPipe objects to exchange blocks of data. @see InterprocessConnection */ @@ -45,16 +45,13 @@ public: /** Destructor. */ ~NamedPipe(); - //============================================================================== /** Tries to open a pipe that already exists. - Returns true if it succeeds. */ bool openExisting (const String& pipeName); /** Tries to create a new pipe. - Returns true if it succeeds. */ bool createNewPipe (const String& pipeName); @@ -81,18 +78,14 @@ public: If timeOutMilliseconds is less than zero, it will wait indefinitely, otherwise this is a maximum timeout for reading from the pipe. */ - int read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds = 5000); + int read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds); /** Writes some data to the pipe. - - If the operation fails, it returns -1, otherwise, it will return the number of - bytes written. + @returns the number of bytes written, or -1 on failure. */ - int write (const void* sourceBuffer, int numBytesToWrite, - int timeOutMilliseconds = 2000); + int write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds); - /** If any threads are currently blocked on a read operation, this tells them to abort. - */ + /** If any threads are currently blocked on a read operation, this tells them to abort. */ void cancelPendingReads(); private: diff --git a/modules/juce_core/system/juce_StandardHeader.h b/modules/juce_core/system/juce_StandardHeader.h index 88bf2021e2..c873cb7a1f 100644 --- a/modules/juce_core/system/juce_StandardHeader.h +++ b/modules/juce_core/system/juce_StandardHeader.h @@ -43,24 +43,15 @@ See also SystemStats::getJUCEVersion() for a string version. */ -#define JUCE_VERSION ((JUCE_MAJOR_VERSION << 16) + (JUCE_MINOR_VERSION << 8) + JUCE_BUILDNUMBER) +#define JUCE_VERSION ((JUCE_MAJOR_VERSION << 16) + (JUCE_MINOR_VERSION << 8) + JUCE_BUILDNUMBER) //============================================================================== #include "juce_TargetPlatform.h" // (sets up the various JUCE_WINDOWS, JUCE_MAC, etc flags) - -//============================================================================== -#ifndef DOXYGEN - // These are old macros that are now deprecated: you should just use the juce namespace directly. - #define JUCE_NAMESPACE juce - #define BEGIN_JUCE_NAMESPACE namespace juce { - #define END_JUCE_NAMESPACE } -#endif - -//============================================================================== #include "juce_PlatformDefs.h" -// Now we'll include any OS headers we need.. (at this point we are outside the Juce namespace). +//============================================================================== +// Now we'll include some common OS headers.. #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4514 4245 4100) @@ -112,6 +103,12 @@ #include #endif +// undef symbols that are sometimes set by misguided 3rd-party headers.. +#undef check +#undef TYPE_BOOL +#undef max +#undef min + //============================================================================== // DLL building settings on Windows #if JUCE_MSVC @@ -131,70 +128,40 @@ #endif #endif +//============================================================================== #ifndef JUCE_API - /** This macro is added to all juce public class declarations. */ - #define JUCE_API + #define JUCE_API /**< This macro is added to all juce public class declarations. */ #endif /** This macro is added to all juce public function declarations. */ #define JUCE_PUBLIC_FUNCTION JUCE_API JUCE_CALLTYPE -/** This turns on some non-essential bits of code that should prevent old code from compiling - in cases where method signatures have changed, etc. -*/ #if (! defined (JUCE_CATCH_DEPRECATED_CODE_MISUSE)) && JUCE_DEBUG && ! DOXYGEN + /** This turns on some non-essential bits of code that should prevent old code from compiling + in cases where method signatures have changed, etc. + */ #define JUCE_CATCH_DEPRECATED_CODE_MISUSE 1 #endif -//============================================================================== -// Now include some basics that are needed by most of the Juce classes... -BEGIN_JUCE_NAMESPACE - -extern JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger(); - -#if JUCE_LOG_ASSERTIONS - extern JUCE_API void logAssertion (const char* filename, int lineNum) noexcept; +#ifndef DOXYGEN + #define JUCE_NAMESPACE juce // This old macro is deprecated: you should just use the juce namespace directly. #endif -#undef max -#undef min - -#include "../memory/juce_Memory.h" -#include "../maths/juce_MathsFunctions.h" -#include "../memory/juce_ByteOrder.h" -#include "../logging/juce_Logger.h" -#include "../memory/juce_LeakedObjectDetector.h" - -// unbelievably, some system headers actually use macros to define these symbols: -#undef check -#undef TYPE_BOOL - //============================================================================== -#if JUCE_MAC || JUCE_IOS || DOXYGEN +// Now include some common headers... +namespace juce +{ + extern JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger(); - /** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII. - You should use the JUCE_AUTORELEASEPOOL macro to create a local auto-release pool on the stack. - */ - class JUCE_API ScopedAutoReleasePool - { - public: - ScopedAutoReleasePool(); - ~ScopedAutoReleasePool(); - - private: - void* pool; - - JUCE_DECLARE_NON_COPYABLE (ScopedAutoReleasePool); - }; - - /** A macro that can be used to easily declare a local ScopedAutoReleasePool object for RAII-based obj-C autoreleasing. */ - #define JUCE_AUTORELEASEPOOL const juce::ScopedAutoReleasePool JUCE_JOIN_MACRO (autoReleasePool_, __LINE__); - -#else - #define JUCE_AUTORELEASEPOOL -#endif - -END_JUCE_NAMESPACE + #if JUCE_LOG_ASSERTIONS + extern JUCE_API void logAssertion (const char* file, int line) noexcept; + #endif + #include "../memory/juce_Memory.h" + #include "../maths/juce_MathsFunctions.h" + #include "../memory/juce_ByteOrder.h" + #include "../logging/juce_Logger.h" + #include "../memory/juce_LeakedObjectDetector.h" +} #endif // __JUCE_STANDARDHEADER_JUCEHEADER__ diff --git a/modules/juce_events/interprocess/juce_InterprocessConnection.cpp b/modules/juce_events/interprocess/juce_InterprocessConnection.cpp index fb8c9c31ed..5ed60090ff 100644 --- a/modules/juce_events/interprocess/juce_InterprocessConnection.cpp +++ b/modules/juce_events/interprocess/juce_InterprocessConnection.cpp @@ -64,8 +64,7 @@ bool InterprocessConnection::connectToSocket (const String& hostName, } } -bool InterprocessConnection::connectToPipe (const String& pipeName, - const int pipeReceiveMessageTimeoutMs) +bool InterprocessConnection::connectToPipe (const String& pipeName, const int timeoutMs) { disconnect(); @@ -74,7 +73,7 @@ bool InterprocessConnection::connectToPipe (const String& pipeName, if (newPipe->openExisting (pipeName)) { const ScopedLock sl (pipeAndSocketLock); - pipeReceiveMessageTimeout = pipeReceiveMessageTimeoutMs; + pipeReceiveMessageTimeout = timeoutMs; initialiseWithPipe (newPipe.release()); return true; } @@ -82,8 +81,7 @@ bool InterprocessConnection::connectToPipe (const String& pipeName, return false; } -bool InterprocessConnection::createPipe (const String& pipeName, - const int pipeReceiveMessageTimeoutMs) +bool InterprocessConnection::createPipe (const String& pipeName, const int timeoutMs) { disconnect(); @@ -92,7 +90,7 @@ bool InterprocessConnection::createPipe (const String& pipeName, if (newPipe->createNewPipe (pipeName)) { const ScopedLock sl (pipeAndSocketLock); - pipeReceiveMessageTimeout = pipeReceiveMessageTimeoutMs; + pipeReceiveMessageTimeout = timeoutMs; initialiseWithPipe (newPipe.release()); return true; } @@ -163,7 +161,7 @@ bool InterprocessConnection::sendMessage (const MemoryBlock& message) if (socket != nullptr) bytesWritten = socket->write (messageData.getData(), (int) messageData.getSize()); else if (pipe != nullptr) - bytesWritten = pipe->write (messageData.getData(), (int) messageData.getSize()); + bytesWritten = pipe->write (messageData.getData(), (int) messageData.getSize(), pipeReceiveMessageTimeout); return bytesWritten == (int) messageData.getSize(); } @@ -171,7 +169,7 @@ bool InterprocessConnection::sendMessage (const MemoryBlock& message) //============================================================================== void InterprocessConnection::initialiseWithSocket (StreamingSocket* const socket_) { - jassert (socket == 0); + jassert (socket == nullptr); socket = socket_; connectionMadeInt(); startThread(); @@ -179,7 +177,7 @@ void InterprocessConnection::initialiseWithSocket (StreamingSocket* const socket void InterprocessConnection::initialiseWithPipe (NamedPipe* const pipe_) { - jassert (pipe == 0); + jassert (pipe == nullptr); pipe = pipe_; connectionMadeInt(); startThread(); @@ -206,6 +204,8 @@ struct ConnectionStateMessage : public MessageManager::MessageBase WeakReference owner; bool connectionMade; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionStateMessage); }; void InterprocessConnection::connectionMadeInt() @@ -264,18 +264,16 @@ void InterprocessConnection::deliverDataInt (const MemoryBlock& data) //============================================================================== bool InterprocessConnection::readNextMessageInt() { - const int maximumMessageSize = 1024 * 1024 * 10; // sanity check - uint32 messageHeader[2]; const int bytes = socket != nullptr ? socket->read (messageHeader, sizeof (messageHeader), true) - : pipe ->read (messageHeader, sizeof (messageHeader), pipeReceiveMessageTimeout); + : pipe ->read (messageHeader, sizeof (messageHeader), -1); if (bytes == sizeof (messageHeader) && ByteOrder::swapIfBigEndian (messageHeader[0]) == magicMessageHeader) { int bytesInMessage = (int) ByteOrder::swapIfBigEndian (messageHeader[1]); - if (bytesInMessage > 0 && bytesInMessage < maximumMessageSize) + if (bytesInMessage > 0) { MemoryBlock messageData ((size_t) bytesInMessage, true); int bytesRead = 0; @@ -286,8 +284,10 @@ bool InterprocessConnection::readNextMessageInt() return false; const int numThisTime = jmin (bytesInMessage, 65536); - const int bytesIn = socket != nullptr ? socket->read (static_cast (messageData.getData()) + bytesRead, numThisTime, true) - : pipe ->read (static_cast (messageData.getData()) + bytesRead, numThisTime, pipeReceiveMessageTimeout); + void* const data = addBytesToPointer (messageData.getData(), bytesRead); + + const int bytesIn = socket != nullptr ? socket->read (data, numThisTime, true) + : pipe ->read (data, numThisTime, -1); if (bytesIn <= 0) break; @@ -339,7 +339,7 @@ void InterprocessConnection::run() } else { - Thread::sleep (2); + Thread::sleep (1); } } else if (pipe != nullptr) diff --git a/modules/juce_events/interprocess/juce_InterprocessConnection.h b/modules/juce_events/interprocess/juce_InterprocessConnection.h index f2b6125446..2acd7ca715 100644 --- a/modules/juce_events/interprocess/juce_InterprocessConnection.h +++ b/modules/juce_events/interprocess/juce_InterprocessConnection.h @@ -96,25 +96,26 @@ public: an InterprocessConnection object and used createPipe() to create a pipe for this to connect to. - You can optionally specify a timeout length to be passed to the NamedPipe::read() method. - + @param pipeName the name to use for the pipe - this should be unique to your app + @param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing + to the pipe, or -1 for an infinite timeout. @returns true if it connects successfully. @see createPipe, NamedPipe */ - bool connectToPipe (const String& pipeName, - int pipeReceiveMessageTimeoutMs = -1); + bool connectToPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs); /** Tries to create a new pipe for other processes to connect to. This creates a pipe with the given name, so that other processes can use connectToPipe() to connect to the other end. - You can optionally specify a timeout length to be passed to the NamedPipe::read() method. - - If another process is already using this pipe, this will fail and return false. + @param pipeName the name to use for the pipe - this should be unique to your app + @param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing + to the pipe, or -1 for an infinite timeout. + @returns true if the pipe was created, or false if it fails (e.g. if another process is + already using using the pipe). */ - bool createPipe (const String& pipeName, - int pipeReceiveMessageTimeoutMs = -1); + bool createPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs); /** Disconnects and closes any currently-open sockets or pipes. */ void disconnect(); @@ -122,16 +123,14 @@ public: /** True if a socket or pipe is currently active. */ bool isConnected() const; - /** Returns the socket that this connection is using (or null if it uses a pipe). */ + /** Returns the socket that this connection is using (or nullptr if it uses a pipe). */ StreamingSocket* getSocket() const noexcept { return socket; } - /** Returns the pipe that this connection is using (or null if it uses a socket). */ + /** Returns the pipe that this connection is using (or nullptr if it uses a socket). */ NamedPipe* getPipe() const noexcept { return pipe; } /** Returns the name of the machine at the other end of this connection. - - This will return an empty string if the other machine isn't known for - some reason. + This may return an empty string if the name is unknown. */ String getConnectedHostName() const;