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.3KB

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