
commit
d6b2e72612
7 changed files with 523 additions and 0 deletions
-
8src/Makefile
-
243src/io/base.d
-
45src/io/base_c.c
-
BINsrc/io/base_c.o
-
177src/io/events.d
-
50src/io/events_c.c
-
BINsrc/io/events_c.o
@ -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); |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue