The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

309 lines
8.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../jucer_Headers.h"
  18. #include "../Utility/jucer_PresetIDs.h"
  19. #include "../Utility/jucer_FileHelpers.h"
  20. #include "../Application/jucer_AppearanceSettings.h"
  21. #include "../Application/jucer_Application.h"
  22. #include "../Utility/jucer_CodeHelpers.h"
  23. #include "projucer_CompileEngineDLL.h"
  24. #include "projucer_MessageIDs.h"
  25. #include "projucer_CppHelpers.h"
  26. #include "projucer_SourceCodeRange.h"
  27. #include "projucer_ClassDatabase.h"
  28. #include "projucer_DiagnosticMessage.h"
  29. #include "projucer_ProjectBuildInfo.h"
  30. #include "projucer_ClientServerMessages.h"
  31. #if JUCE_LINUX
  32. #include <sys/types.h>
  33. #include <unistd.h>
  34. #endif
  35. #ifndef RUN_CLANG_IN_CHILD_PROCESS
  36. #error
  37. #endif
  38. #if RUN_CLANG_IN_CHILD_PROCESS
  39. static bool parentProcessHasExited();
  40. #endif
  41. #if JUCE_WINDOWS
  42. static void setParentProcessID (int);
  43. static int getCurrentProcessID();
  44. #endif
  45. //==============================================================================
  46. /** Detects whether this process has hung, and kills it if so. */
  47. struct ZombiePatrol : private Thread,
  48. private AsyncUpdater,
  49. private Timer
  50. {
  51. ZombiePatrol (MessageHandler& mh)
  52. : Thread ("Ping"), owner (mh)
  53. {
  54. startThread (2);
  55. startTimer (1000);
  56. }
  57. ~ZombiePatrol()
  58. {
  59. stopThread (1000);
  60. }
  61. private:
  62. MessageHandler& owner;
  63. int failedPings = 0;
  64. void run() override
  65. {
  66. while (! threadShouldExit())
  67. {
  68. #if RUN_CLANG_IN_CHILD_PROCESS
  69. if (parentProcessHasExited())
  70. {
  71. killProcess();
  72. break;
  73. }
  74. #endif
  75. wait (1000);
  76. }
  77. }
  78. void handleAsyncUpdate() override
  79. {
  80. DBG ("Server: quitting");
  81. stopTimer();
  82. ProjucerApplication::getApp().systemRequestedQuit();
  83. }
  84. void timerCallback() override
  85. {
  86. if (! MessageTypes::sendPing (owner))
  87. {
  88. if (++failedPings == 10)
  89. {
  90. killProcess();
  91. return;
  92. }
  93. }
  94. else
  95. {
  96. failedPings = 0;
  97. }
  98. }
  99. void killProcess()
  100. {
  101. triggerAsyncUpdate(); // give the messagequeue a chance to do things cleanly.
  102. static UnstoppableKillerThread* k = new UnstoppableKillerThread(); // (allowed to leak, but static so only one is created)
  103. ignoreUnused (k);
  104. }
  105. struct UnstoppableKillerThread : public Thread
  106. {
  107. UnstoppableKillerThread() : Thread ("Killer") { startThread(); }
  108. void run() override
  109. {
  110. wait (15000);
  111. if (! threadShouldExit())
  112. Process::terminate();
  113. }
  114. };
  115. };
  116. //==============================================================================
  117. class ServerIPC : public InterprocessConnection,
  118. public MessageHandler
  119. {
  120. public:
  121. ServerIPC (const StringArray& info)
  122. : InterprocessConnection (true), liveCodeBuilder (nullptr)
  123. {
  124. if (! createPipe (info[0], -1))
  125. {
  126. Logger::writeToLog ("*** Couldn't create pipe!");
  127. ProjucerApplication::getApp().systemRequestedQuit();
  128. return;
  129. }
  130. if (dll.isLoaded())
  131. liveCodeBuilder = dll.projucer_createBuilder (sendMessageCallback, this, info[1].toRawUTF8(), info[2].toRawUTF8());
  132. #if JUCE_WINDOWS
  133. setParentProcessID (info[3].getHexValue32());
  134. #endif
  135. zombieKiller = new ZombiePatrol (*this);
  136. }
  137. ~ServerIPC()
  138. {
  139. zombieKiller = nullptr;
  140. if (dll.isLoaded())
  141. dll.projucer_deleteBuilder (liveCodeBuilder);
  142. dll.shutdown();
  143. DBG ("Server: finished closing down");
  144. }
  145. void connectionMade() override
  146. {
  147. DBG ("Server: client connected");
  148. }
  149. void connectionLost() override
  150. {
  151. Logger::writeToLog ("Server: client lost");
  152. JUCEApplication::quit();
  153. }
  154. void sendQuitMessageToIDE()
  155. {
  156. MessageTypes::sendShouldCloseIDE (*this);
  157. }
  158. bool sendMessage (const ValueTree& m) override
  159. {
  160. return InterprocessConnection::sendMessage (MessageHandler::convertMessage (m));
  161. }
  162. void messageReceived (const MemoryBlock& message) override
  163. {
  164. jassert (dll.isLoaded());
  165. dll.projucer_sendMessage (liveCodeBuilder, message.getData(), message.getSize());
  166. }
  167. static bool sendMessageCallback (void* userInfo, const void* data, size_t dataSize)
  168. {
  169. return static_cast<InterprocessConnection*> (static_cast<ServerIPC*> (userInfo))
  170. ->sendMessage (MemoryBlock (data, dataSize));
  171. }
  172. CompileEngineDLL dll;
  173. LiveCodeBuilder liveCodeBuilder;
  174. ScopedPointer<ZombiePatrol> zombieKiller;
  175. };
  176. //==============================================================================
  177. const char* commandPrefix = "--server:";
  178. const char* commandTokenSeparator = "\x01";
  179. String createCommandLineForLaunchingServer (const String& pipeName, const String& projectUID, const File& cacheLocation)
  180. {
  181. StringArray info;
  182. info.add (pipeName);
  183. info.add (projectUID);
  184. info.add (cacheLocation.getFullPathName());
  185. #if JUCE_WINDOWS
  186. info.add (String::toHexString (getCurrentProcessID()));
  187. #endif
  188. const File exe (File::getSpecialLocation (File::currentExecutableFile).getFullPathName());
  189. return "\"" + exe.getFullPathName() + "\" " + commandPrefix + info.joinIntoString (commandTokenSeparator);
  190. }
  191. static ServerIPC* currentServer = nullptr;
  192. static void crashCallback (const char* message)
  193. {
  194. if (currentServer != nullptr)
  195. {
  196. #if RUN_CLANG_IN_CHILD_PROCESS
  197. MessageTypes::sendCrash (*currentServer, message);
  198. Logger::writeToLog ("*** Crashed! " + String (message));
  199. #else
  200. ignoreUnused (message);
  201. jassertfalse;
  202. #endif
  203. currentServer->disconnect();
  204. }
  205. }
  206. static void quitCallback()
  207. {
  208. ProjucerApplication::getApp().systemRequestedQuit();
  209. }
  210. void* createClangServer (const String& commandLine)
  211. {
  212. StringArray info;
  213. info.addTokens (commandLine.fromFirstOccurrenceOf (commandPrefix, false, false), commandTokenSeparator, "");
  214. ScopedPointer<ServerIPC> ipc = new ServerIPC (info);
  215. if (ipc->dll.isLoaded())
  216. {
  217. ipc->dll.initialise (crashCallback, quitCallback, (bool) RUN_CLANG_IN_CHILD_PROCESS);
  218. currentServer = ipc.release();
  219. return currentServer;
  220. }
  221. return nullptr;
  222. }
  223. void destroyClangServer (void* server)
  224. {
  225. currentServer = nullptr;
  226. delete static_cast<ServerIPC*> (server);
  227. }
  228. void sendQuitMessageToIDE (void* server)
  229. {
  230. static_cast<ServerIPC*> (server)->sendQuitMessageToIDE();
  231. }
  232. //==============================================================================
  233. #if JUCE_WINDOWS
  234. #define STRICT 1
  235. #define WIN32_LEAN_AND_MEAN 1
  236. #include <windows.h>
  237. static HANDLE parentProcessHandle = 0;
  238. static void setParentProcessID (int pid) { parentProcessHandle = OpenProcess (SYNCHRONIZE, FALSE, (DWORD) pid); }
  239. static int getCurrentProcessID() { return (int) GetCurrentProcessId(); }
  240. #endif
  241. #if RUN_CLANG_IN_CHILD_PROCESS
  242. bool parentProcessHasExited()
  243. {
  244. #if JUCE_WINDOWS
  245. return WaitForSingleObject (parentProcessHandle, 0) == WAIT_OBJECT_0;
  246. #else
  247. return getppid() == 1;
  248. #endif
  249. }
  250. #endif