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.

301 lines
8.0KB

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