diff options
author | Bert Belder <bertbelder@gmail.com> | 2014-02-10 21:22:06 +0100 |
---|---|---|
committer | Bert Belder <bertbelder@gmail.com> | 2014-02-10 21:22:06 +0100 |
commit | fa4eb47caacde4435c16f4ebef0c4f3fa001ccd2 (patch) | |
tree | 76554f03374cecc9dc6331d841e23f5d2471f566 /src/spawn_sync.h | |
parent | d58c206862dc4f4ffe364cab5bf6b69afc0a52df (diff) | |
download | node-new-fa4eb47caacde4435c16f4ebef0c4f3fa001ccd2.tar.gz |
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.
Diffstat (limited to 'src/spawn_sync.h')
-rw-r--r-- | src/spawn_sync.h | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/src/spawn_sync.h b/src/spawn_sync.h new file mode 100644 index 0000000000..f0fcb464ed --- /dev/null +++ b/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_ |