|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2015 - ROLI Ltd.
-
- Permission is granted to use this software under the terms of either:
- a) the GPL v2 (or any later version)
- b) the Affero GPL v3
-
- Details of these licenses can be found at: www.gnu.org/licenses
-
- JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.juce.com for more information.
-
- ==============================================================================
- */
-
- #include "../jucer_Headers.h"
- #include "../Utility/jucer_PresetIDs.h"
- #include "../Utility/jucer_FileHelpers.h"
- #include "../Application/jucer_AppearanceSettings.h"
- #include "../Application/jucer_Application.h"
- #include "../Utility/jucer_CodeHelpers.h"
- #include "projucer_CompileEngineDLL.h"
- #include "projucer_MessageIDs.h"
- #include "projucer_CppHelpers.h"
- #include "projucer_SourceCodeRange.h"
- #include "projucer_ClassDatabase.h"
- #include "projucer_DiagnosticMessage.h"
- #include "projucer_ProjectBuildInfo.h"
- #include "projucer_ClientServerMessages.h"
-
- #if JUCE_LINUX
- #include <sys/types.h>
- #include <unistd.h>
- #endif
-
- #ifndef RUN_CLANG_IN_CHILD_PROCESS
- #error
- #endif
-
- #if RUN_CLANG_IN_CHILD_PROCESS
- static bool parentProcessHasExited();
- #endif
-
- #if JUCE_WINDOWS
- static void setParentProcessID (int);
- static int getCurrentProcessID();
- #endif
-
-
- //==============================================================================
- /** Detects whether this process has hung, and kills it if so. */
- struct ZombiePatrol : private Thread,
- private AsyncUpdater,
- private Timer
- {
- ZombiePatrol (MessageHandler& mh)
- : Thread ("Ping"), owner (mh)
- {
- startThread (2);
- startTimer (1000);
- }
-
- ~ZombiePatrol()
- {
- stopThread (1000);
- }
-
- private:
- MessageHandler& owner;
- int failedPings = 0;
-
- void run() override
- {
- while (! threadShouldExit())
- {
- #if RUN_CLANG_IN_CHILD_PROCESS
- if (parentProcessHasExited())
- {
- killProcess();
- break;
- }
- #endif
-
- wait (1000);
- }
- }
-
- void handleAsyncUpdate() override
- {
- DBG ("Server: quitting");
- stopTimer();
- ProjucerApplication::getApp().systemRequestedQuit();
- }
-
- void timerCallback() override
- {
- if (! MessageTypes::sendPing (owner))
- {
- if (++failedPings == 10)
- {
- killProcess();
- return;
- }
- }
- else
- {
- failedPings = 0;
- }
- }
-
- void killProcess()
- {
- triggerAsyncUpdate(); // give the messagequeue a chance to do things cleanly.
- static UnstoppableKillerThread* k = new UnstoppableKillerThread(); // (allowed to leak, but static so only one is created)
- ignoreUnused (k);
- }
-
- struct UnstoppableKillerThread : public Thread
- {
- UnstoppableKillerThread() : Thread ("Killer") { startThread(); }
-
- void run() override
- {
- wait (15000);
-
- if (! threadShouldExit())
- Process::terminate();
- }
- };
- };
-
- //==============================================================================
- class ServerIPC : public InterprocessConnection,
- public MessageHandler
- {
- public:
- ServerIPC (const StringArray& info)
- : InterprocessConnection (true), liveCodeBuilder (nullptr)
- {
- if (! createPipe (info[0], -1))
- {
- Logger::writeToLog ("*** Couldn't create pipe!");
- ProjucerApplication::getApp().systemRequestedQuit();
- return;
- }
-
- if (dll.isLoaded())
- liveCodeBuilder = dll.projucer_createBuilder (sendMessageCallback, this, info[1].toRawUTF8(), info[2].toRawUTF8());
-
- #if JUCE_WINDOWS
- setParentProcessID (info[3].getHexValue32());
- #endif
-
- zombieKiller = new ZombiePatrol (*this);
- }
-
- ~ServerIPC()
- {
- zombieKiller = nullptr;
-
- if (dll.isLoaded())
- dll.projucer_deleteBuilder (liveCodeBuilder);
-
- dll.shutdown();
-
- DBG ("Server: finished closing down");
- }
-
- void connectionMade() override
- {
- DBG ("Server: client connected");
- }
-
- void connectionLost() override
- {
- Logger::writeToLog ("Server: client lost");
- JUCEApplication::quit();
- }
-
- void sendQuitMessageToIDE()
- {
- MessageTypes::sendShouldCloseIDE (*this);
- }
-
- bool sendMessage (const ValueTree& m) override
- {
- return InterprocessConnection::sendMessage (MessageHandler::convertMessage (m));
- }
-
- void messageReceived (const MemoryBlock& message) override
- {
- jassert (dll.isLoaded());
- dll.projucer_sendMessage (liveCodeBuilder, message.getData(), message.getSize());
- }
-
- static bool sendMessageCallback (void* userInfo, const void* data, size_t dataSize)
- {
- return static_cast<InterprocessConnection*> (static_cast<ServerIPC*> (userInfo))
- ->sendMessage (MemoryBlock (data, dataSize));
- }
-
- CompileEngineDLL dll;
- LiveCodeBuilder liveCodeBuilder;
- ScopedPointer<ZombiePatrol> zombieKiller;
- };
-
- //==============================================================================
- const char* commandPrefix = "--server:";
- const char* commandTokenSeparator = "\x01";
-
- String createCommandLineForLaunchingServer (const String& pipeName, const String& projectUID, const File& cacheLocation)
- {
- StringArray info;
- info.add (pipeName);
- info.add (projectUID);
- info.add (cacheLocation.getFullPathName());
-
- #if JUCE_WINDOWS
- info.add (String::toHexString (getCurrentProcessID()));
- #endif
-
- const File exe (File::getSpecialLocation (File::currentExecutableFile).getFullPathName());
-
- return "\"" + exe.getFullPathName() + "\" " + commandPrefix + info.joinIntoString (commandTokenSeparator);
- }
-
- static ServerIPC* currentServer = nullptr;
-
- static void crashCallback (const char* message)
- {
- if (currentServer != nullptr)
- {
- #if RUN_CLANG_IN_CHILD_PROCESS
- MessageTypes::sendCrash (*currentServer, message);
- Logger::writeToLog ("*** Crashed! " + String (message));
- #else
- ignoreUnused (message);
- jassertfalse;
- #endif
-
- currentServer->disconnect();
- }
- }
-
- static void quitCallback()
- {
- ProjucerApplication::getApp().systemRequestedQuit();
- }
-
- void* createClangServer (const String& commandLine)
- {
- StringArray info;
- info.addTokens (commandLine.fromFirstOccurrenceOf (commandPrefix, false, false), commandTokenSeparator, "");
-
- ScopedPointer<ServerIPC> ipc = new ServerIPC (info);
-
- if (ipc->dll.isLoaded())
- {
- ipc->dll.initialise (crashCallback, quitCallback, (bool) RUN_CLANG_IN_CHILD_PROCESS);
-
- currentServer = ipc.release();
- return currentServer;
- }
-
- return nullptr;
- }
-
- void destroyClangServer (void* server)
- {
- currentServer = nullptr;
- delete static_cast<ServerIPC*> (server);
- }
-
- void sendQuitMessageToIDE (void* server)
- {
- static_cast<ServerIPC*> (server)->sendQuitMessageToIDE();
- }
-
- //==============================================================================
- #if JUCE_WINDOWS
- #define STRICT 1
- #define WIN32_LEAN_AND_MEAN 1
- #include <windows.h>
-
- static HANDLE parentProcessHandle = 0;
- static void setParentProcessID (int pid) { parentProcessHandle = OpenProcess (SYNCHRONIZE, FALSE, (DWORD) pid); }
- static int getCurrentProcessID() { return (int) GetCurrentProcessId(); }
- #endif
-
- #if RUN_CLANG_IN_CHILD_PROCESS
- bool parentProcessHasExited()
- {
- #if JUCE_WINDOWS
- return WaitForSingleObject (parentProcessHandle, 0) == WAIT_OBJECT_0;
- #else
- return getppid() == 1;
- #endif
- }
- #endif
|