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.

308 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 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #include "../Application/jucer_Headers.h"
  19. #include "../Application/jucer_Application.h"
  20. #include "jucer_CompileEngineServer.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. #else
  43. #include <unistd.h>
  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. explicit ZombiePatrol (MessageHandler& mh)
  52. : Thread ("Ping"), owner (mh)
  53. {
  54. startThread (2);
  55. startTimer (1000);
  56. }
  57. ~ZombiePatrol() override
  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. explicit 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.reset (new ZombiePatrol (*this));
  136. }
  137. ~ServerIPC() override
  138. {
  139. zombieKiller.reset();
  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. std::unique_ptr<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. std::unique_ptr<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 = nullptr;
  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