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.

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