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.

311 lines
8.4KB

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