@ -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 |
@ -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(); | |||
} |
@ -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; | |||
} | |||
} |
@ -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); | |||
} | |||
} |
@ -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); | |||
} |