Initial commit
This commit is contained in:
commit
d6b2e72612
8
src/Makefile
Normal file
8
src/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
LDC=ldc
|
||||
|
||||
override DFLAGS+=-O2
|
||||
override CFLAGS+=-O2
|
||||
|
||||
delight: io/base.d io/base_c.o io/events.d io/events_c.o
|
||||
gdc $(DFLAGS) -o "$@" $^ -lev
|
243
src/io/base.d
Normal file
243
src/io/base.d
Normal file
@ -0,0 +1,243 @@
|
||||
|
||||
module io.base;
|
||||
|
||||
import std.socket;
|
||||
|
||||
import io.events;
|
||||
|
||||
R delegate(T) makeDelegate(R, T)(R function(T) f) {
|
||||
struct delegateClass {
|
||||
R function(T) m_f;
|
||||
|
||||
R callback(T t) { return m_f(t); }
|
||||
}
|
||||
|
||||
delegateClass* o = new delegateClass;
|
||||
o.m_f = f;
|
||||
return &o.callback;
|
||||
}
|
||||
|
||||
private extern (C) int iobase_write(int fd, void *buf, int len);
|
||||
private extern (C) int iobase_read(int fd, void *buf, int len);
|
||||
|
||||
class BufferedSocket {
|
||||
private Socket m_socket;
|
||||
private IOWatcher m_watcher;
|
||||
private void delegate(void[] buf) m_readcb;
|
||||
private void delegate() m_eofcb, m_closecb;
|
||||
|
||||
private bool m_closed, m_eof, m_sendeof;
|
||||
|
||||
private class chunk {
|
||||
chunk next;
|
||||
void[] buf;
|
||||
this(void[] b) { buf = b.dup; }
|
||||
}
|
||||
private chunk m_cfirst, m_clast;
|
||||
|
||||
final void delegate(void[] buf) readcb() { return m_readcb; }
|
||||
final void delegate(void[] buf) readcb(void delegate(void[] buf) r) {
|
||||
m_readcb = r;
|
||||
if (m_readcb is null || m_eof || m_closed) {
|
||||
m_watcher.events = m_watcher.events & ~Event.READ;
|
||||
} else {
|
||||
m_watcher.events = m_watcher.events | Event.READ;
|
||||
}
|
||||
return m_readcb;
|
||||
}
|
||||
|
||||
final void delegate() eofcb() { return m_eofcb; }
|
||||
final void delegate() eofcb(void delegate() f) { return m_eofcb = f; }
|
||||
|
||||
final void delegate() closecb() { return m_closecb; }
|
||||
final void delegate() closecb(void delegate() f) { return m_closecb = f; }
|
||||
|
||||
this(Loop loop, Socket sock, void delegate(void[] buf) readcb = null) {
|
||||
m_socket = sock;
|
||||
m_readcb = readcb;
|
||||
|
||||
m_watcher = new IOWatcher();
|
||||
m_watcher.socket = m_socket;
|
||||
m_watcher.events = Event.READ;
|
||||
m_watcher.read = &this.handleread;
|
||||
m_watcher.write = &this.handlewrite;
|
||||
m_watcher.start(loop);
|
||||
}
|
||||
|
||||
~this() {
|
||||
m_cfirst = m_clast = null;
|
||||
shutdown();
|
||||
delete m_watcher;
|
||||
delete m_socket;
|
||||
}
|
||||
|
||||
void write(void[] buf) {
|
||||
if (m_closed || m_sendeof || 0 == buf.length) return;
|
||||
chunk c = new chunk(buf);
|
||||
if (m_clast !is null) {
|
||||
m_clast.next = c;
|
||||
m_clast = c;
|
||||
} else {
|
||||
m_cfirst = m_clast = c;
|
||||
}
|
||||
m_watcher.events = m_watcher.events | Event.WRITE;
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
if (m_closed || m_sendeof) return;
|
||||
m_sendeof = true;
|
||||
if (m_cfirst is null) {
|
||||
m_socket.shutdown(SocketShutdown.SEND);
|
||||
}
|
||||
}
|
||||
|
||||
/* may destroy object */
|
||||
private final void handleeof() {
|
||||
m_eof = true;
|
||||
m_watcher.events = m_watcher.events & ~Event.READ;
|
||||
if (m_eofcb !is null) {
|
||||
m_eofcb();
|
||||
} else {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
/* may destroy object */
|
||||
private final void handleclose() {
|
||||
m_closed = true;
|
||||
m_watcher.events = Event.NONE;
|
||||
if (m_closecb !is null) {
|
||||
m_closecb();
|
||||
} else {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
private final void handleread() {
|
||||
if (m_readcb is null) {
|
||||
m_watcher.events = m_watcher.events & ~Event.READ;
|
||||
return;
|
||||
}
|
||||
ubyte buf[4096];
|
||||
int res = iobase_read(m_socket.handle, buf.ptr, buf.length);
|
||||
if (res == 0) {
|
||||
handleeof();
|
||||
} else if (res > 0) {
|
||||
m_readcb(buf[0 .. res]);
|
||||
} else if (res != -1) {
|
||||
handleclose();
|
||||
}
|
||||
}
|
||||
private final void handlewrite() {
|
||||
while (m_cfirst !is null) {
|
||||
chunk c = m_cfirst;
|
||||
int res = iobase_write(m_socket.handle, c.buf.ptr, c.buf.length);
|
||||
if (res > 0) {
|
||||
if (res == c.buf.length) {
|
||||
m_cfirst = c.next;
|
||||
} else {
|
||||
c.buf = c.buf[res .. $];
|
||||
return;
|
||||
}
|
||||
} else if (res == -1) {
|
||||
return;
|
||||
} else {
|
||||
handleclose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (m_sendeof) {
|
||||
m_socket.shutdown(SocketShutdown.SEND);
|
||||
}
|
||||
m_clast = null;
|
||||
m_watcher.events = m_watcher.events & ~Event.WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
class TcpServer {
|
||||
private Socket m_socket;
|
||||
private IOWatcher m_watcher;
|
||||
private void delegate(Socket con) m_accept_cb;
|
||||
|
||||
public final Socket socket() { return m_socket; }
|
||||
|
||||
this(Loop loop, Address bind, void delegate(Socket con) accept) {
|
||||
m_accept_cb = accept;
|
||||
|
||||
m_socket = new TcpSocket(bind.addressFamily);
|
||||
m_socket.blocking = false;
|
||||
m_socket.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, 1);
|
||||
// m_socket.setOption(SocketOptionLevel.IPV6, SocketOption.IPV6_V6ONLY, 1);
|
||||
m_socket.bind(bind);
|
||||
m_socket.listen(1024);
|
||||
|
||||
m_watcher = new IOWatcher();
|
||||
m_watcher.socket = m_socket;
|
||||
m_watcher.events = Event.READ;
|
||||
m_watcher.read = &this.acceptcb;
|
||||
m_watcher.start(loop);
|
||||
}
|
||||
|
||||
private final void acceptcb() {
|
||||
Socket con = m_socket.accept();
|
||||
if (con !is null) m_accept_cb(con);
|
||||
}
|
||||
}
|
||||
|
||||
class EchoConnection {
|
||||
protected int m_idx = -1;
|
||||
protected EchoServer m_srv;
|
||||
protected Socket m_socket;
|
||||
protected BufferedSocket m_bufsock;
|
||||
|
||||
this(Socket s) {
|
||||
m_socket = s;
|
||||
m_bufsock = new BufferedSocket(Loop.defaultloop, s, &this.handleread);
|
||||
}
|
||||
|
||||
~this() {
|
||||
if (m_srv !is null) m_srv.delCon(this);
|
||||
}
|
||||
|
||||
private final void handleread(void[] buf) {
|
||||
m_bufsock.write(buf);
|
||||
}
|
||||
};
|
||||
|
||||
class EchoServer {
|
||||
private EchoConnection m_con[];
|
||||
private Loop m_loop;
|
||||
private TcpServer m_server;
|
||||
|
||||
this(Loop loop, Address address) {
|
||||
m_loop = loop;
|
||||
m_server = new TcpServer(m_loop, address, &this.accept);
|
||||
m_con = new EchoConnection[0];
|
||||
}
|
||||
|
||||
protected final void addCon(EchoConnection con) {
|
||||
con.m_srv = this;
|
||||
con.m_idx = m_con.length;
|
||||
m_con.length = m_con.length + 1;
|
||||
m_con[con.m_idx] = con;
|
||||
}
|
||||
|
||||
protected final void delCon(EchoConnection con) {
|
||||
if (con.m_srv !is this) return;
|
||||
m_con[con.m_idx] = m_con[m_con.length - 1];
|
||||
m_con.length = m_con.length - 1;
|
||||
con.m_srv = null;
|
||||
con.m_idx = -1;
|
||||
}
|
||||
|
||||
private final void accept(Socket s) {
|
||||
auto con = new EchoConnection(s);
|
||||
addCon(con);
|
||||
}
|
||||
};
|
||||
|
||||
void main(string args[]) {
|
||||
Loop loop = Loop.defaultloop;
|
||||
EchoServer srv = new EchoServer(loop, new InternetAddress(8080));
|
||||
Loop.defaultloop.run();
|
||||
}
|
45
src/io/base_c.c
Normal file
45
src/io/base_c.c
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* -1: not now (try later), -2: con closed, -3: unexpected error */
|
||||
|
||||
int iobase_write(int fd, const void* buf, int len) {
|
||||
int res = write(fd, buf, len);
|
||||
if (res >= 0) return res;
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
#if EAGAIN != EWOULDBLOCK
|
||||
case EWOULDBLOCK:
|
||||
#endif
|
||||
case EINTR:
|
||||
return -1;
|
||||
break;
|
||||
case ECONNRESET:
|
||||
case EPIPE:
|
||||
case ETIMEDOUT:
|
||||
return -2;
|
||||
default:
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
/* 0: eof, -1: not now (try later), -2: con closed, -3: unexpected error */
|
||||
int iobase_read(int fd, void* buf, int len) {
|
||||
int res = read(fd, buf, len);
|
||||
if (res >= 0) return res;
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
#if EAGAIN != EWOULDBLOCK
|
||||
case EWOULDBLOCK:
|
||||
#endif
|
||||
case EINTR:
|
||||
return -1;
|
||||
break;
|
||||
case ECONNRESET:
|
||||
case ETIMEDOUT:
|
||||
return -2;
|
||||
default:
|
||||
return -3;
|
||||
}
|
||||
}
|
BIN
src/io/base_c.o
Normal file
BIN
src/io/base_c.o
Normal file
Binary file not shown.
177
src/io/events.d
Normal file
177
src/io/events.d
Normal file
@ -0,0 +1,177 @@
|
||||
|
||||
module io.events;
|
||||
|
||||
import std.socket;
|
||||
import std.outofmemory;
|
||||
|
||||
private struct evloop {
|
||||
}
|
||||
|
||||
private struct ev_io {
|
||||
}
|
||||
|
||||
enum Event {
|
||||
NONE = 0,
|
||||
READ = 1,
|
||||
WRITE = 2,
|
||||
TIMEOUT = 4,
|
||||
ERROR = 128,
|
||||
}
|
||||
|
||||
private extern (C) void ioev_event_callback(Watcher watcher, int revents) {
|
||||
watcher.callback(revents);
|
||||
}
|
||||
|
||||
private extern (C) evloop* ioev_ev_default_loop(uint flags); /* is a inline function... */
|
||||
private extern (C) evloop* ev_loop_new(uint flags);
|
||||
private extern (C) void ev_default_destroy(evloop *loop);
|
||||
private extern (C) void ev_loop_destroy(evloop *loop);
|
||||
private extern (C) void ev_loop(evloop *loop, int flags);
|
||||
|
||||
private extern (C) void ev_io_start(evloop *loop, ev_io *w);
|
||||
private extern (C) void ev_io_stop(evloop *loop, ev_io *w);
|
||||
|
||||
private extern (C) ev_io* ioev_io_watcher_new(Watcher watcher);
|
||||
private extern (C) void ioev_io_watcher_free(ev_io *w);
|
||||
private extern (C) void ioev_io_set(ev_io *w, int fd, int events);
|
||||
|
||||
class Watcher {
|
||||
protected Loop m_cur_loop = null;
|
||||
protected bool m_active = false;
|
||||
|
||||
protected final Loop loop(Loop l) {
|
||||
if (m_cur_loop is l) return l;
|
||||
if (m_cur_loop !is null) stop();
|
||||
return m_cur_loop = l;
|
||||
}
|
||||
|
||||
~this() {
|
||||
loop = null;
|
||||
}
|
||||
|
||||
abstract void callback(int revents);
|
||||
abstract protected void do_start();
|
||||
abstract protected void do_stop();
|
||||
|
||||
final public Loop loop() {
|
||||
return m_cur_loop;
|
||||
}
|
||||
|
||||
final public void start(Loop l) {
|
||||
if (l is null) throw new Exception("Cannot start on null loop");
|
||||
loop = l;
|
||||
do_start();
|
||||
m_active = true;
|
||||
}
|
||||
|
||||
final public void start() {
|
||||
start(m_cur_loop);
|
||||
}
|
||||
|
||||
final public void stop() {
|
||||
if (!m_active) return;
|
||||
if (m_cur_loop !is null && m_cur_loop.loop !is null)
|
||||
do_stop();
|
||||
m_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
class IOWatcher : Watcher {
|
||||
private ev_io *m_w;
|
||||
private Socket m_socket;
|
||||
private Event m_events;
|
||||
private int m_fd = -1;
|
||||
|
||||
void delegate() read, write, error;
|
||||
|
||||
this() {
|
||||
m_w = ioev_io_watcher_new(this);
|
||||
if (m_w is null) throw new OutOfMemoryException;
|
||||
}
|
||||
~this() {
|
||||
ioev_io_watcher_free(m_w);
|
||||
}
|
||||
|
||||
override final void callback(int revents) {
|
||||
if (revents & Event.READ) read();
|
||||
if (revents & Event.WRITE) write();
|
||||
if (revents & Event.ERROR) error();
|
||||
}
|
||||
|
||||
private final void set(int fd, int ev) {
|
||||
bool active = m_active;
|
||||
stop();
|
||||
m_fd = fd; m_events = cast(Event)(ev);
|
||||
ioev_io_set(m_w, m_fd, m_events);
|
||||
if (active) start();
|
||||
}
|
||||
|
||||
final Socket socket(Socket s) {
|
||||
m_socket = s;
|
||||
if (m_fd != s.handle)
|
||||
set(s.handle, m_events);
|
||||
return m_socket;
|
||||
}
|
||||
|
||||
final Event events() { return m_events; }
|
||||
|
||||
final Event events(int ev) {
|
||||
if (m_events != ev) set(m_fd, ev);
|
||||
return m_events;
|
||||
}
|
||||
|
||||
final void events(bool waitForRead, bool waitForWrite) {
|
||||
Event ev = Event.NONE;
|
||||
if (waitForRead) ev = ev | Event.READ;
|
||||
if (waitForWrite) ev = ev | Event.WRITE;
|
||||
events(ev);
|
||||
}
|
||||
|
||||
override final protected void do_start() {
|
||||
ev_io_start(loop.loop, m_w);
|
||||
}
|
||||
|
||||
override final protected void do_stop() {
|
||||
ev_io_stop(loop.loop, m_w);
|
||||
}
|
||||
}
|
||||
|
||||
class Loop {
|
||||
protected evloop* loop;
|
||||
private bool default_loop;
|
||||
|
||||
private static Loop def = null;
|
||||
|
||||
private this(evloop* loop) {
|
||||
this.loop = loop;
|
||||
default_loop = true;
|
||||
}
|
||||
|
||||
static synchronized Loop defaultloop() {
|
||||
if (def !is null) return def;
|
||||
evloop* l = ioev_ev_default_loop(0);
|
||||
if (l is null) throw new Exception("ev_default_loop failed");
|
||||
def = new Loop(l);
|
||||
return def;
|
||||
}
|
||||
|
||||
this() {
|
||||
default_loop = false;
|
||||
loop = ev_loop_new(0);
|
||||
if (loop is null) throw new Exception("ev_loop_new failed");
|
||||
}
|
||||
|
||||
~this() {
|
||||
if (loop !is null) {
|
||||
if (default_loop) {
|
||||
ev_default_destroy(loop);
|
||||
} else {
|
||||
ev_loop_destroy(loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final void run() {
|
||||
ev_loop(loop, 0);
|
||||
}
|
||||
}
|
50
src/io/events_c.c
Normal file
50
src/io/events_c.c
Normal file
@ -0,0 +1,50 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <ev.h>
|
||||
|
||||
#define UNUSED(x) ((void)(x))
|
||||
|
||||
#define IOEV_NONE 0
|
||||
#define IOEV_READ 1
|
||||
#define IOEV_WRITE 2
|
||||
#define IOEV_TIMEOUT 4
|
||||
#define IOEV_ERROR 128
|
||||
|
||||
void ioev_event_callback(void* watcher, int revents);
|
||||
|
||||
struct ev_loop *ioev_ev_default_loop (unsigned int flags) {
|
||||
return ev_default_loop(flags);
|
||||
}
|
||||
|
||||
static void io_cb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
int ev = 0;
|
||||
UNUSED(loop);
|
||||
|
||||
if (revents & EV_READ) ev = ev | IOEV_READ;
|
||||
if (revents & EV_WRITE) ev = ev | IOEV_WRITE;
|
||||
if (revents & EV_ERROR) ev = ev | IOEV_ERROR;
|
||||
|
||||
ioev_event_callback(w->data, ev);
|
||||
}
|
||||
|
||||
ev_io* ioev_io_watcher_new(void *watcher) {
|
||||
ev_io *w = malloc(sizeof(ev_io));
|
||||
ev_io_init(w, io_cb, -1, 0);
|
||||
w->data = watcher;
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
void ioev_io_watcher_free(ev_io *w) {
|
||||
free(w);
|
||||
}
|
||||
|
||||
void ioev_io_set(ev_io *w, int fd, int ev) {
|
||||
int events = 0;
|
||||
|
||||
if (ev & IOEV_READ) events = events | EV_READ;
|
||||
if (ev & IOEV_WRITE) events = events | EV_WRITE;
|
||||
|
||||
ev_io_set(w, fd, events);
|
||||
}
|
BIN
src/io/events_c.o
Normal file
BIN
src/io/events_c.o
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user