#if HAVE_GNUTLS
# include <gnutls/gnutls.h>
-#endif // HAVE_GNUTLS
/* a few forwards
* they wont even be defined if not having gnutls
* */
static int secure_full_goodbye (oi_socket *socket);
static int secure_half_goodbye (oi_socket *socket);
+#endif // HAVE_GNUTLS
#undef TRUE
#define TRUE 1
return buf;
}
-#define CLOSE_ASAP(socket) do { \
- if ((socket)->read_action) { \
- (socket)->read_action = full_close; \
- } \
- if ((socket)->write_action) { \
- (socket)->write_action = full_close; \
- } \
+#define CLOSE_ASAP(socket) do { \
+ (socket)->read_action = full_close; \
+ (socket)->write_action = full_close; \
} while (0)
static int
full_close(oi_socket *socket)
{
+ //printf("close(%d)\n", socket->fd);
if (close(socket->fd) == -1)
return errno == EINTR ? AGAIN : ERROR;
socket->read_action = NULL;
socket->write_action = NULL;
+ socket->fd = -1;
return OKAY;
}
{
int r = shutdown(socket->fd, SHUT_WR);
if (r == -1) {
- socket->errorno = errno;
- assert(0 && "Shouldn't get an error on shutdown");
- return ERROR;
+ switch (errno) {
+ case ENOTCONN:
+ socket->errorno = errno;
+ return ERROR;
+
+ default:
+ perror("shutdown()");
+ socket->errorno = errno;
+ assert(0 && "Shouldn't get an error on shutdown");
+ return ERROR;
+ }
}
socket->write_action = NULL;
return OKAY;
* a very complicated bunch of close logic!
* XXX this is awful. FIXME
*/
+ if (socket->write_action == full_close || socket->read_action == full_close)
+ return;
+
if (socket->got_half_close == FALSE) {
if (socket->got_full_close == FALSE) {
/* Normal situation. Didn't get any close signals. */
if (socket->on_connect) socket->on_connect(socket);
}
- if (socket->read_action)
+ if (socket->read_action == secure_handshake)
socket->read_action = secure_socket_recv;
- if (socket->write_action)
+ if (socket->write_action == secure_handshake)
socket->write_action = secure_socket_send;
return OKAY;
if (recved >= 0) {
/* Got EOF */
- if (recved == 0)
+ if (recved == 0) {
socket->read_action = NULL;
+ if (socket->write_action == NULL)
+ CLOSE_ASAP(socket);
+ }
if (socket->write_action)
socket->write_action = secure_socket_send;
default:
perror("send()");
+ printf("%p had send error\n", socket);
assert(0 && "oi shouldn't let this happen.");
socket->errorno = errno;
return ERROR;
default:
perror("recv()");
- printf("unmatched errno %d\n", errno);
+ printf("unmatched errno %d %s\n\n", errno, strerror(errno));
assert(0 && "recv returned error that oi should have caught before.");
return ERROR;
}
if (recved == 0) {
oi_socket_read_stop(socket);
socket->read_action = NULL;
+ if (socket->write_action == NULL)
+ CLOSE_ASAP(socket);
}
/* NOTE: EOF is signaled with recved == 0 on callback */
void oi_socket_force_close (oi_socket *socket)
{
+ release_write_buffer(socket);
+
+ ev_clear_pending (EV_A_ &socket->write_watcher);
+ ev_clear_pending (EV_A_ &socket->read_watcher);
+ ev_clear_pending (EV_A_ &socket->timeout_watcher);
+
+ socket->write_action = socket->read_action = NULL;
// socket->errorno = OI_SOCKET_ERROR_FORCE_CLOSE
- CLOSE_ASAP(socket);
+ close(socket->fd);
+
+ socket->fd = -1;
+
+ oi_socket_detach(socket);
}
void
String::Utf8Value new_path(args[1]->ToString());
node::eio_warmup();
- eio_req *req = eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, NULL);
+ eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, NULL);
return Undefined();
}
String::Utf8Value path(args[0]->ToString());
node::eio_warmup();
- eio_req *req = eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, NULL);
+ eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, NULL);
return Undefined();
}
{
Handle<Value> fd_value = handle_->Get(FD_SYMBOL);
int fd = fd_value->IntegerValue();
+ return fd;
}
Handle<Value>
int fd = file->GetFD();
node::eio_warmup();
- eio_req *req = eio_close (fd, EIO_PRI_DEFAULT, File::AfterClose, file);
-
+ eio_close (fd, EIO_PRI_DEFAULT, File::AfterClose, file);
+ file->Attach();
return Undefined();
}
Local<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
CallTopCallback(file->handle_, argc, argv);
-
+ file->Detach();
return 0;
}
// TODO how should the mode be set?
node::eio_warmup();
- eio_req *req = eio_open (*path, flags, 0666, EIO_PRI_DEFAULT, File::AfterOpen, file);
-
+ eio_open (*path, flags, 0666, EIO_PRI_DEFAULT, File::AfterOpen, file);
+ file->Attach();
return Undefined();
}
argv[0] = Integer::New(req->errorno);
CallTopCallback(file->handle_, argc, argv);
+ file->Detach();
return 0;
}
Local<Array> array = Local<Array>::Cast(args[0]);
length = array->Length();
buf = static_cast<char*>(malloc(length));
- for (int i = 0; i < length; i++) {
+ for (unsigned int i = 0; i < length; i++) {
Local<Value> int_value = array->Get(Integer::New(i));
buf[i] = int_value->Int32Value();
}
int fd = file->GetFD();
node::eio_warmup();
- eio_req *req = eio_write(fd, buf, length, pos, EIO_PRI_DEFAULT, File::AfterWrite, file);
+ eio_write(fd, buf, length, pos, EIO_PRI_DEFAULT, File::AfterWrite, file);
+ file->Attach();
return Undefined();
}
//char *buf = static_cast<char*>(req->ptr2);
free(req->ptr2);
- size_t written = req->result;
+ ssize_t written = req->result;
HandleScope scope;
argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0);
CallTopCallback(file->handle_, argc, argv);
+ file->Detach();
return 0;
}
// NOTE: NULL pointer tells eio to allocate it itself
node::eio_warmup();
- eio_req *req = eio_read(fd, NULL, length, pos, EIO_PRI_DEFAULT, File::AfterRead, file);
- assert(req);
+ eio_read(fd, NULL, length, pos, EIO_PRI_DEFAULT, File::AfterRead, file);
+ file->Attach();
return Undefined();
}
} else {
// raw encoding
Local<Array> array = Array::New(length);
- for (int i = 0; i < length; i++) {
+ for (unsigned int i = 0; i < length; i++) {
array->Set(Integer::New(i), Integer::New(buf[i]));
}
argv[1] = array;
}
}
CallTopCallback(file->handle_, argc, argv);
+
+ file->Detach();
return 0;
}
Connection::~Connection ()
{
- handle_->Delete(SEND_SYMBOL);
- Close();
+ static int i = 0;
+ if(socket_.fd > 0) {
+ printf("garbage collecting open Connection : %d\n", i++);
+ printf(" socket->read_action: %p\n", socket_.read_action);
+ printf(" socket->write_action: %p\n", socket_.write_action);
+ }
+ ForceClose();
}
Local<Object>
{
HandleScope scope;
handle_->Set(SERVER_SYMBOL, acceptor_handle);
+
+ Attach();
}
Handle<Value>
, Connection::AfterResolve
, connection
);
+
+ connection->Attach();
+
return Undefined();
}
}
connection->OnDisconnect();
+ connection->Detach();
return 0;
}
HandleScope scope;
Connection *connection = NODE_UNWRAP(Connection, args.Holder());
connection->ForceClose();
+ //connection->Detach();
return Undefined();
}
static void on_close (oi_socket *s) {
Connection *connection = static_cast<Connection*> (s->data);
connection->OnDisconnect();
+ connection->Detach();
}
static void on_timeout (oi_socket *s) {
Acceptor (v8::Handle<v8::Object> handle,
v8::Handle<v8::Function> protocol_class,
v8::Handle<v8::Object> options);
- virtual ~Acceptor () { Close(); }
+ virtual ~Acceptor () { Close(); puts("acceptor gc'd!");}
v8::Local<v8::Function> GetProtocolClass (void);
int r = oi_server_listen (&server_, address);
if(r != 0) return r;
oi_server_attach (EV_DEFAULT_ &server_);
+ Attach();
return 0;
}
void Close ( ) {
oi_server_close (&server_);
+ Detach();
}
virtual Connection* OnConnection (struct sockaddr *addr, socklen_t len);
ObjectWrap::~ObjectWrap ( )
{
- if (!handle_.IsEmpty()) {
- handle_->SetInternalField(0, Undefined());
- handle_.Dispose();
- handle_.Clear();
- }
+ handle_->SetInternalField(0, Undefined());
+ handle_.Dispose();
+ handle_.Clear();
}
ObjectWrap::ObjectWrap (Handle<Object> handle)
Handle<External> external = External::New(this);
handle_->SetInternalField(0, external);
handle_.MakeWeak(this, ObjectWrap::MakeWeak);
+
+ attach_count_ = 0;
+ weak_ = false;
+}
+
+void
+ObjectWrap::Attach ()
+{
+ attach_count_ += 1;
+}
+
+void
+ObjectWrap::Detach ()
+{
+ attach_count_ -= 1;
+ assert(attach_count_ >= 0);
+
+ if(weak_ && attach_count_ == 0)
+ delete this;
}
void*
void
ObjectWrap::MakeWeak (Persistent<Value> _, void *data)
{
- ObjectWrap *w = static_cast<ObjectWrap*> (data);
- delete w;
+ ObjectWrap *obj = static_cast<ObjectWrap*> (data);
+ obj->weak_ = true;
+ if (obj->attach_count_ == 0)
+ delete obj;
}
// Extracts a C string from a V8 Utf8Value.
static void* Unwrap (v8::Handle<v8::Object> handle);
v8::Persistent<v8::Object> handle_;
+ /* Attach() marks the object as being attached to an event loop.
+ * Attached objects will not be garbage collected, even if
+ * all references are lost.
+ */
+ void Attach();
+ /* Detach() marks an object as detached from the event loop. This is its
+ * default state. When an object with a "weak" reference changes from
+ * attached to detached state it will be freed. Be careful not to access
+ * the object after making this call as it might be gone!
+ * (A "weak reference" is v8 terminology for an object who only has a
+ * persistant handle.)
+ */
+ void Detach();
+
private:
static void MakeWeak (v8::Persistent<v8::Value> _, void *data);
+ int attach_count_;
+ bool weak_;
};
} // namespace node
#include <assert.h>
using namespace v8;
+using namespace node;
-class Timer : node::ObjectWrap {
+class Timer : ObjectWrap {
public:
Timer(Handle<Object> handle, Handle<Function> callback, ev_tstamp after, ev_tstamp repeat);
~Timer();
TryCatch try_catch;
callback->Call (timer->handle_, 0, NULL);
if (try_catch.HasCaught())
- node::fatal_exception(try_catch);
+ fatal_exception(try_catch);
+
+ /* XXX i'm a bit worried if this is the correct test?
+ * it's rather crutial for memory leaks the conditional here test to see
+ * if the watcher will make another callback.
+ */
+ if (false == ev_is_active(&timer->watcher_))
+ timer->Detach();
}
Timer::Timer (Handle<Object> handle, Handle<Function> callback, ev_tstamp after, ev_tstamp repeat)
watcher_.data = this;
ev_timer_start(EV_DEFAULT_UC_ &watcher_);
+ Attach();
}
Timer::~Timer ()
{
Timer *timer = NODE_UNWRAP(Timer, args.Holder());
ev_timer_start(EV_DEFAULT_UC_ &timer->watcher_);
+ timer->Attach();
return Undefined();
}
{
Timer *timer = NODE_UNWRAP(Timer, args.Holder());
ev_timer_stop(EV_DEFAULT_UC_ &timer->watcher_);
+ timer->Detach();
return Undefined();
}