From 17c6a67f15944962f27326072d9ea2df40ec0491 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 24 Aug 2009 20:25:24 +0200 Subject: [PATCH] Introduce node.stdio Remove old stdout, stderr, stdin objects. --- deps/coupling/coupling.c | 321 ++++++++++++++++++++++++++++++++++ deps/coupling/coupling.h | 42 +++++ deps/coupling/test.c | 73 ++++++++ src/constants.cc | 4 - src/file.js | 11 -- src/node.cc | 15 +- src/node.js | 2 +- src/node_stdio.cc | 268 ++++++++++++++++++++++++++++ src/node_stdio.h | 17 ++ src/util.js | 16 ++ test/mjsunit/test-node-cat.js | 11 +- website/api.txt | 37 +++- wscript | 14 +- 13 files changed, 793 insertions(+), 38 deletions(-) create mode 100644 deps/coupling/coupling.c create mode 100644 deps/coupling/coupling.h create mode 100644 deps/coupling/test.c create mode 100644 src/node_stdio.cc create mode 100644 src/node_stdio.h diff --git a/deps/coupling/coupling.c b/deps/coupling/coupling.c new file mode 100644 index 000000000..bd7b4b8a7 --- /dev/null +++ b/deps/coupling/coupling.c @@ -0,0 +1,321 @@ +/* Copyright (c) 2009 Ryan Dahl (ry@tinyclouds.org) + * + * All rights reserved. + * + * 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. + */ +#include "coupling.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef PIPE_BUF +# define BUFSIZE PIPE_BUF +#else +# define BUFSIZE 4096 +#endif + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +// ring buffer +typedef struct { + int head; + int tail; + int size; + char buf[BUFSIZE]; +} ring_buffer; + +static inline void +ring_buffer_inspect (ring_buffer *ring) +{ + printf("size %5d head %5d tail %5d\n", ring->size, ring->head, ring->tail); +} + +static inline void +ring_buffer_init (ring_buffer *ring) +{ + ring->head = 0; + ring->tail = 0; + ring->size = 0; +} + +static inline int +ring_buffer_filled_p (ring_buffer *ring) +{ + assert(BUFSIZE - (long)ring->size >= 0); + return (BUFSIZE == ring->size); +} + +static inline int +ring_buffer_empty_p (ring_buffer *ring) +{ + return 0 == ring->size; +} + +static ssize_t +ring_buffer_pull (ring_buffer *ring, int fd) +{ + // DO NOT CALL WHEN FILLED + assert(!ring_buffer_filled_p(ring)); + + struct iovec iov[2]; + int iovcnt = 1; + + // Very tough logic. Can you follow? Barely can I. + iov[0].iov_base = ring->buf + ring->tail; + if (ring->tail < ring->head) { + iov[0].iov_len = ring->head - ring->tail; + } else { + iov[0].iov_len = BUFSIZE - ring->tail; + if (ring->head != 0) { + iovcnt = 2; + iov[1].iov_base = ring->buf; + iov[1].iov_len = ring->head; + } + } + + int r = readv(fd, iov, iovcnt); + + if (r > 0) { + ring->size += r; + ring->tail = (ring->tail + r) % BUFSIZE; + } + assert(ring->size <= BUFSIZE); + + return r; +} + +static ssize_t +ring_buffer_push (ring_buffer *ring, int fd) +{ + // DO NOT CALL WHEN EMPTY + assert(!ring_buffer_empty_p(ring)); + + struct iovec iov[2]; + int iovcnt = 1; + + iov[0].iov_base = ring->buf + ring->head; + if (ring->head < ring->tail) { + iov[0].iov_len = ring->tail - ring->head; + } else { + iov[0].iov_len = BUFSIZE - ring->head; + if (ring->tail != 0) { + iovcnt = 2; + iov[1].iov_base = ring->buf; + iov[1].iov_len = ring->tail; + } + } + + int r = writev(fd, iov, iovcnt); + + if (r > 0) { + ring->size -= r; + ring->head = (ring->head + r) % BUFSIZE; + } + assert(0 <= (long)ring->size); + + return r; +} + + +static void +pump (int pullfd, int pushfd) +{ + int r; + ring_buffer ring; + fd_set readfds, writefds, exceptfds; + + ring_buffer_init(&ring); + + int maxfd; + + while (pushfd >= 0 && (pullfd >= 0 || !ring_buffer_empty_p(&ring))) { + FD_ZERO(&exceptfds); + FD_ZERO(&readfds); + FD_ZERO(&writefds); + + maxfd = pushfd; + FD_SET(pushfd, &exceptfds); + + if (pullfd >= 0) { + FD_SET(pullfd, &exceptfds); + maxfd = MAX(pushfd, pullfd); + if (!ring_buffer_filled_p(&ring)) FD_SET(pullfd, &readfds); + } + + if (!ring_buffer_empty_p(&ring)) { + FD_SET(pushfd, &writefds); + } + + r = select(maxfd+1, &readfds, &writefds, &exceptfds, NULL); + + if (r < 0 || FD_ISSET(pushfd, &exceptfds)) { + pushfd = pullfd = -1; + return; + } + + if (pullfd >= 0 && FD_ISSET(pullfd, &exceptfds)) { + pullfd = -1; + } + + if (pullfd >= 0 && FD_ISSET(pullfd, &readfds)) { + r = ring_buffer_pull(&ring, pullfd); + if (r == 0) { + /* eof */ + pullfd = -1; + + } else if (r < 0) { + if (errno != EINTR && errno != EAGAIN) goto error; + } + } + + if (FD_ISSET(pushfd, &writefds)) { + r = ring_buffer_push(&ring, pushfd); + if (r < 0) { + switch (errno) { + case EINTR: + case EAGAIN: + continue; + + case EPIPE: + /* TODO catch SIGPIPE? */ + pushfd = pullfd = -1; + return; + + default: + goto error; + } + } + } + } + + return; + +error: + perror("(coupling) pump"); +} + +static inline int +set_nonblock (int fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) return -1; + + int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (r == -1) return -1; + + return 0; +} + +struct coupling { + int is_pull; + int pullfd; + int pushfd; + int exposedfd; + pthread_t tid; +}; + +static void * +pump_thread (void *data) +{ + struct coupling *c = (struct coupling*)data; + + pump(c->pullfd, c->pushfd); + + return NULL; +} + +static struct coupling* +create_coupling (int fd, int is_pull) +{ + int pipefd[2]; + + struct coupling *c = malloc(sizeof(struct coupling)); + if (!c) return NULL; + + int r = pipe(pipefd); + if (r < 0) return NULL; + + r = set_nonblock(pipefd[0]); + if (r < 0) return NULL; + assert(pipefd[0] >= 0); + + r = set_nonblock(pipefd[1]); + if (r < 0) return NULL; + assert(pipefd[1] >= 0); + + if (is_pull) { + c->is_pull = 1; + c->pullfd = fd; + c->pushfd = pipefd[1]; + c->exposedfd = pipefd[0]; + } else { + c->is_pull = 0; + c->pushfd = fd; + c->pullfd = pipefd[0]; + c->exposedfd = pipefd[1]; + } + + r = pthread_create(&c->tid, NULL, pump_thread, c); + if (r < 0) return NULL; + + return c; +} + +struct coupling* +coupling_new_pull (int fd) +{ + return create_coupling(fd, 1); +} + +struct coupling* +coupling_new_push (int fd) +{ + return create_coupling(fd, 0); +} + +int +coupling_nonblocking_fd (struct coupling *c) +{ + return c->exposedfd; +} + +void +coupling_join (struct coupling *c) +{ + int r = pthread_join(c->tid, NULL); + assert(r == 0); +} + +void +coupling_destroy (struct coupling *c) +{ + close(c->is_pull ? c->pushfd : c->pullfd); + free(c); +} diff --git a/deps/coupling/coupling.h b/deps/coupling/coupling.h new file mode 100644 index 000000000..53d316e53 --- /dev/null +++ b/deps/coupling/coupling.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2009 Ryan Dahl (ry@tinyclouds.org) + * + * All rights reserved. + * + * 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 coupling_h +#define coupling_h +#ifdef __cplusplus +extern "C" { +#endif + +struct coupling; + +struct coupling* coupling_new_pull (int fd); +struct coupling* coupling_new_push (int fd); + int coupling_nonblocking_fd (struct coupling*); + void coupling_join (struct coupling*); + void coupling_destroy (struct coupling*); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/deps/coupling/test.c b/deps/coupling/test.c new file mode 100644 index 000000000..87884e81b --- /dev/null +++ b/deps/coupling/test.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include +#include "coupling.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define CHUNKSIZE 44231 + +int +main (int argc, char *argv[]) +{ + struct coupling *c = coupling_new_push(STDERR_FILENO); + int stderr_fd = coupling_nonblocking_fd(c); + + int size; + if (argc == 2) { + size = atoi(argv[1]); + printf("size = %d\n", size); + } else { + printf("usage: ./test 123 2> stderr; wc -c stderr\n"); + exit(1); + } + + char *msg = malloc(CHUNKSIZE); + int i, r; + for (i = 0; i < CHUNKSIZE; i++) { + msg[i] = 'A' + i % 26; + } + + int written = 0; + + fd_set writefds, exceptfds; + + FD_ZERO(&exceptfds); + FD_SET(stderr_fd, &exceptfds); + + FD_ZERO(&writefds); + FD_SET(stderr_fd, &writefds); + + while (written < size) { + r = select(stderr_fd+1, NULL, &writefds, &exceptfds, NULL); + if (r < 0) { + printf("test.c select(): %s\n", strerror(errno)); + exit(1); + } + + if (FD_ISSET(stderr_fd, &exceptfds)) { + printf("exception on stderr fd\n"); + exit(1); + } + + if (FD_ISSET(stderr_fd, &writefds)) { + r = write(stderr_fd, msg, MIN(size - written, CHUNKSIZE)); + if (r < 0 && errno != EAGAIN) { + printf("test.c write(): %s\n", strerror(errno)); + exit(1); + } else { + written += r; + printf("%d\n", written); + } + } + } + + close(stderr_fd); + coupling_join(c); + coupling_destroy(c); + + return 0; +} diff --git a/src/constants.cc b/src/constants.cc index 449b34f5d..5012fc696 100644 --- a/src/constants.cc +++ b/src/constants.cc @@ -16,10 +16,6 @@ node::DefineConstants (Handle target) NODE_DEFINE_CONSTANT(target, UTF8); NODE_DEFINE_CONSTANT(target, ASCII); - NODE_DEFINE_CONSTANT(target, STDIN_FILENO); - NODE_DEFINE_CONSTANT(target, STDOUT_FILENO); - NODE_DEFINE_CONSTANT(target, STDERR_FILENO); - // file access modes NODE_DEFINE_CONSTANT(target, O_RDONLY); NODE_DEFINE_CONSTANT(target, O_WRONLY); diff --git a/src/file.js b/src/file.js index 1c61ddecc..42ab8129e 100644 --- a/src/file.js +++ b/src/file.js @@ -203,14 +203,3 @@ node.fs.File = function (options) { return self.write(data + "\n", null); }; }; - -stdout = new node.fs.File({ fd: node.STDOUT_FILENO }); -stderr = new node.fs.File({ fd: node.STDERR_FILENO }); -stdin = new node.fs.File({ fd: node.STDIN_FILENO }); - -puts = stdout.puts; -print = stdout.print; -p = function (data) { - return puts(JSON.stringify(data)); -} - diff --git a/src/node.cc b/src/node.cc index 7fbd6903f..4f85d51d9 100644 --- a/src/node.cc +++ b/src/node.cc @@ -8,6 +8,7 @@ #include "timer.h" #include "process.h" #include "constants.h" +#include "node_stdio.h" #include "natives.h" @@ -140,17 +141,6 @@ compile (const v8::Arguments& args) return scope.Close(result); } -v8::Handle -debug (const v8::Arguments& args) -{ - if (args.Length() < 1) - return Undefined(); - HandleScope scope; - String::Utf8Value msg(args[0]->ToString()); - fprintf(stderr, "DEBUG: %s\n", *msg); - return Undefined(); -} - static void OnFatalError (const char* location, const char* message) { @@ -240,11 +230,12 @@ Load (int argc, char *argv[]) global_obj->Set(String::NewSymbol("ARGV"), arguments); NODE_SET_METHOD(node_obj, "compile", compile); - NODE_SET_METHOD(node_obj, "debug", debug); NODE_SET_METHOD(node_obj, "reallyExit", node_exit); EventEmitter::Initialize(node_obj); Promise::Initialize(node_obj); + + Stdio::Initialize(node_obj); Timer::Initialize(node_obj); Process::Initialize(node_obj); diff --git a/src/node.js b/src/node.js index 271458e92..b445e4b8b 100644 --- a/src/node.js +++ b/src/node.js @@ -73,7 +73,7 @@ node.Module.prototype.load = function (callback) { var promise = node.cat(self.filename, "utf8"); promise.addErrback(function () { - stderr.puts("Error reading " + self.filename); + node.stdio.writeError("Error reading " + self.filename + "\n"); node.exit(1); }); diff --git a/src/node_stdio.cc b/src/node_stdio.cc new file mode 100644 index 000000000..c52d632cb --- /dev/null +++ b/src/node_stdio.cc @@ -0,0 +1,268 @@ +#include "node_stdio.h" +#include "events.h" +#include "coupling.h" + +#include +#include + +using namespace v8; +using namespace node; + +static Persistent stdio; +static Persistent emit; + +static struct coupling *stdin_coupling = NULL; +static struct coupling *stdout_coupling = NULL; + +static int stdin_fd = -1; +static int stdout_fd = -1; + +static evcom_reader in; +static evcom_writer out; + +static enum encoding stdin_encoding; + +static void +EmitInput (Local input) +{ + HandleScope scope; + + Local args = Array::New(1); + args->Set(Integer::New(0), input); + + Local argv[2] = { String::NewSymbol("data"), args }; + + emit->Call(stdio, 2, argv); +} + +static void +EmitClose (void) +{ + HandleScope scope; + + Local argv[1] = { String::NewSymbol("close") }; + + emit->Call(stdio, 1, argv); +} + +/* STDERR IS ALWAY SYNC */ +static Handle +WriteError (const Arguments& args) +{ + HandleScope scope; + + if (args.Length() < 1) + return Undefined(); + + String::Utf8Value msg(args[0]->ToString()); + + fprintf(stderr, "%s", *msg); + fflush(stderr); + + return Undefined(); +} + +static Handle +Write (const Arguments& args) +{ + HandleScope scope; + + ssize_t len; + + Local string; + Local array; + + if (args[0]->IsArray()) { + array = Local::Cast(args[0]); + len = array->Length(); + } else { + string = args[0]->ToString(); + len = string->Utf8Length(); + } + + char buf[len]; + + if (args[0]->IsArray()) { + for (ssize_t index = 0; index < len; index++) { + Local int_value = array->Get(Integer::New(index)); + buf[index] = int_value->IntegerValue(); + } + } else { + switch (ParseEncoding(args[1])) { + case RAW: + case ASCII: + string->WriteAscii(buf, 0, len); + break; + + case UTF8: + string->WriteUtf8(buf, len); + break; + + default: + return ThrowException(String::New("Unknown encoding.")); + } + } + + evcom_writer_write(&out, buf, len); + + return Undefined(); +} + +static void +detach_in (evcom_reader *r) +{ + assert(r == &in); + HandleScope scope; + + EmitClose(); + + evcom_reader_detach(&in); + + if (stdin_coupling) { + coupling_destroy(stdin_coupling); + stdin_coupling = NULL; + } + + stdin_fd = -1; +} + +static void +detach_out (evcom_writer* w) +{ + assert(w == &out); + + evcom_writer_detach(&out); + if (stdout_coupling) { + coupling_destroy(stdout_coupling); + stdout_coupling = NULL; + } + stdout_fd = -1; +} + +static void +on_read (evcom_reader *r, const void *buf, size_t len) +{ + assert(r == &in); + HandleScope scope; + + if (!len) { + return; + } + + Local input; + + if (stdin_encoding == RAW) { + // raw encoding + Local array = Array::New(len); + for (size_t i = 0; i < len; i++) { + unsigned char val = static_cast(buf)[i]; + array->Set(Integer::New(i), Integer::New(val)); + } + input = array; + + } else { + // utf8 or ascii encoding + input = String::New((const char*)buf, len); + } + + EmitInput(input); +} + +static inline int +set_nonblock (int fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) return -1; + + int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (r == -1) return -1; + + return 0; +} + +static Handle +Open (const Arguments& args) +{ + HandleScope scope; + + if (stdin_fd >= 0) { + return ThrowException(String::New("stdin already open")); + } + + stdin_encoding = UTF8; + if (args.Length() > 0) { + stdin_encoding = ParseEncoding(args[0]); + } + + if (isatty(STDIN_FILENO)) { + // XXX selecting on tty fds wont work in windows. + // Must ALWAYS make a coupling on shitty platforms. + stdin_fd = STDIN_FILENO; + } else { + stdin_coupling = coupling_new_pull(STDIN_FILENO); + stdin_fd = coupling_nonblocking_fd(stdin_coupling); + } + set_nonblock(stdin_fd); + + evcom_reader_init(&in); + + in.on_read = on_read; + in.on_close = detach_in; + + evcom_reader_set(&in, stdin_fd); + evcom_reader_attach(EV_DEFAULT_ &in); + + return Undefined(); +} + +static Handle +Close (const Arguments& args) +{ + HandleScope scope; + + if (stdin_fd < 0) { + return ThrowException(String::New("stdin not open")); + } + + evcom_reader_close(&in); + + return Undefined(); +} + +void +Stdio::Initialize (v8::Handle target) +{ + HandleScope scope; + + Local stdio_local = + EventEmitter::constructor_template->GetFunction()->NewInstance(0, NULL); + + stdio = Persistent::New(stdio_local); + + NODE_SET_METHOD(stdio, "open", Open); + NODE_SET_METHOD(stdio, "write", Write); + NODE_SET_METHOD(stdio, "writeError", WriteError); + NODE_SET_METHOD(stdio, "close", Close); + + target->Set(String::NewSymbol("stdio"), stdio); + + Local emit_v = stdio->Get(String::NewSymbol("emit")); + assert(emit_v->IsFunction()); + Local emit_f = Local::Cast(emit_v); + emit = Persistent::New(emit_f); + + if (isatty(STDOUT_FILENO)) { + // XXX selecting on tty fds wont work in windows. + // Must ALWAYS make a coupling on shitty platforms. + stdout_fd = STDOUT_FILENO; + } else { + stdout_coupling = coupling_new_push(STDOUT_FILENO); + stdout_fd = coupling_nonblocking_fd(stdout_coupling); + } + set_nonblock(stdout_fd); + + evcom_writer_init(&out); + out.on_close = detach_out; + evcom_writer_set(&out, stdout_fd); + evcom_writer_attach(EV_DEFAULT_ &out); +} diff --git a/src/node_stdio.h b/src/node_stdio.h new file mode 100644 index 000000000..cfd473f28 --- /dev/null +++ b/src/node_stdio.h @@ -0,0 +1,17 @@ +#ifndef node_stdio_h +#define node_stdio_h + +#include "node.h" + +#include +#include + +namespace node { + +class Stdio { +public: + static void Initialize (v8::Handle target); +}; + +} // namespace node +#endif diff --git a/src/util.js b/src/util.js index 67bccbb1f..a59f33b92 100644 --- a/src/util.js +++ b/src/util.js @@ -86,3 +86,19 @@ node.path = new function () { return parts[parts.length-1]; }; }; + +print = function (x) { + return node.stdio.write(x); +}; + +puts = function (x) { + return print(x.toString() + "\n"); +}; + +p = function (x) { + return puts(JSON.stringify(x)); +}; + +node.debug = function (x) { + return node.stdio.writeError("DEBUG: " + x.toString() + "\n"); +}; diff --git a/test/mjsunit/test-node-cat.js b/test/mjsunit/test-node-cat.js index 7e85965b2..1467e0103 100644 --- a/test/mjsunit/test-node-cat.js +++ b/test/mjsunit/test-node-cat.js @@ -1,12 +1,15 @@ include("mjsunit.js"); PORT = 8888; +puts("hello world"); + var body = "exports.A = function() { return 'A';}"; var server = node.http.createServer(function (req, res) { - res.sendHeader(200, [ - ["Content-Length", body.length], - ["Content-Type", "text/plain"] - ]); + puts("req?"); + res.sendHeader(200, { + "Content-Length": body.length, + "Content-Type": "text/plain" + }); res.sendBody(body); res.finish(); }); diff --git a/website/api.txt b/website/api.txt index b8aba191f..c4a4fa986 100644 --- a/website/api.txt +++ b/website/api.txt @@ -102,8 +102,7 @@ execution. === Helpers +puts(string)+:: -Alias for +stdout.puts()+. Outputs the +string+ and a trailing new-line to -+stdout+. +Outputs the +string+ and a trailing new-line to +stdout+. + Everything in node is asynchronous; +puts()+ is no exception. This might seem ridiculous but, if for example, one is piping +stdout+ into an NFS @@ -138,9 +137,6 @@ Immediately ends the process with the specified code. An array containing the command line arguments. -+stdout+, +stderr+, and +stdin+ :: -Objects of type +node.fs.File+. (See below.) - +__filename+ :: The filename of the script being executed. @@ -194,6 +190,37 @@ Adds a listener for the +"success"+ event. Returns the same promise object. Adds a listener for the +"error"+ event. Returns the same promise object. +=== Standard I/O + +Standard I/O is handled through a special object +node.stdio+. stdout and +stdin are fully non-blocking (even when piping to files). stderr is +synchronous. + +[cols="1,2,10",options="header"] +|========================================================= +| Event | Parameters | Notes + +| +"data"+ | +data+ | Made when stdin has received a chunk of data. + Depending on the encoding that stdin was opened + with, +data+ will be either an array of integers + (raw encoding) or a string (ascii or utf8 + encoding). This event will only be emited after + +node.stdio.open()+ has been called. +| +"close"+ | | Made when stdin has been closed. +|========================================================= + ++node.stdio.open(encoding="utf8")+:: +Open stdin. The program will not exit until +node.stdio.close()+ has been +called or the +"close"+ event has been emitted. + ++node.stdio.write(data)+:: +Write data to stdout. + ++node.stdio.writeError(data)+:: +Write data to stderr. Synchronous. + ++node.stdio.close()+:: +Close stdin. === Modules diff --git a/wscript b/wscript index 4c4c158c6..d2afe1b66 100644 --- a/wscript +++ b/wscript @@ -189,6 +189,16 @@ def build(bld): if bld.env["USE_DEBUG"]: http_parser.clone("debug") + ### coupling + coupling = bld.new_task_gen("cc", "staticlib") + coupling.source = "deps/coupling/coupling.c" + coupling.includes = "deps/coupling/" + coupling.name = "coupling" + coupling.target = "coupling" + coupling.install_path = None + if bld.env["USE_DEBUG"]: + coupling.clone("debug") + ### src/native.cc def javascript_in_c(task): env = task.env @@ -220,6 +230,7 @@ def build(bld): src/events.cc src/http.cc src/net.cc + src/node_stdio.cc src/dns.cc src/file.cc src/timer.cc @@ -234,8 +245,9 @@ def build(bld): deps/libeio deps/evcom deps/http_parser + deps/coupling """ - node.uselib_local = "evcom ev eio http_parser" + node.uselib_local = "evcom ev eio http_parser coupling" node.uselib = "UDNS V8 EXECINFO PROFILER EFENCE" node.install_path = '${PREFIX}/bin' node.chmod = 0755