diff --git a/appshell/appshell_node_process_linux.cpp b/appshell/appshell_node_process_linux.cpp index 34721a0fd..14ffbb60b 100644 --- a/appshell/appshell_node_process_linux.cpp +++ b/appshell/appshell_node_process_linux.cpp @@ -21,55 +21,213 @@ * */ + #include "appshell_node_process.h" #include "appshell_node_process_internal.h" +#include +#include +#include +#include +#include +#include +#include + + #ifndef OS_LINUX #define OS_LINUX 1 #endif #include "config.h" #define BRACKETS_NODE_BUFFER_SIZE 4096 +#define MAX_PATH 128 + +// init mutex +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +// write & read references to subprocess +FILE* streamTo; +FILE* streamFrom; + +// Threads should hold mutex before using these +static int nodeState = BRACKETS_NODE_NOT_YET_STARTED; +static int nodeStartTime = 0; // Forward declarations -//DWORD WINAPI NodeThread(LPVOID); +void* nodeThread(void*); +void* nodeReadThread(void*); void restartNode(bool); // Creates the thread that starts Node and then monitors the state // of the node process. void startNodeProcess() { + + pthread_t thread_id; + if (pthread_create(&thread_id, NULL, &nodeThread, NULL) != 0) + nodeState = BRACKETS_NODE_FAILED; } + +// Thread function for the thread that starts the node process +// and monitors that process's state +void* nodeThread(void* unused) { + + // get mutex + if (pthread_mutex_lock(&mutex)) { + fprintf(stderr, + "failed to acquire mutex for Node subprocess start: %s\n", + strerror(errno)); + return NULL; + } + + // TODO nodeStartTime = get time(); + + char executablePath[MAX_PATH]; + char bracketsShellPath[MAX_PATH]; + char nodeExecutablePath[MAX_PATH]; + char nodecorePath[MAX_PATH]; + + // get path to Brackets + if (readlink("/proc/self/exe", executablePath, MAX_PATH) == -1) { + fprintf(stderr, "cannot find Brackets path: %s\n", strerror(errno)); + pthread_mutex_unlock(&mutex); + return NULL; + } + + // strip off 'out/Release/Brackets' for path to brackets-shell + size_t pathLength = strlen(executablePath); + memcpy(bracketsShellPath, executablePath, pathLength - 20); + + // create node exec and node-core paths + strcpy(nodeExecutablePath, bracketsShellPath); + strcat(nodeExecutablePath, NODE_EXECUTABLE_PATH); + strcpy(nodecorePath, bracketsShellPath); + strcat(nodecorePath, NODE_CORE_PATH); + + // create pipes for node process stdin/stdout + int toNode[2]; + int fromNode[2]; + + pipe(toNode); + pipe(fromNode); + + // create the Node process + pid_t child_pid = fork(); + if (child_pid == 0) { // child (node) process + // close our copy of write end and + // connect read end to stdin + close(toNode[1]); + dup2(toNode[0], STDIN_FILENO); + + // close our copy of read end and + // connect write end to stdout + close(fromNode[0]); + dup2(fromNode[1], STDOUT_FILENO); + + // run node executable + char* arg_list[] = { nodeExecutablePath, nodecorePath, NULL}; + execvp(arg_list[0], arg_list); + + fprintf(stderr, "the Node process failed to start: %s\n", strerror(errno)); + abort(); + } + else { // parent + + // close our reference of toNode's read end + // and fromNode's write end + close(toNode[0]); + close(fromNode[1]); + + // convert subprocess write & read to FILE objects + streamTo = fdopen(toNode[1], "w"); + streamFrom = fdopen(fromNode[0], "r"); + + nodeState = BRACKETS_NODE_PORT_NOT_YET_SET; + + // done launching process so release mutex + if (pthread_mutex_unlock(&mutex)) { + fprintf(stderr, + "failed to release mutex for Node subprocess startup: %s\n", + strerror(errno)); + } + + // start pipe read thread + pthread_t readthread_id; + if (pthread_create(&readthread_id, NULL, &nodeReadThread, NULL) != 0) + nodeState = BRACKETS_NODE_FAILED; + // ugly - need to think more about what to do if read thread fails + + } + + return NULL; +} + + // Thread function for the thread that reads from the Node pipe // Reads on anonymous pipes are always blocking (OVERLAPPED reads // are not possible) So, we need to do this in a separate thread -// + // TODO: This code first reads to a character buffer, and then // copies it to a std::string. The code could be optimized to avoid // this double-copy -//DWORD WINAPI NodeReadThread(LPVOID lpParam) { -//} +void* nodeReadThread(void* unused) { + + char charBuf[BRACKETS_NODE_BUFFER_SIZE]; + std::string strBuf(""); + while (fgets(charBuf, BRACKETS_NODE_BUFFER_SIZE, streamFrom) != NULL) { + strBuf.assign(charBuf); + processIncomingData(strBuf); + } +} -// Thread function for the thread that starts the node process -// and monitors that process's state -//DWORD WINAPI NodeThread(LPVOID lpParam) { -//} // Determines whether the current node process has run long // enough that a restart is warranted, and initiates the startup // if so. Can also optionally terminate the running process. void restartNode(bool terminateCurrentProcess) { + } // Sends data to the node process. If the write fails completely, // calls restartNode. void sendData(const std::string &data) { + + if (pthread_mutex_lock(&mutex)) { + fprintf(stderr, + "failed to acquire mutex for write to Node subprocess: %s\n", + strerror(errno)); + return; + } + + // write to pipe + fprintf(streamTo, "%s", data.c_str()); + + if (pthread_mutex_unlock(&mutex)) { + fprintf(stderr, + "failed to release mutex for write to Node subprocess: %s\n", + strerror(errno)); + } } -// Thread-safe way to access nodeState variable +// Returns nodeState variable int getNodeState() { + + return nodeState; } // Thread-safe way to set nodeState variable void setNodeState(int newState) { + + if (pthread_mutex_lock(&mutex)) { + fprintf(stderr, + "failed to acquire mutex for setting Node state: %s\n", + strerror(errno)); + return; + } + nodeState = newState; + if (pthread_mutex_unlock(&mutex)) { + fprintf(stderr, + "failed to release mutex for Node set state: %s\n", + strerror(errno)); + } } diff --git a/appshell/cefclient_gtk.cpp b/appshell/cefclient_gtk.cpp index 668e8d9ba..94ab4d134 100644 --- a/appshell/cefclient_gtk.cpp +++ b/appshell/cefclient_gtk.cpp @@ -1,4 +1,4 @@ - // Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights +// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. @@ -13,6 +13,7 @@ #include "include/cef_frame.h" #include "include/cef_runnable.h" #include "client_handler.h" +#include "appshell_node_process.h" static std::string APPICONS[] = {"appshell32.png","appshell48.png","appshell128.png","appshell256.png"}; char szWorkingDir[512]; // The current working directory @@ -24,7 +25,7 @@ bool isReallyClosing = false; // The global ClientHandler reference. extern CefRefPtr g_handler; -//Application startup time +// Application startup time time_t g_appStartupTime; void destroy(void) { @@ -247,6 +248,9 @@ int main(int argc, char* argv[]) { // Install an signal handler so we clean up after ourselves. signal(SIGINT, TerminationSignalHandler); signal(SIGTERM, TerminationSignalHandler); + + // Start the node server process + startNodeProcess(); CefRunMessageLoop(); diff --git a/appshell/config.h b/appshell/config.h index e43c5f6f2..ae3d054ae 100644 --- a/appshell/config.h +++ b/appshell/config.h @@ -49,6 +49,19 @@ #define NODE_CORE_PATH @"/Contents/node-core" #endif +#ifdef OS_LINUX +//#define GROUP_NAME @"" +//#define APP_NAME @"Brackets" +//#define WINDOW_TITLE APP_NAME + +// Path for node resources is in dependencies dir and relative to the location of the appshell executable +#define NODE_EXECUTABLE_PATH "deps/node/bin/Brackets-node" +#define NODE_CORE_PATH "appshell/node-core" + +#endif + + + #define REMOTE_DEBUGGING_PORT 9234