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.

306 lines
8.2KB

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