Browse Source

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.
v0.11.12-release
Bert Belder 11 years ago
parent
commit
fa4eb47caa
  1. 1
      node.gyp
  2. 21
      src/env.h
  3. 1042
      src/spawn_sync.cc
  4. 247
      src/spawn_sync.h

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

@ -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

File diff suppressed because it is too large

247
src/spawn_sync.h

@ -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…
Cancel
Save