node/src/stream_wrap.cc
Fedor Indutny 30b7349176 stream_base: dispatch reqs in the stream impl
Dispatch requests in the implementation of the stream, not in the code
creating these requests. The requests might be piled up and invoked
internally in the implementation, so it should know better when it is
the time to dispatch them.

In fact, TLS was doing exactly this thing which led us to...

Fix: https://github.com/iojs/io.js/issues/1512
PR-URL: https://github.com/iojs/io.js/pull/1563
Reviewed-By: Shigeki Ohtsu <ohtsu@iij.ad.jp>
2015-05-01 13:52:11 +02:00

381 lines
9.9 KiB
C++

#include "stream_wrap.h"
#include "stream_base.h"
#include "stream_base-inl.h"
#include "env-inl.h"
#include "env.h"
#include "handle_wrap.h"
#include "node_buffer.h"
#include "node_counters.h"
#include "pipe_wrap.h"
#include "req-wrap.h"
#include "req-wrap-inl.h"
#include "tcp_wrap.h"
#include "udp_wrap.h"
#include "util.h"
#include "util-inl.h"
#include <stdlib.h> // abort()
#include <string.h> // memcpy()
#include <limits.h> // INT_MAX
namespace node {
using v8::Array;
using v8::Context;
using v8::EscapableHandleScope;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Handle;
using v8::HandleScope;
using v8::Integer;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::PropertyCallbackInfo;
using v8::String;
using v8::True;
using v8::Undefined;
using v8::Value;
void StreamWrap::Initialize(Handle<Object> target,
Handle<Value> unused,
Handle<Context> context) {
Environment* env = Environment::GetCurrent(context);
Local<FunctionTemplate> sw =
FunctionTemplate::New(env->isolate(), ShutdownWrap::NewShutdownWrap);
sw->InstanceTemplate()->SetInternalFieldCount(1);
sw->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "ShutdownWrap"));
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ShutdownWrap"),
sw->GetFunction());
Local<FunctionTemplate> ww =
FunctionTemplate::New(env->isolate(), WriteWrap::NewWriteWrap);
ww->InstanceTemplate()->SetInternalFieldCount(1);
ww->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "WriteWrap"));
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "WriteWrap"),
ww->GetFunction());
env->set_write_wrap_constructor_function(ww->GetFunction());
}
StreamWrap::StreamWrap(Environment* env,
Local<Object> object,
uv_stream_t* stream,
AsyncWrap::ProviderType provider,
AsyncWrap* parent)
: HandleWrap(env,
object,
reinterpret_cast<uv_handle_t*>(stream),
provider,
parent),
StreamBase(env),
stream_(stream) {
set_after_write_cb(OnAfterWriteImpl, this);
set_alloc_cb(OnAllocImpl, this);
set_read_cb(OnReadImpl, this);
}
void StreamWrap::AddMethods(Environment* env,
v8::Handle<v8::FunctionTemplate> target,
int flags) {
env->SetProtoMethod(target, "setBlocking", SetBlocking);
StreamBase::AddMethods<StreamWrap>(env, target, flags);
}
int StreamWrap::GetFD() {
int fd = -1;
#if !defined(_WIN32)
if (stream() != nullptr)
fd = stream()->io_watcher.fd;
#endif
return fd;
}
bool StreamWrap::IsAlive() {
return HandleWrap::IsAlive(this);
}
bool StreamWrap::IsClosing() {
return uv_is_closing(reinterpret_cast<uv_handle_t*>(stream()));
}
void* StreamWrap::Cast() {
return reinterpret_cast<void*>(this);
}
AsyncWrap* StreamWrap::GetAsyncWrap() {
return static_cast<AsyncWrap*>(this);
}
bool StreamWrap::IsIPCPipe() {
return is_named_pipe_ipc();
}
void StreamWrap::UpdateWriteQueueSize() {
HandleScope scope(env()->isolate());
Local<Integer> write_queue_size =
Integer::NewFromUnsigned(env()->isolate(), stream()->write_queue_size);
object()->Set(env()->write_queue_size_string(), write_queue_size);
}
int StreamWrap::ReadStart() {
return uv_read_start(stream(), OnAlloc, OnRead);
}
int StreamWrap::ReadStop() {
return uv_read_stop(stream());
}
void StreamWrap::OnAlloc(uv_handle_t* handle,
size_t suggested_size,
uv_buf_t* buf) {
StreamWrap* wrap = static_cast<StreamWrap*>(handle->data);
HandleScope scope(wrap->env()->isolate());
Context::Scope context_scope(wrap->env()->context());
CHECK_EQ(wrap->stream(), reinterpret_cast<uv_stream_t*>(handle));
return static_cast<StreamBase*>(wrap)->OnAlloc(suggested_size, buf);
}
void StreamWrap::OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx) {
buf->base = static_cast<char*>(malloc(size));
buf->len = size;
if (buf->base == nullptr && size > 0) {
FatalError(
"node::StreamWrap::DoAlloc(size_t, uv_buf_t*, void*)",
"Out Of Memory");
}
}
template <class WrapType, class UVType>
static Local<Object> AcceptHandle(Environment* env, StreamWrap* parent) {
EscapableHandleScope scope(env->isolate());
Local<Object> wrap_obj;
UVType* handle;
wrap_obj = WrapType::Instantiate(env, parent);
if (wrap_obj.IsEmpty())
return Local<Object>();
WrapType* wrap = Unwrap<WrapType>(wrap_obj);
handle = wrap->UVHandle();
if (uv_accept(parent->stream(), reinterpret_cast<uv_stream_t*>(handle)))
abort();
return scope.Escape(wrap_obj);
}
void StreamWrap::OnReadImpl(ssize_t nread,
const uv_buf_t* buf,
uv_handle_type pending,
void* ctx) {
StreamWrap* wrap = static_cast<StreamWrap*>(ctx);
Environment* env = wrap->env();
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());
Local<Object> pending_obj;
if (nread < 0) {
if (buf->base != nullptr)
free(buf->base);
wrap->EmitData(nread, Local<Object>(), pending_obj);
return;
}
if (nread == 0) {
if (buf->base != nullptr)
free(buf->base);
return;
}
char* base = static_cast<char*>(realloc(buf->base, nread));
CHECK_LE(static_cast<size_t>(nread), buf->len);
if (pending == UV_TCP) {
pending_obj = AcceptHandle<TCPWrap, uv_tcp_t>(env, wrap);
} else if (pending == UV_NAMED_PIPE) {
pending_obj = AcceptHandle<PipeWrap, uv_pipe_t>(env, wrap);
} else if (pending == UV_UDP) {
pending_obj = AcceptHandle<UDPWrap, uv_udp_t>(env, wrap);
} else {
CHECK_EQ(pending, UV_UNKNOWN_HANDLE);
}
wrap->EmitData(nread, Buffer::Use(env, base, nread), pending_obj);
}
void StreamWrap::OnReadCommon(uv_stream_t* handle,
ssize_t nread,
const uv_buf_t* buf,
uv_handle_type pending) {
StreamWrap* wrap = static_cast<StreamWrap*>(handle->data);
HandleScope scope(wrap->env()->isolate());
Context::Scope context_scope(wrap->env()->context());
// We should not be getting this callback if someone as already called
// uv_close() on the handle.
CHECK_EQ(wrap->persistent().IsEmpty(), false);
if (nread > 0) {
if (wrap->is_tcp()) {
NODE_COUNT_NET_BYTES_RECV(nread);
} else if (wrap->is_named_pipe()) {
NODE_COUNT_PIPE_BYTES_RECV(nread);
}
}
static_cast<StreamBase*>(wrap)->OnRead(nread, buf, pending);
}
void StreamWrap::OnRead(uv_stream_t* handle,
ssize_t nread,
const uv_buf_t* buf) {
StreamWrap* wrap = static_cast<StreamWrap*>(handle->data);
uv_handle_type type = UV_UNKNOWN_HANDLE;
if (wrap->is_named_pipe_ipc() &&
uv_pipe_pending_count(reinterpret_cast<uv_pipe_t*>(handle)) > 0) {
type = uv_pipe_pending_type(reinterpret_cast<uv_pipe_t*>(handle));
}
OnReadCommon(handle, nread, buf, type);
}
void StreamWrap::SetBlocking(const FunctionCallbackInfo<Value>& args) {
StreamWrap* wrap = Unwrap<StreamWrap>(args.Holder());
CHECK_GT(args.Length(), 0);
if (!wrap->IsAlive())
return args.GetReturnValue().Set(UV_EINVAL);
bool enable = args[0]->IsTrue();
args.GetReturnValue().Set(uv_stream_set_blocking(wrap->stream(), enable));
}
int StreamWrap::DoShutdown(ShutdownWrap* req_wrap) {
int err;
err = uv_shutdown(&req_wrap->req_, stream(), AfterShutdown);
req_wrap->Dispatched();
return err;
}
void StreamWrap::AfterShutdown(uv_shutdown_t* req, int status) {
ShutdownWrap* req_wrap = ContainerOf(&ShutdownWrap::req_, req);
HandleScope scope(req_wrap->env()->isolate());
Context::Scope context_scope(req_wrap->env()->context());
req_wrap->Done(status);
}
// NOTE: Call to this function could change both `buf`'s and `count`'s
// values, shifting their base and decrementing their length. This is
// required in order to skip the data that was successfully written via
// uv_try_write().
int StreamWrap::DoTryWrite(uv_buf_t** bufs, size_t* count) {
int err;
size_t written;
uv_buf_t* vbufs = *bufs;
size_t vcount = *count;
err = uv_try_write(stream(), vbufs, vcount);
if (err == UV_ENOSYS || err == UV_EAGAIN)
return 0;
if (err < 0)
return err;
// Slice off the buffers: skip all written buffers and slice the one that
// was partially written.
written = err;
for (; written != 0 && vcount > 0; vbufs++, vcount--) {
// Slice
if (vbufs[0].len > written) {
vbufs[0].base += written;
vbufs[0].len -= written;
written = 0;
break;
// Discard
} else {
written -= vbufs[0].len;
}
}
*bufs = vbufs;
*count = vcount;
return 0;
}
int StreamWrap::DoWrite(WriteWrap* w,
uv_buf_t* bufs,
size_t count,
uv_stream_t* send_handle) {
int r;
if (send_handle == nullptr) {
r = uv_write(&w->req_, stream(), bufs, count, AfterWrite);
} else {
r = uv_write2(&w->req_, stream(), bufs, count, send_handle, AfterWrite);
}
if (!r) {
size_t bytes = 0;
for (size_t i = 0; i < count; i++)
bytes += bufs[i].len;
if (stream()->type == UV_TCP) {
NODE_COUNT_NET_BYTES_SENT(bytes);
} else if (stream()->type == UV_NAMED_PIPE) {
NODE_COUNT_PIPE_BYTES_SENT(bytes);
}
}
w->Dispatched();
UpdateWriteQueueSize();
return r;
}
void StreamWrap::AfterWrite(uv_write_t* req, int status) {
WriteWrap* req_wrap = ContainerOf(&WriteWrap::req_, req);
HandleScope scope(req_wrap->env()->isolate());
Context::Scope context_scope(req_wrap->env()->context());
req_wrap->Done(status);
}
void StreamWrap::OnAfterWriteImpl(WriteWrap* w, void* ctx) {
StreamWrap* wrap = static_cast<StreamWrap*>(ctx);
wrap->UpdateWriteQueueSize();
}
} // namespace node
NODE_MODULE_CONTEXT_AWARE_BUILTIN(stream_wrap, node::StreamWrap::Initialize)