initial
This commit is contained in:
367
node_modules/epoll/src/epoll.cc
generated
vendored
Normal file
367
node_modules/epoll/src/epoll.cc
generated
vendored
Normal file
@ -0,0 +1,367 @@
|
||||
#ifdef __linux__
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <unistd.h>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <uv.h>
|
||||
#include <v8.h>
|
||||
#include <node.h>
|
||||
#include <node_object_wrap.h>
|
||||
#include <node_version.h>
|
||||
#include <nan.h>
|
||||
#include "epoll.h"
|
||||
|
||||
// TODO - strerror isn't threadsafe, use strerror_r instead
|
||||
// TODO - use uv_strerror rather than strerror_r for libuv errors?
|
||||
|
||||
static int epfd_g;
|
||||
|
||||
static uv_sem_t sem_g;
|
||||
static uv_async_t async_g;
|
||||
|
||||
static struct epoll_event event_g;
|
||||
static int errno_g;
|
||||
|
||||
|
||||
/*
|
||||
* Watcher thread
|
||||
*/
|
||||
static void *watcher(void *arg) {
|
||||
int count;
|
||||
|
||||
while (true) {
|
||||
// Wait till the event loop says it's ok to poll. The semaphore serves more
|
||||
// than one purpose.
|
||||
// - It synchronizing access to '1 element queue' in variables
|
||||
// event_g and errno_g.
|
||||
// - When level-triggered epoll is used, the default when EPOLLET isn't
|
||||
// specified, the event triggered by the last call to epoll_wait may be
|
||||
// trigged again and again if the condition that triggered it hasn't been
|
||||
// cleared yet. Waiting prevents multiple triggers for the same event.
|
||||
// - It forces a context switch from the watcher thread to the event loop
|
||||
// thread.
|
||||
uv_sem_wait(&sem_g);
|
||||
|
||||
do {
|
||||
count = epoll_wait(epfd_g, &event_g, 1, -1);
|
||||
} while (count == -1 && errno == EINTR);
|
||||
|
||||
errno_g = count == -1 ? errno : 0;
|
||||
|
||||
// Errors returned from uv_async_send are silently ignored.
|
||||
uv_async_send(&async_g);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int start_watcher() {
|
||||
static bool watcher_started = false;
|
||||
pthread_t theread_id;
|
||||
|
||||
if (watcher_started)
|
||||
return 0;
|
||||
|
||||
epfd_g = epoll_create1(0);
|
||||
if (epfd_g == -1)
|
||||
return errno;
|
||||
|
||||
int err = uv_sem_init(&sem_g, 1);
|
||||
if (err < 0) {
|
||||
close(epfd_g);
|
||||
return -err;
|
||||
}
|
||||
|
||||
err = uv_async_init(uv_default_loop(), &async_g, Epoll::HandleEvent);
|
||||
if (err < 0) {
|
||||
close(epfd_g);
|
||||
uv_sem_destroy(&sem_g);
|
||||
return -err;
|
||||
}
|
||||
|
||||
// Prevent async_g from keeping event loop alive, for the time being.
|
||||
uv_unref((uv_handle_t *) &async_g);
|
||||
|
||||
err = pthread_create(&theread_id, 0, watcher, 0);
|
||||
if (err != 0) {
|
||||
close(epfd_g);
|
||||
uv_sem_destroy(&sem_g);
|
||||
uv_close((uv_handle_t *) &async_g, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
watcher_started = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Epoll
|
||||
*/
|
||||
Nan::Persistent<v8::Function> Epoll::constructor;
|
||||
std::map<int, Epoll*> Epoll::fd2epoll;
|
||||
|
||||
|
||||
Epoll::Epoll(Nan::Callback *callback)
|
||||
: callback_(callback), closed_(false) {
|
||||
};
|
||||
|
||||
|
||||
Epoll::~Epoll() {
|
||||
// v8 decides when and if destructors are called. In particular, if the
|
||||
// process is about to terminate, it's highly likely that destructors will
|
||||
// not be called. This is therefore not the place for calling the likes of
|
||||
// uv_unref, which, in general, must be called to terminate a process
|
||||
// gracefully!
|
||||
Nan::HandleScope scope;
|
||||
if (callback_) delete callback_;
|
||||
};
|
||||
|
||||
|
||||
NAN_MODULE_INIT(Epoll::Init) {
|
||||
// Constructor
|
||||
v8::Local<v8::FunctionTemplate> ctor =
|
||||
Nan::New<v8::FunctionTemplate>(Epoll::New);
|
||||
ctor->SetClassName(Nan::New("Epoll").ToLocalChecked());
|
||||
ctor->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
|
||||
// Prototype
|
||||
Nan::SetPrototypeMethod(ctor, "add", Add);
|
||||
Nan::SetPrototypeMethod(ctor, "modify", Modify);
|
||||
Nan::SetPrototypeMethod(ctor, "remove", Remove);
|
||||
Nan::SetPrototypeMethod(ctor, "close", Close);
|
||||
|
||||
v8::Local<v8::ObjectTemplate> itpl = ctor->InstanceTemplate();
|
||||
Nan::SetAccessor(itpl, Nan::New<v8::String>("closed").ToLocalChecked(),
|
||||
GetClosed);
|
||||
|
||||
Nan::SetTemplate(ctor, Nan::New<v8::String>("EPOLLIN").ToLocalChecked(),
|
||||
Nan::New<v8::Integer>(EPOLLIN), v8::ReadOnly);
|
||||
Nan::SetTemplate(ctor, Nan::New<v8::String>("EPOLLOUT").ToLocalChecked(),
|
||||
Nan::New<v8::Integer>(EPOLLOUT), v8::ReadOnly);
|
||||
Nan::SetTemplate(ctor, Nan::New<v8::String>("EPOLLRDHUP").ToLocalChecked(),
|
||||
Nan::New<v8::Integer>(EPOLLRDHUP), v8::ReadOnly);
|
||||
Nan::SetTemplate(ctor, Nan::New<v8::String>("EPOLLPRI").ToLocalChecked(),
|
||||
Nan::New<v8::Integer>(EPOLLPRI), v8::ReadOnly);
|
||||
Nan::SetTemplate(ctor, Nan::New<v8::String>("EPOLLERR").ToLocalChecked(),
|
||||
Nan::New<v8::Integer>(EPOLLERR), v8::ReadOnly);
|
||||
Nan::SetTemplate(ctor, Nan::New<v8::String>("EPOLLHUP").ToLocalChecked(),
|
||||
Nan::New<v8::Integer>(EPOLLHUP), v8::ReadOnly);
|
||||
Nan::SetTemplate(ctor, Nan::New<v8::String>("EPOLLET").ToLocalChecked(),
|
||||
Nan::New<v8::Integer>(EPOLLET), v8::ReadOnly);
|
||||
Nan::SetTemplate(ctor, Nan::New<v8::String>("EPOLLONESHOT").ToLocalChecked(),
|
||||
Nan::New<v8::Integer>(EPOLLONESHOT), v8::ReadOnly);
|
||||
|
||||
constructor.Reset(ctor->GetFunction());
|
||||
Nan::Set(target, Nan::New<v8::String>("Epoll").ToLocalChecked(),
|
||||
ctor->GetFunction());
|
||||
|
||||
// TODO - Is it a good idea to throw an exception here?
|
||||
if (int err = start_watcher())
|
||||
Nan::ThrowError(strerror(err)); // TODO - use err also
|
||||
}
|
||||
|
||||
|
||||
NAN_METHOD(Epoll::New) {
|
||||
if (info.Length() < 1 || !info[0]->IsFunction())
|
||||
return Nan::ThrowError("First argument to construtor must be a callback");
|
||||
|
||||
Nan::Callback *callback = new Nan::Callback(info[0].As<v8::Function>());
|
||||
|
||||
Epoll *epoll = new Epoll(callback);
|
||||
epoll->Wrap(info.This());
|
||||
|
||||
info.GetReturnValue().Set(info.This());
|
||||
}
|
||||
|
||||
|
||||
NAN_METHOD(Epoll::Add) {
|
||||
Epoll *epoll = ObjectWrap::Unwrap<Epoll>(info.This());
|
||||
|
||||
if (epoll->closed_)
|
||||
return Nan::ThrowError("add can't be called after calling close");
|
||||
|
||||
// Epoll.EPOLLET is -0x8000000 on ARM and an IsUint32 check fails so
|
||||
// check for IsNumber instead.
|
||||
if (info.Length() < 2 || !info[0]->IsInt32() || !info[1]->IsNumber())
|
||||
return Nan::ThrowError("incorrect arguments passed to add"
|
||||
"(int fd, int events)");
|
||||
|
||||
int err = epoll->Add(info[0]->Int32Value(), info[1]->Int32Value());
|
||||
if (err != 0)
|
||||
return Nan::ThrowError(strerror(err)); // TODO - use err also
|
||||
|
||||
info.GetReturnValue().Set(info.This());
|
||||
}
|
||||
|
||||
|
||||
NAN_METHOD(Epoll::Modify) {
|
||||
Epoll *epoll = ObjectWrap::Unwrap<Epoll>(info.This());
|
||||
|
||||
if (epoll->closed_)
|
||||
return Nan::ThrowError("modify can't be called after calling close");
|
||||
|
||||
// Epoll.EPOLLET is -0x8000000 on ARM and an IsUint32 check fails so
|
||||
// check for IsNumber instead.
|
||||
if (info.Length() < 2 || !info[0]->IsInt32() || !info[1]->IsNumber())
|
||||
return Nan::ThrowError("incorrect arguments passed to modify"
|
||||
"(int fd, int events)");
|
||||
|
||||
int err = epoll->Modify(info[0]->Int32Value(), info[1]->Int32Value());
|
||||
if (err != 0)
|
||||
return Nan::ThrowError(strerror(err)); // TODO - use err also
|
||||
|
||||
info.GetReturnValue().Set(info.This());
|
||||
}
|
||||
|
||||
|
||||
NAN_METHOD(Epoll::Remove) {
|
||||
Epoll *epoll = ObjectWrap::Unwrap<Epoll>(info.This());
|
||||
|
||||
if (epoll->closed_)
|
||||
return Nan::ThrowError("remove can't be called after calling close");
|
||||
|
||||
if (info.Length() < 1 || !info[0]->IsInt32())
|
||||
return Nan::ThrowError("incorrect arguments passed to remove(int fd)");
|
||||
|
||||
int err = epoll->Remove(info[0]->Int32Value());
|
||||
if (err != 0)
|
||||
return Nan::ThrowError(strerror(err)); // TODO - use err also
|
||||
|
||||
info.GetReturnValue().Set(info.This());
|
||||
}
|
||||
|
||||
|
||||
NAN_METHOD(Epoll::Close) {
|
||||
Epoll *epoll = ObjectWrap::Unwrap<Epoll>(info.This());
|
||||
|
||||
if (epoll->closed_)
|
||||
return Nan::ThrowError("close can't be called more than once");
|
||||
|
||||
int err = epoll->Close();
|
||||
if (err != 0)
|
||||
return Nan::ThrowError(strerror(err)); // TODO - use err also
|
||||
|
||||
info.GetReturnValue().SetNull();
|
||||
}
|
||||
|
||||
|
||||
NAN_GETTER(Epoll::GetClosed) {
|
||||
Epoll *epoll = ObjectWrap::Unwrap<Epoll>(info.This());
|
||||
|
||||
info.GetReturnValue().Set(Nan::New<v8::Boolean>(epoll->closed_));
|
||||
}
|
||||
|
||||
|
||||
int Epoll::Add(int fd, uint32_t events) {
|
||||
struct epoll_event event;
|
||||
event.events = events;
|
||||
event.data.fd = fd;
|
||||
|
||||
if (epoll_ctl(epfd_g, EPOLL_CTL_ADD, fd, &event) == -1)
|
||||
return errno;
|
||||
|
||||
fd2epoll.insert(std::pair<int, Epoll*>(fd, this));
|
||||
fds_.push_back(fd);
|
||||
|
||||
// Keep event loop alive. uv_unref called in Remove.
|
||||
uv_ref((uv_handle_t *) &async_g);
|
||||
|
||||
// Prevent GC for this instance. Unref called in Remove.
|
||||
Ref();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Epoll::Modify(int fd, uint32_t events) {
|
||||
struct epoll_event event;
|
||||
event.events = events;
|
||||
event.data.fd = fd;
|
||||
|
||||
if (epoll_ctl(epfd_g, EPOLL_CTL_MOD, fd, &event) == -1)
|
||||
return errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Epoll::Remove(int fd) {
|
||||
if (epoll_ctl(epfd_g, EPOLL_CTL_DEL, fd, 0) == -1)
|
||||
return errno;
|
||||
|
||||
fd2epoll.erase(fd);
|
||||
fds_.remove(fd);
|
||||
|
||||
if (fd2epoll.empty())
|
||||
uv_unref((uv_handle_t *) &async_g);
|
||||
Unref();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Epoll::Close() {
|
||||
closed_ = true;
|
||||
|
||||
delete callback_;
|
||||
callback_ = 0;
|
||||
|
||||
std::list<int>::iterator it = fds_.begin();
|
||||
for (; it != fds_.end(); it = fds_.begin()) {
|
||||
int err = Remove(*it);
|
||||
if (err != 0)
|
||||
return err; // TODO - Returning here may leave things messed up.
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void Epoll::HandleEvent(uv_async_t* handle) {
|
||||
// This method is executed in the event loop thread.
|
||||
// By the time flow of control arrives here the original Epoll instance that
|
||||
// registered interest in the event may no longer have this interest. If
|
||||
// this is the case, the event will be silently ignored.
|
||||
|
||||
std::map<int, Epoll*>::iterator it = fd2epoll.find(event_g.data.fd);
|
||||
if (it != fd2epoll.end()) {
|
||||
it->second->DispatchEvent(errno_g, &event_g);
|
||||
}
|
||||
|
||||
uv_sem_post(&sem_g);
|
||||
}
|
||||
|
||||
|
||||
void Epoll::DispatchEvent(int err, struct epoll_event *event) {
|
||||
Nan::HandleScope scope;
|
||||
Nan::AsyncResource resource("Epoll:DispatchEvent");
|
||||
|
||||
if (err) {
|
||||
v8::Local<v8::Value> args[1] = {
|
||||
v8::Exception::Error(
|
||||
Nan::New<v8::String>(strerror(err)).ToLocalChecked()
|
||||
)
|
||||
};
|
||||
callback_->Call(1, args, &resource);
|
||||
} else {
|
||||
v8::Local<v8::Value> args[3] = {
|
||||
Nan::Null(),
|
||||
Nan::New<v8::Integer>(event->data.fd),
|
||||
Nan::New<v8::Integer>(event->events)
|
||||
};
|
||||
callback_->Call(3, args, &resource);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NODE_MODULE(epoll, Epoll::Init)
|
||||
|
||||
#endif
|
||||
|
35
node_modules/epoll/src/epoll.h
generated
vendored
Normal file
35
node_modules/epoll/src/epoll.h
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef EPOLL_H
|
||||
#define EPOLL_H
|
||||
|
||||
class Epoll : public Nan::ObjectWrap {
|
||||
public:
|
||||
static NAN_MODULE_INIT(Init);
|
||||
static void HandleEvent(uv_async_t* handle);
|
||||
|
||||
private:
|
||||
Epoll(Nan::Callback *callback);
|
||||
~Epoll();
|
||||
|
||||
static NAN_METHOD(New);
|
||||
static NAN_METHOD(Add);
|
||||
static NAN_METHOD(Modify);
|
||||
static NAN_METHOD(Remove);
|
||||
static NAN_METHOD(Close);
|
||||
static NAN_GETTER(GetClosed);
|
||||
|
||||
int Add(int fd, uint32_t events);
|
||||
int Modify(int fd, uint32_t events);
|
||||
int Remove(int fd);
|
||||
int Close();
|
||||
void DispatchEvent(int err, struct epoll_event *event);
|
||||
|
||||
Nan::Callback *callback_;
|
||||
std::list<int> fds_;
|
||||
bool closed_;
|
||||
|
||||
static Nan::Persistent<v8::Function> constructor;
|
||||
static std::map<int, Epoll*> fd2epoll;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user