mirror of
https://github.com/nodejs/node.git
synced 2025-05-16 10:12:54 +00:00
bindings: add spawn_sync bindings
This implements a nested event loop that makes it possible to control a child process, while blocking the main loop until the process exits.
This commit is contained in:
parent
d58c206862
commit
fa4eb47caa
1
node.gyp
1
node.gyp
@ -104,6 +104,7 @@
|
||||
'src/pipe_wrap.cc',
|
||||
'src/signal_wrap.cc',
|
||||
'src/smalloc.cc',
|
||||
'src/spawn_sync.cc',
|
||||
'src/string_bytes.cc',
|
||||
'src/stream_wrap.cc',
|
||||
'src/tcp_wrap.cc',
|
||||
|
21
src/env.h
21
src/env.h
@ -53,6 +53,7 @@ namespace node {
|
||||
// for the sake of convenience.
|
||||
#define PER_ISOLATE_STRING_PROPERTIES(V) \
|
||||
V(address_string, "address") \
|
||||
V(args_string, "args") \
|
||||
V(async_queue_string, "_asyncQueue") \
|
||||
V(async, "async") \
|
||||
V(atime_string, "atime") \
|
||||
@ -66,10 +67,13 @@ namespace node {
|
||||
V(close_string, "close") \
|
||||
V(code_string, "code") \
|
||||
V(ctime_string, "ctime") \
|
||||
V(cwd_string, "cwd") \
|
||||
V(detached_string, "detached") \
|
||||
V(dev_string, "dev") \
|
||||
V(disposed_string, "_disposed") \
|
||||
V(domain_string, "domain") \
|
||||
V(enter_string, "enter") \
|
||||
V(env_pairs_string, "envPairs") \
|
||||
V(errno_string, "errno") \
|
||||
V(error_string, "error") \
|
||||
V(exit_string, "exit") \
|
||||
@ -78,6 +82,8 @@ namespace node {
|
||||
V(ext_key_usage_string, "ext_key_usage") \
|
||||
V(family_string, "family") \
|
||||
V(fatal_exception_string, "_fatalException") \
|
||||
V(fd_string, "fd") \
|
||||
V(file_string, "file") \
|
||||
V(fingerprint_string, "fingerprint") \
|
||||
V(flags_string, "flags") \
|
||||
V(gid_string, "gid") \
|
||||
@ -86,12 +92,17 @@ namespace node {
|
||||
V(heap_size_limit_string, "heap_size_limit") \
|
||||
V(heap_total_string, "heapTotal") \
|
||||
V(heap_used_string, "heapUsed") \
|
||||
V(ignore_string, "ignore") \
|
||||
V(immediate_callback_string, "_immediateCallback") \
|
||||
V(inherit_string, "inherit") \
|
||||
V(ino_string, "ino") \
|
||||
V(input_string, "input") \
|
||||
V(ipv4_string, "IPv4") \
|
||||
V(ipv6_string, "IPv6") \
|
||||
V(issuer_string, "issuer") \
|
||||
V(kill_signal_string, "killSignal") \
|
||||
V(mark_sweep_compact_string, "mark-sweep-compact") \
|
||||
V(max_buffer_string, "maxBuffer") \
|
||||
V(message_string, "message") \
|
||||
V(method_string, "method") \
|
||||
V(mode_string, "mode") \
|
||||
@ -114,10 +125,14 @@ namespace node {
|
||||
V(onselect_string, "onselect") \
|
||||
V(onsignal_string, "onsignal") \
|
||||
V(onstop_string, "onstop") \
|
||||
V(output_string, "output") \
|
||||
V(path_string, "path") \
|
||||
V(pid_string, "pid") \
|
||||
V(pipe_string, "pipe") \
|
||||
V(port_string, "port") \
|
||||
V(processed_string, "processed") \
|
||||
V(rdev_string, "rdev") \
|
||||
V(readable_string, "readable") \
|
||||
V(rename_string, "rename") \
|
||||
V(rss_string, "rss") \
|
||||
V(scavenge_string, "scavenge") \
|
||||
@ -125,6 +140,7 @@ namespace node {
|
||||
V(servername_string, "servername") \
|
||||
V(session_id_string, "sessionId") \
|
||||
V(should_keep_alive_string, "shouldKeepAlive") \
|
||||
V(signal_string, "signal") \
|
||||
V(size_string, "size") \
|
||||
V(smalloc_p_string, "_smalloc_p") \
|
||||
V(sni_context_err_string, "Invalid SNI context") \
|
||||
@ -132,9 +148,12 @@ namespace node {
|
||||
V(stack_string, "stack") \
|
||||
V(status_code_string, "statusCode") \
|
||||
V(status_message_string, "statusMessage") \
|
||||
V(status_string, "status") \
|
||||
V(stdio_string, "stdio") \
|
||||
V(subject_string, "subject") \
|
||||
V(subjectaltname_string, "subjectaltname") \
|
||||
V(syscall_string, "syscall") \
|
||||
V(timeout_string, "timeout") \
|
||||
V(timestamp_string, "timestamp") \
|
||||
V(tls_ticket_string, "tlsTicket") \
|
||||
V(total_heap_size_executable_string, "total_heap_size_executable") \
|
||||
@ -150,6 +169,8 @@ namespace node {
|
||||
V(version_major_string, "versionMajor") \
|
||||
V(version_minor_string, "versionMinor") \
|
||||
V(version_string, "version") \
|
||||
V(windows_verbatim_arguments_string, "windowsVerbatimArguments") \
|
||||
V(writable_string, "writable") \
|
||||
V(write_queue_size_string, "writeQueueSize") \
|
||||
|
||||
#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \
|
||||
|
1042
src/spawn_sync.cc
Normal file
1042
src/spawn_sync.cc
Normal file
File diff suppressed because it is too large
Load Diff
247
src/spawn_sync.h
Normal file
247
src/spawn_sync.h
Normal file
@ -0,0 +1,247 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#ifndef SRC_SPAWN_SYNC_H_
|
||||
#define SRC_SPAWN_SYNC_H_
|
||||
|
||||
#include "node.h"
|
||||
#include "node_buffer.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
using v8::Array;
|
||||
using v8::Context;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::Handle;
|
||||
using v8::HandleScope;
|
||||
using v8::Integer;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::Null;
|
||||
using v8::Number;
|
||||
using v8::Object;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
|
||||
class SyncProcessOutputBuffer;
|
||||
class SyncProcessStdioPipe;
|
||||
class SyncProcessRunner;
|
||||
|
||||
|
||||
class SyncProcessOutputBuffer {
|
||||
static const unsigned int kBufferSize = 65536;
|
||||
|
||||
public:
|
||||
inline SyncProcessOutputBuffer();
|
||||
|
||||
inline void OnAlloc(size_t suggested_size, uv_buf_t* buf) const;
|
||||
inline void OnRead(const uv_buf_t* buf, size_t nread);
|
||||
|
||||
inline size_t Copy(char* dest) const;
|
||||
|
||||
inline unsigned int available() const;
|
||||
inline unsigned int used() const;
|
||||
|
||||
inline SyncProcessOutputBuffer* next() const;
|
||||
inline void set_next(SyncProcessOutputBuffer* next);
|
||||
|
||||
private:
|
||||
// Use unsigned int because that's what `uv_buf_init` takes.
|
||||
mutable char data_[kBufferSize];
|
||||
unsigned int used_;
|
||||
|
||||
SyncProcessOutputBuffer* next_;
|
||||
};
|
||||
|
||||
|
||||
class SyncProcessStdioPipe {
|
||||
enum Lifecycle {
|
||||
kUninitialized = 0,
|
||||
kInitialized,
|
||||
kStarted,
|
||||
kClosing,
|
||||
kClosed
|
||||
};
|
||||
|
||||
public:
|
||||
SyncProcessStdioPipe(SyncProcessRunner* process_handler,
|
||||
bool readable,
|
||||
bool writable,
|
||||
uv_buf_t input_buffer);
|
||||
~SyncProcessStdioPipe();
|
||||
|
||||
int Initialize(uv_loop_t* loop);
|
||||
int Start();
|
||||
void Close();
|
||||
|
||||
Local<Object> GetOutputAsBuffer() const;
|
||||
|
||||
inline bool readable() const;
|
||||
inline bool writable() const;
|
||||
inline uv_stdio_flags uv_flags() const;
|
||||
|
||||
inline uv_pipe_t* uv_pipe() const;
|
||||
inline uv_stream_t* uv_stream() const;
|
||||
inline uv_handle_t* uv_handle() const;
|
||||
|
||||
private:
|
||||
inline size_t OutputLength() const;
|
||||
inline void CopyOutput(char* dest) const;
|
||||
|
||||
inline void OnAlloc(size_t suggested_size, uv_buf_t* buf);
|
||||
inline void OnRead(const uv_buf_t* buf, ssize_t nread);
|
||||
inline void OnWriteDone(int result);
|
||||
inline void OnShutdownDone(int result);
|
||||
inline void OnClose();
|
||||
|
||||
inline void SetError(int error);
|
||||
|
||||
static void AllocCallback(uv_handle_t* handle,
|
||||
size_t suggested_size,
|
||||
uv_buf_t* buf);
|
||||
static void ReadCallback(uv_stream_t* stream,
|
||||
ssize_t nread,
|
||||
const uv_buf_t* buf);
|
||||
static void WriteCallback(uv_write_t* req, int result);
|
||||
static void ShutdownCallback(uv_shutdown_t* req, int result);
|
||||
static void CloseCallback(uv_handle_t* handle);
|
||||
|
||||
SyncProcessRunner* process_handler_;
|
||||
|
||||
bool readable_;
|
||||
bool writable_;
|
||||
uv_buf_t input_buffer_;
|
||||
|
||||
SyncProcessOutputBuffer* first_output_buffer_;
|
||||
SyncProcessOutputBuffer* last_output_buffer_;
|
||||
|
||||
mutable uv_pipe_t uv_pipe_;
|
||||
uv_write_t write_req_;
|
||||
uv_shutdown_t shutdown_req_;
|
||||
|
||||
Lifecycle lifecycle_;
|
||||
};
|
||||
|
||||
|
||||
class SyncProcessRunner {
|
||||
enum Lifecycle {
|
||||
kUninitialized = 0,
|
||||
kInitialized,
|
||||
kHandlesClosed
|
||||
};
|
||||
|
||||
public:
|
||||
static void Initialize(Handle<Object> target,
|
||||
Handle<Value> unused,
|
||||
Handle<Context> context);
|
||||
static void Spawn(const FunctionCallbackInfo<Value>& args);
|
||||
|
||||
private:
|
||||
friend class SyncProcessStdioPipe;
|
||||
|
||||
explicit SyncProcessRunner(Environment* env_);
|
||||
~SyncProcessRunner();
|
||||
|
||||
inline Environment* env() const;
|
||||
|
||||
Local<Object> Run(Local<Value> options);
|
||||
void TryInitializeAndRunLoop(Local<Value> options);
|
||||
void CloseHandlesAndDeleteLoop();
|
||||
|
||||
void CloseStdioPipes();
|
||||
void CloseKillTimer();
|
||||
|
||||
void Kill();
|
||||
void IncrementBufferSizeAndCheckOverflow(ssize_t length);
|
||||
|
||||
void OnExit(int64_t exit_status, int term_signal);
|
||||
void OnKillTimerTimeout(int status);
|
||||
|
||||
int GetError();
|
||||
void SetError(int error);
|
||||
void SetPipeError(int pipe_error);
|
||||
|
||||
Local<Object> BuildResultObject();
|
||||
Local<Array> BuildOutputArray();
|
||||
|
||||
int ParseOptions(Local<Value> js_value);
|
||||
int ParseStdioOptions(Local<Value> js_value);
|
||||
int ParseStdioOption(int child_fd, Local<Object> js_stdio_option);
|
||||
|
||||
inline int AddStdioIgnore(uint32_t child_fd);
|
||||
inline int AddStdioPipe(uint32_t child_fd,
|
||||
bool readable,
|
||||
bool writable,
|
||||
uv_buf_t input_buffer);
|
||||
inline int AddStdioInheritFD(uint32_t child_fd, int inherit_fd);
|
||||
|
||||
static bool IsSet(Local<Value> value);
|
||||
template <typename t> static bool CheckRange(Local<Value> js_value);
|
||||
static int CopyJsString(Local<Value> js_value, const char** target);
|
||||
static int CopyJsStringArray(Local<Value> js_value, char** target);
|
||||
|
||||
static void ExitCallback(uv_process_t* handle,
|
||||
int64_t exit_status,
|
||||
int term_signal);
|
||||
static void KillTimerCallback(uv_timer_t* handle, int status);
|
||||
static void KillTimerCloseCallback(uv_handle_t* handle);
|
||||
|
||||
size_t max_buffer_;
|
||||
uint64_t timeout_;
|
||||
int kill_signal_;
|
||||
|
||||
uv_loop_t* uv_loop_;
|
||||
|
||||
uint32_t stdio_count_;
|
||||
uv_stdio_container_t* uv_stdio_containers_;
|
||||
SyncProcessStdioPipe** stdio_pipes_;
|
||||
bool stdio_pipes_initialized_;
|
||||
|
||||
uv_process_options_t uv_process_options_;
|
||||
const char* file_buffer_;
|
||||
char* args_buffer_;
|
||||
char* env_buffer_;
|
||||
const char* cwd_buffer_;
|
||||
|
||||
uv_process_t uv_process_;
|
||||
bool killed_;
|
||||
|
||||
size_t buffered_output_size_;
|
||||
int64_t exit_status_;
|
||||
int term_signal_;
|
||||
|
||||
uv_timer_t uv_timer_;
|
||||
bool kill_timer_initialized_;
|
||||
|
||||
// Errors that happen in one of the pipe handlers are stored in the
|
||||
// `pipe_error` field. They are treated as "low-priority", only to be
|
||||
// reported if no more serious errors happened.
|
||||
int error_;
|
||||
int pipe_error_;
|
||||
|
||||
Lifecycle lifecycle_;
|
||||
|
||||
Environment* env_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SRC_SPAWN_SYNC_H_
|
Loading…
Reference in New Issue
Block a user