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.

227 lines
9.6KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. namespace juce
  18. {
  19. class InterprocessConnectionServer;
  20. class MemoryBlock;
  21. //==============================================================================
  22. /**
  23. Manages a simple two-way messaging connection to another process, using either
  24. a socket or a named pipe as the transport medium.
  25. To connect to a waiting socket or an open pipe, use the connectToSocket() or
  26. connectToPipe() methods. If this succeeds, messages can be sent to the other end,
  27. and incoming messages will result in a callback via the messageReceived()
  28. method.
  29. To open a pipe and wait for another client to connect to it, use the createPipe()
  30. method.
  31. To act as a socket server and create connections for one or more client, see the
  32. InterprocessConnectionServer class.
  33. IMPORTANT NOTE: Your derived Connection class *must* call `disconnect` in its destructor
  34. in order to cancel any pending messages before the class is destroyed.
  35. @see InterprocessConnectionServer, Socket, NamedPipe
  36. @tags{Events}
  37. */
  38. class JUCE_API InterprocessConnection
  39. {
  40. public:
  41. //==============================================================================
  42. /** Creates a connection.
  43. Connections are created manually, connecting them with the connectToSocket()
  44. or connectToPipe() methods, or they are created automatically by a InterprocessConnectionServer
  45. when a client wants to connect.
  46. @param callbacksOnMessageThread if true, callbacks to the connectionMade(),
  47. connectionLost() and messageReceived() methods will
  48. always be made using the message thread; if false,
  49. these will be called immediately on the connection's
  50. own thread.
  51. @param magicMessageHeaderNumber a magic number to use in the header to check the
  52. validity of the data blocks being sent and received. This
  53. can be any number, but the sender and receiver must obviously
  54. use matching values or they won't recognise each other.
  55. */
  56. InterprocessConnection (bool callbacksOnMessageThread = true,
  57. uint32 magicMessageHeaderNumber = 0xf2b49e2c);
  58. /** Destructor. */
  59. virtual ~InterprocessConnection();
  60. //==============================================================================
  61. /** Tries to connect this object to a socket.
  62. For this to work, the machine on the other end needs to have a InterprocessConnectionServer
  63. object waiting to receive client connections on this port number.
  64. @param hostName the host computer, either a network address or name
  65. @param portNumber the socket port number to try to connect to
  66. @param timeOutMillisecs how long to keep trying before giving up
  67. @returns true if the connection is established successfully
  68. @see Socket
  69. */
  70. bool connectToSocket (const String& hostName,
  71. int portNumber,
  72. int timeOutMillisecs);
  73. /** Tries to connect the object to an existing named pipe.
  74. For this to work, another process on the same computer must already have opened
  75. an InterprocessConnection object and used createPipe() to create a pipe for this
  76. to connect to.
  77. @param pipeName the name to use for the pipe - this should be unique to your app
  78. @param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing
  79. to the pipe, or -1 for an infinite timeout.
  80. @returns true if it connects successfully.
  81. @see createPipe, NamedPipe
  82. */
  83. bool connectToPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs);
  84. /** Tries to create a new pipe for other processes to connect to.
  85. This creates a pipe with the given name, so that other processes can use
  86. connectToPipe() to connect to the other end.
  87. @param pipeName the name to use for the pipe - this should be unique to your app
  88. @param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing
  89. to the pipe, or -1 for an infinite timeout
  90. @param mustNotExist if set to true, the method will fail if the pipe already exists
  91. @returns true if the pipe was created, or false if it fails (e.g. if another process is
  92. already using the pipe)
  93. */
  94. bool createPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs, bool mustNotExist = false);
  95. /** Whether the disconnect call should trigger callbacks. */
  96. enum class Notify { no, yes };
  97. /** Disconnects and closes any currently-open sockets or pipes.
  98. Derived classes *must* call this in their destructors in order to avoid undefined
  99. behaviour.
  100. @param timeoutMs the time in ms to wait before killing the thread by force
  101. @param notify whether or not to call `connectionLost`
  102. */
  103. void disconnect (int timeoutMs = -1, Notify notify = Notify::yes);
  104. /** True if a socket or pipe is currently active. */
  105. bool isConnected() const;
  106. /** Returns the socket that this connection is using (or nullptr if it uses a pipe). */
  107. StreamingSocket* getSocket() const noexcept { return socket.get(); }
  108. /** Returns the pipe that this connection is using (or nullptr if it uses a socket). */
  109. NamedPipe* getPipe() const noexcept { return pipe.get(); }
  110. /** Returns the name of the machine at the other end of this connection.
  111. This may return an empty string if the name is unknown.
  112. */
  113. String getConnectedHostName() const;
  114. //==============================================================================
  115. /** Tries to send a message to the other end of this connection.
  116. This will fail if it's not connected, or if there's some kind of write error. If
  117. it succeeds, the connection object at the other end will receive the message by
  118. a callback to its messageReceived() method.
  119. @see messageReceived
  120. */
  121. bool sendMessage (const MemoryBlock& message);
  122. //==============================================================================
  123. /** Called when the connection is first connected.
  124. If the connection was created with the callbacksOnMessageThread flag set, then
  125. this will be called on the message thread; otherwise it will be called on a server
  126. thread.
  127. */
  128. virtual void connectionMade() = 0;
  129. /** Called when the connection is broken.
  130. If the connection was created with the callbacksOnMessageThread flag set, then
  131. this will be called on the message thread; otherwise it will be called on a server
  132. thread.
  133. */
  134. virtual void connectionLost() = 0;
  135. /** Called when a message arrives.
  136. When the object at the other end of this connection sends us a message with sendMessage(),
  137. this callback is used to deliver it to us.
  138. If the connection was created with the callbacksOnMessageThread flag set, then
  139. this will be called on the message thread; otherwise it will be called on a server
  140. thread.
  141. @see sendMessage
  142. */
  143. virtual void messageReceived (const MemoryBlock& message) = 0;
  144. private:
  145. //==============================================================================
  146. ReadWriteLock pipeAndSocketLock;
  147. std::unique_ptr<StreamingSocket> socket;
  148. std::unique_ptr<NamedPipe> pipe;
  149. bool callbackConnectionState = false;
  150. const bool useMessageThread;
  151. const uint32 magicMessageHeader;
  152. int pipeReceiveMessageTimeout = -1;
  153. friend class InterprocessConnectionServer;
  154. void initialise();
  155. void initialiseWithSocket (std::unique_ptr<StreamingSocket>);
  156. void initialiseWithPipe (std::unique_ptr<NamedPipe>);
  157. void deletePipeAndSocket();
  158. void connectionMadeInt();
  159. void connectionLostInt();
  160. void deliverDataInt (const MemoryBlock&);
  161. bool readNextMessage();
  162. int readData (void*, int);
  163. struct ConnectionThread;
  164. std::unique_ptr<ConnectionThread> thread;
  165. std::atomic<bool> threadIsRunning { false };
  166. class SafeAction;
  167. std::shared_ptr<SafeAction> safeAction;
  168. void runThread();
  169. int writeData (void*, int);
  170. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnection)
  171. };
  172. } // namespace juce