Rename Vector to Array

For consistency with Phobos.
This commit is contained in:
Eugen Wissner 2017-05-16 12:12:57 +02:00
parent 58664570f9
commit 8c42cbfd63
13 changed files with 3033 additions and 3011 deletions

View File

@ -18,7 +18,7 @@ import tanya.async.event.selector;
import tanya.async.loop; import tanya.async.loop;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.vector; import tanya.container.array;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
@ -29,153 +29,153 @@ import std.algorithm.comparison;
extern (C) nothrow @nogc extern (C) nothrow @nogc
{ {
int epoll_create1(int flags); int epoll_create1(int flags);
int epoll_ctl (int epfd, int op, int fd, epoll_event *event); int epoll_ctl (int epfd, int op, int fd, epoll_event *event);
int epoll_wait (int epfd, epoll_event *events, int maxevents, int timeout); int epoll_wait (int epfd, epoll_event *events, int maxevents, int timeout);
} }
final class EpollLoop : SelectorLoop final class EpollLoop : SelectorLoop
{ {
protected int fd; protected int fd;
private Vector!epoll_event events; private Array!epoll_event events;
/** /**
* Initializes the loop. * Initializes the loop.
*/ */
this() @nogc this() @nogc
{ {
if ((fd = epoll_create1(EPOLL_CLOEXEC)) < 0) if ((fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
{ {
throw defaultAllocator.make!BadLoopException("epoll initialization failed"); throw defaultAllocator.make!BadLoopException("epoll initialization failed");
} }
super(); super();
events = Vector!epoll_event(maxEvents, MmapPool.instance); events = Array!epoll_event(maxEvents, MmapPool.instance);
} }
/** /**
* Frees loop internals. * Frees loop internals.
*/ */
~this() @nogc ~this() @nogc
{ {
close(fd); close(fd);
} }
/** /**
* Should be called if the backend configuration changes. * Should be called if the backend configuration changes.
* *
* Params: * Params:
* watcher = Watcher. * watcher = Watcher.
* oldEvents = The events were already set. * oldEvents = The events were already set.
* events = The events should be set. * events = The events should be set.
* *
* Returns: $(D_KEYWORD true) if the operation was successful. * Returns: $(D_KEYWORD true) if the operation was successful.
*/ */
protected override bool reify(SocketWatcher watcher, protected override bool reify(SocketWatcher watcher,
EventMask oldEvents, EventMask oldEvents,
EventMask events) @nogc EventMask events) @nogc
{ {
int op = EPOLL_CTL_DEL; int op = EPOLL_CTL_DEL;
epoll_event ev; epoll_event ev;
if (events == oldEvents) if (events == oldEvents)
{ {
return true; return true;
} }
if (events && oldEvents) if (events && oldEvents)
{ {
op = EPOLL_CTL_MOD; op = EPOLL_CTL_MOD;
} }
else if (events && !oldEvents) else if (events && !oldEvents)
{ {
op = EPOLL_CTL_ADD; op = EPOLL_CTL_ADD;
} }
ev.data.fd = watcher.socket.handle; ev.data.fd = watcher.socket.handle;
ev.events = (events & (Event.read | Event.accept) ? EPOLLIN | EPOLLPRI : 0) ev.events = (events & (Event.read | Event.accept) ? EPOLLIN | EPOLLPRI : 0)
| (events & Event.write ? EPOLLOUT : 0) | (events & Event.write ? EPOLLOUT : 0)
| EPOLLET; | EPOLLET;
return epoll_ctl(fd, op, watcher.socket.handle, &ev) == 0; return epoll_ctl(fd, op, watcher.socket.handle, &ev) == 0;
} }
/** /**
* Does the actual polling. * Does the actual polling.
*/ */
protected override void poll() @nogc protected override void poll() @nogc
{ {
// Don't block // Don't block
immutable timeout = cast(immutable int) blockTime.total!"msecs"; immutable timeout = cast(immutable int) blockTime.total!"msecs";
auto eventCount = epoll_wait(fd, events.get().ptr, maxEvents, timeout); auto eventCount = epoll_wait(fd, events.get().ptr, maxEvents, timeout);
if (eventCount < 0) if (eventCount < 0)
{ {
if (errno != EINTR) if (errno != EINTR)
{ {
throw defaultAllocator.make!BadLoopException(); throw defaultAllocator.make!BadLoopException();
} }
return; return;
} }
for (auto i = 0; i < eventCount; ++i) for (auto i = 0; i < eventCount; ++i)
{ {
auto transport = cast(StreamTransport) connections[events[i].data.fd]; auto transport = cast(StreamTransport) connections[events[i].data.fd];
if (transport is null) if (transport is null)
{ {
auto connection = cast(ConnectionWatcher) connections[events[i].data.fd]; auto connection = cast(ConnectionWatcher) connections[events[i].data.fd];
assert(connection !is null); assert(connection !is null);
acceptConnections(connection); acceptConnections(connection);
} }
else if (events[i].events & EPOLLERR) else if (events[i].events & EPOLLERR)
{ {
kill(transport); kill(transport);
continue; continue;
} }
else if (events[i].events & (EPOLLIN | EPOLLPRI | EPOLLHUP)) else if (events[i].events & (EPOLLIN | EPOLLPRI | EPOLLHUP))
{ {
SocketException exception; SocketException exception;
try try
{ {
ptrdiff_t received; ptrdiff_t received;
do do
{ {
received = transport.socket.receive(transport.output[]); received = transport.socket.receive(transport.output[]);
transport.output += received; transport.output += received;
} }
while (received); while (received);
} }
catch (SocketException e) catch (SocketException e)
{ {
exception = e; exception = e;
} }
if (transport.socket.disconnected) if (transport.socket.disconnected)
{ {
kill(transport, exception); kill(transport, exception);
continue; continue;
} }
else if (transport.output.length) else if (transport.output.length)
{ {
pendings.enqueue(transport); pendings.enqueue(transport);
} }
} }
if (events[i].events & EPOLLOUT) if (events[i].events & EPOLLOUT)
{ {
transport.writeReady = true; transport.writeReady = true;
if (transport.input.length) if (transport.input.length)
{ {
feed(transport); feed(transport);
} }
} }
} }
} }
/** /**
* Returns: The blocking time. * Returns: The blocking time.
*/ */
override protected @property inout(Duration) blockTime() override protected @property inout(Duration) blockTime()
inout @safe pure nothrow inout @safe pure nothrow
{ {
return min(super.blockTime, 1.dur!"seconds"); return min(super.blockTime, 1.dur!"seconds");
} }
} }

View File

@ -31,354 +31,354 @@ import core.sys.windows.winsock2;
*/ */
final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{ {
private SocketException exception; private SocketException exception;
private ReadBuffer!ubyte output; private ReadBuffer!ubyte output;
private WriteBuffer!ubyte input; private WriteBuffer!ubyte input;
private Protocol protocol_; private Protocol protocol_;
private bool closing; private bool closing;
/** /**
* Creates new completion port transport. * Creates new completion port transport.
* *
* Params: * Params:
* socket = Socket. * socket = Socket.
* *
* Precondition: $(D_INLINECODE socket !is null) * Precondition: $(D_INLINECODE socket !is null)
*/ */
this(OverlappedConnectedSocket socket) @nogc this(OverlappedConnectedSocket socket) @nogc
{ {
super(socket); super(socket);
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance); output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
input = WriteBuffer!ubyte(8192, MmapPool.instance); input = WriteBuffer!ubyte(8192, MmapPool.instance);
active = true; active = true;
} }
/** /**
* Returns: Socket. * Returns: Socket.
* *
* Postcondition: $(D_INLINECODE socket !is null) * Postcondition: $(D_INLINECODE socket !is null)
*/ */
override @property OverlappedConnectedSocket socket() pure nothrow @safe @nogc override @property OverlappedConnectedSocket socket() pure nothrow @safe @nogc
out (socket) out (socket)
{ {
assert(socket !is null); assert(socket !is null);
} }
body body
{ {
return cast(OverlappedConnectedSocket) socket_; return cast(OverlappedConnectedSocket) socket_;
} }
/** /**
* Returns $(D_PARAM true) if the transport is closing or closed. * Returns $(D_PARAM true) if the transport is closing or closed.
*/ */
bool isClosing() const pure nothrow @safe @nogc bool isClosing() const pure nothrow @safe @nogc
{ {
return closing; return closing;
} }
/** /**
* Close the transport. * Close the transport.
* *
* Buffered data will be flushed. No more data will be received. * Buffered data will be flushed. No more data will be received.
*/ */
void close() pure nothrow @safe @nogc void close() pure nothrow @safe @nogc
{ {
closing = true; closing = true;
} }
/** /**
* Write some data to the transport. * Write some data to the transport.
* *
* Params: * Params:
* data = Data to send. * data = Data to send.
*/ */
void write(ubyte[] data) @nogc void write(ubyte[] data) @nogc
{ {
input ~= data; input ~= data;
} }
/** /**
* Returns: Application protocol. * Returns: Application protocol.
*/ */
@property Protocol protocol() pure nothrow @safe @nogc @property Protocol protocol() pure nothrow @safe @nogc
{ {
return protocol_; return protocol_;
} }
/** /**
* Switches the protocol. * Switches the protocol.
* *
* The protocol is deallocated by the event loop, it should currently be * The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool). * allocated with $(D_PSYMBOL MmapPool).
* *
* Params: * Params:
* protocol = Application protocol. * protocol = Application protocol.
* *
* Precondition: $(D_INLINECODE protocol !is null) * Precondition: $(D_INLINECODE protocol !is null)
*/ */
@property void protocol(Protocol protocol) pure nothrow @safe @nogc @property void protocol(Protocol protocol) pure nothrow @safe @nogc
in in
{ {
assert(protocol !is null); assert(protocol !is null);
} }
body body
{ {
protocol_ = protocol; protocol_ = protocol;
} }
/** /**
* Invokes the watcher callback. * Invokes the watcher callback.
*/ */
override void invoke() @nogc override void invoke() @nogc
{ {
if (output.length) if (output.length)
{ {
immutable empty = input.length == 0; immutable empty = input.length == 0;
protocol.received(output[0 .. $]); protocol.received(output[0 .. $]);
output.clear(); output.clear();
if (empty) if (empty)
{ {
SocketState overlapped; SocketState overlapped;
try try
{ {
overlapped = MmapPool.instance.make!SocketState; overlapped = MmapPool.instance.make!SocketState;
socket.beginSend(input[], overlapped); socket.beginSend(input[], overlapped);
} }
catch (SocketException e) catch (SocketException e)
{ {
MmapPool.instance.dispose(overlapped); MmapPool.instance.dispose(overlapped);
MmapPool.instance.dispose(e); MmapPool.instance.dispose(e);
} }
} }
} }
else else
{ {
protocol.disconnected(exception); protocol.disconnected(exception);
MmapPool.instance.dispose(protocol_); MmapPool.instance.dispose(protocol_);
defaultAllocator.dispose(exception); defaultAllocator.dispose(exception);
active = false; active = false;
} }
} }
} }
final class IOCPLoop : Loop final class IOCPLoop : Loop
{ {
protected HANDLE completionPort; protected HANDLE completionPort;
protected OVERLAPPED overlap; protected OVERLAPPED overlap;
/** /**
* Initializes the loop. * Initializes the loop.
*/ */
this() @nogc this() @nogc
{ {
super(); super();
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!completionPort) if (!completionPort)
{ {
throw make!BadLoopException(defaultAllocator, throw make!BadLoopException(defaultAllocator,
"Creating completion port failed"); "Creating completion port failed");
} }
} }
/** /**
* Should be called if the backend configuration changes. * Should be called if the backend configuration changes.
* *
* Params: * Params:
* watcher = Watcher. * watcher = Watcher.
* oldEvents = The events were already set. * oldEvents = The events were already set.
* events = The events should be set. * events = The events should be set.
* *
* Returns: $(D_KEYWORD true) if the operation was successful. * Returns: $(D_KEYWORD true) if the operation was successful.
*/ */
override protected bool reify(SocketWatcher watcher, override protected bool reify(SocketWatcher watcher,
EventMask oldEvents, EventMask oldEvents,
EventMask events) @nogc EventMask events) @nogc
{ {
SocketState overlapped; SocketState overlapped;
if (!(oldEvents & Event.accept) && (events & Event.accept)) if (!(oldEvents & Event.accept) && (events & Event.accept))
{ {
auto socket = cast(OverlappedStreamSocket) watcher.socket; auto socket = cast(OverlappedStreamSocket) watcher.socket;
assert(socket !is null); assert(socket !is null);
if (CreateIoCompletionPort(cast(HANDLE) socket.handle, if (CreateIoCompletionPort(cast(HANDLE) socket.handle,
completionPort, completionPort,
cast(ULONG_PTR) (cast(void*) watcher), cast(ULONG_PTR) (cast(void*) watcher),
0) !is completionPort) 0) !is completionPort)
{ {
return false; return false;
} }
try try
{ {
overlapped = MmapPool.instance.make!SocketState; overlapped = MmapPool.instance.make!SocketState;
socket.beginAccept(overlapped); socket.beginAccept(overlapped);
} }
catch (SocketException e) catch (SocketException e)
{ {
MmapPool.instance.dispose(overlapped); MmapPool.instance.dispose(overlapped);
defaultAllocator.dispose(e); defaultAllocator.dispose(e);
return false; return false;
} }
} }
if (!(oldEvents & Event.read) && (events & Event.read) if (!(oldEvents & Event.read) && (events & Event.read)
|| !(oldEvents & Event.write) && (events & Event.write)) || !(oldEvents & Event.write) && (events & Event.write))
{ {
auto transport = cast(StreamTransport) watcher; auto transport = cast(StreamTransport) watcher;
assert(transport !is null); assert(transport !is null);
if (CreateIoCompletionPort(cast(HANDLE) transport.socket.handle, if (CreateIoCompletionPort(cast(HANDLE) transport.socket.handle,
completionPort, completionPort,
cast(ULONG_PTR) (cast(void*) watcher), cast(ULONG_PTR) (cast(void*) watcher),
0) !is completionPort) 0) !is completionPort)
{ {
return false; return false;
} }
// Begin to read // Begin to read
if (!(oldEvents & Event.read) && (events & Event.read)) if (!(oldEvents & Event.read) && (events & Event.read))
{ {
try try
{ {
overlapped = MmapPool.instance.make!SocketState; overlapped = MmapPool.instance.make!SocketState;
transport.socket.beginReceive(transport.output[], overlapped); transport.socket.beginReceive(transport.output[], overlapped);
} }
catch (SocketException e) catch (SocketException e)
{ {
MmapPool.instance.dispose(overlapped); MmapPool.instance.dispose(overlapped);
defaultAllocator.dispose(e); defaultAllocator.dispose(e);
return false; return false;
} }
} }
} }
return true; return true;
} }
private void kill(StreamTransport transport, private void kill(StreamTransport transport,
SocketException exception = null) @nogc SocketException exception = null) @nogc
in in
{ {
assert(transport !is null); assert(transport !is null);
} }
body body
{ {
transport.socket.shutdown(); transport.socket.shutdown();
defaultAllocator.dispose(transport.socket); defaultAllocator.dispose(transport.socket);
transport.exception = exception; transport.exception = exception;
pendings.enqueue(transport); pendings.enqueue(transport);
} }
/** /**
* Does the actual polling. * Does the actual polling.
*/ */
override protected void poll() @nogc override protected void poll() @nogc
{ {
DWORD lpNumberOfBytes; DWORD lpNumberOfBytes;
ULONG_PTR key; ULONG_PTR key;
LPOVERLAPPED overlap; LPOVERLAPPED overlap;
immutable timeout = cast(immutable int) blockTime.total!"msecs"; immutable timeout = cast(immutable int) blockTime.total!"msecs";
auto result = GetQueuedCompletionStatus(completionPort, auto result = GetQueuedCompletionStatus(completionPort,
&lpNumberOfBytes, &lpNumberOfBytes,
&key, &key,
&overlap, &overlap,
timeout); timeout);
if (result == FALSE && overlap == NULL) if (result == FALSE && overlap == NULL)
{ {
return; // Timeout return; // Timeout
} }
auto overlapped = (cast(SocketState) ((cast(void*) overlap) - 8)); auto overlapped = (cast(SocketState) ((cast(void*) overlap) - 8));
assert(overlapped !is null); assert(overlapped !is null);
scope (failure) scope (failure)
{ {
MmapPool.instance.dispose(overlapped); MmapPool.instance.dispose(overlapped);
} }
switch (overlapped.event) switch (overlapped.event)
{ {
case OverlappedSocketEvent.accept: case OverlappedSocketEvent.accept:
auto connection = cast(ConnectionWatcher) (cast(void*) key); auto connection = cast(ConnectionWatcher) (cast(void*) key);
assert(connection !is null); assert(connection !is null);
auto listener = cast(OverlappedStreamSocket) connection.socket; auto listener = cast(OverlappedStreamSocket) connection.socket;
assert(listener !is null); assert(listener !is null);
auto socket = listener.endAccept(overlapped); auto socket = listener.endAccept(overlapped);
auto transport = MmapPool.instance.make!StreamTransport(socket); auto transport = MmapPool.instance.make!StreamTransport(socket);
connection.incoming.enqueue(transport); connection.incoming.enqueue(transport);
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write)); reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
pendings.enqueue(connection); pendings.enqueue(connection);
listener.beginAccept(overlapped); listener.beginAccept(overlapped);
break; break;
case OverlappedSocketEvent.read: case OverlappedSocketEvent.read:
auto transport = cast(StreamTransport) (cast(void*) key); auto transport = cast(StreamTransport) (cast(void*) key);
assert(transport !is null); assert(transport !is null);
if (!transport.active) if (!transport.active)
{ {
MmapPool.instance.dispose(transport); MmapPool.instance.dispose(transport);
MmapPool.instance.dispose(overlapped); MmapPool.instance.dispose(overlapped);
return; return;
} }
int received; int received;
SocketException exception; SocketException exception;
try try
{ {
received = transport.socket.endReceive(overlapped); received = transport.socket.endReceive(overlapped);
} }
catch (SocketException e) catch (SocketException e)
{ {
exception = e; exception = e;
} }
if (transport.socket.disconnected) if (transport.socket.disconnected)
{ {
// We want to get one last notification to destroy the watcher. // We want to get one last notification to destroy the watcher.
transport.socket.beginReceive(transport.output[], overlapped); transport.socket.beginReceive(transport.output[], overlapped);
kill(transport, exception); kill(transport, exception);
} }
else if (received > 0) else if (received > 0)
{ {
immutable full = transport.output.free == received; immutable full = transport.output.free == received;
transport.output += received; transport.output += received;
// Receive was interrupted because the buffer is full. We have to continue. // Receive was interrupted because the buffer is full. We have to continue.
if (full) if (full)
{ {
transport.socket.beginReceive(transport.output[], overlapped); transport.socket.beginReceive(transport.output[], overlapped);
} }
pendings.enqueue(transport); pendings.enqueue(transport);
} }
break; break;
case OverlappedSocketEvent.write: case OverlappedSocketEvent.write:
auto transport = cast(StreamTransport) (cast(void*) key); auto transport = cast(StreamTransport) (cast(void*) key);
assert(transport !is null); assert(transport !is null);
transport.input += transport.socket.endSend(overlapped); transport.input += transport.socket.endSend(overlapped);
if (transport.input.length > 0) if (transport.input.length > 0)
{ {
transport.socket.beginSend(transport.input[], overlapped); transport.socket.beginSend(transport.input[], overlapped);
} }
else else
{ {
transport.socket.beginReceive(transport.output[], overlapped); transport.socket.beginReceive(transport.output[], overlapped);
if (transport.isClosing()) if (transport.isClosing())
{ {
kill(transport); kill(transport);
} }
} }
break; break;
default: default:
assert(false, "Unknown event"); assert(false, "Unknown event");
} }
} }
} }

View File

@ -12,31 +12,31 @@ module tanya.async.event.kqueue;
version (OSX) version (OSX)
{ {
version = MacBSD; version = MacBSD;
} }
else version (iOS) else version (iOS)
{ {
version = MacBSD; version = MacBSD;
} }
else version (TVOS) else version (TVOS)
{ {
version = MacBSD; version = MacBSD;
} }
else version (WatchOS) else version (WatchOS)
{ {
version = MacBSD; version = MacBSD;
} }
else version (FreeBSD) else version (FreeBSD)
{ {
version = MacBSD; version = MacBSD;
} }
else version (OpenBSD) else version (OpenBSD)
{ {
version = MacBSD; version = MacBSD;
} }
else version (DragonFlyBSD) else version (DragonFlyBSD)
{ {
version = MacBSD; version = MacBSD;
} }
version (MacBSD): version (MacBSD):
@ -50,62 +50,62 @@ import tanya.async.event.selector;
import tanya.async.loop; import tanya.async.loop;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.vector; import tanya.container.array;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
void EV_SET(kevent_t* kevp, typeof(kevent_t.tupleof) args) pure nothrow @nogc void EV_SET(kevent_t* kevp, typeof(kevent_t.tupleof) args) pure nothrow @nogc
{ {
*kevp = kevent_t(args); *kevp = kevent_t(args);
} }
enum : short enum : short
{ {
EVFILT_READ = -1, EVFILT_READ = -1,
EVFILT_WRITE = -2, EVFILT_WRITE = -2,
EVFILT_AIO = -3, /* attached to aio requests */ EVFILT_AIO = -3, /* attached to aio requests */
EVFILT_VNODE = -4, /* attached to vnodes */ EVFILT_VNODE = -4, /* attached to vnodes */
EVFILT_PROC = -5, /* attached to struct proc */ EVFILT_PROC = -5, /* attached to struct proc */
EVFILT_SIGNAL = -6, /* attached to struct proc */ EVFILT_SIGNAL = -6, /* attached to struct proc */
EVFILT_TIMER = -7, /* timers */ EVFILT_TIMER = -7, /* timers */
EVFILT_MACHPORT = -8, /* Mach portsets */ EVFILT_MACHPORT = -8, /* Mach portsets */
EVFILT_FS = -9, /* filesystem events */ EVFILT_FS = -9, /* filesystem events */
EVFILT_USER = -10, /* User events */ EVFILT_USER = -10, /* User events */
EVFILT_VM = -12, /* virtual memory events */ EVFILT_VM = -12, /* virtual memory events */
EVFILT_SYSCOUNT = 11 EVFILT_SYSCOUNT = 11
} }
struct kevent_t struct kevent_t
{ {
uintptr_t ident; /* identifier for this event */ uintptr_t ident; /* identifier for this event */
short filter; /* filter for event */ short filter; /* filter for event */
ushort flags; ushort flags;
uint fflags; uint fflags;
intptr_t data; intptr_t data;
void *udata; /* opaque user data identifier */ void *udata; /* opaque user data identifier */
} }
enum enum
{ {
/* actions */ /* actions */
EV_ADD = 0x0001, /* add event to kq (implies enable) */ EV_ADD = 0x0001, /* add event to kq (implies enable) */
EV_DELETE = 0x0002, /* delete event from kq */ EV_DELETE = 0x0002, /* delete event from kq */
EV_ENABLE = 0x0004, /* enable event */ EV_ENABLE = 0x0004, /* enable event */
EV_DISABLE = 0x0008, /* disable event (not reported) */ EV_DISABLE = 0x0008, /* disable event (not reported) */
/* flags */ /* flags */
EV_ONESHOT = 0x0010, /* only report one occurrence */ EV_ONESHOT = 0x0010, /* only report one occurrence */
EV_CLEAR = 0x0020, /* clear event state after reporting */ EV_CLEAR = 0x0020, /* clear event state after reporting */
EV_RECEIPT = 0x0040, /* force EV_ERROR on success, data=0 */ EV_RECEIPT = 0x0040, /* force EV_ERROR on success, data=0 */
EV_DISPATCH = 0x0080, /* disable event after reporting */ EV_DISPATCH = 0x0080, /* disable event after reporting */
EV_SYSFLAGS = 0xF000, /* reserved by system */ EV_SYSFLAGS = 0xF000, /* reserved by system */
EV_FLAG1 = 0x2000, /* filter-specific flag */ EV_FLAG1 = 0x2000, /* filter-specific flag */
/* returned values */ /* returned values */
EV_EOF = 0x8000, /* EOF detected */ EV_EOF = 0x8000, /* EOF detected */
EV_ERROR = 0x4000, /* error, data contains errno */ EV_ERROR = 0x4000, /* error, data contains errno */
} }
extern(C) int kqueue() nothrow @nogc; extern(C) int kqueue() nothrow @nogc;
@ -115,211 +115,211 @@ extern(C) int kevent(int kq, const kevent_t *changelist, int nchanges,
final class KqueueLoop : SelectorLoop final class KqueueLoop : SelectorLoop
{ {
protected int fd; protected int fd;
private Vector!kevent_t events; private Array!kevent_t events;
private Vector!kevent_t changes; private Array!kevent_t changes;
private size_t changeCount; private size_t changeCount;
/** /**
* Returns: Maximal event count can be got at a time * Returns: Maximal event count can be got at a time
* (should be supported by the backend). * (should be supported by the backend).
*/ */
override protected @property uint maxEvents() override protected @property uint maxEvents()
const pure nothrow @safe @nogc const pure nothrow @safe @nogc
{ {
return cast(uint) events.length; return cast(uint) events.length;
} }
this() @nogc this() @nogc
{ {
super(); super();
if ((fd = kqueue()) == -1) if ((fd = kqueue()) == -1)
{ {
throw make!BadLoopException(defaultAllocator, throw make!BadLoopException(defaultAllocator,
"kqueue initialization failed"); "kqueue initialization failed");
} }
events = Vector!kevent_t(64, MmapPool.instance); events = Array!kevent_t(64, MmapPool.instance);
changes = Vector!kevent_t(64, MmapPool.instance); changes = Array!kevent_t(64, MmapPool.instance);
} }
/** /**
* Frees loop internals. * Frees loop internals.
*/ */
~this() @nogc ~this() @nogc
{ {
close(fd); close(fd);
} }
private void set(socket_t socket, short filter, ushort flags) @nogc private void set(socket_t socket, short filter, ushort flags) @nogc
{ {
if (changes.length <= changeCount) if (changes.length <= changeCount)
{ {
changes.length = changeCount + maxEvents; changes.length = changeCount + maxEvents;
} }
EV_SET(&changes[changeCount], EV_SET(&changes[changeCount],
cast(ulong) socket, cast(ulong) socket,
filter, filter,
flags, flags,
0U, 0U,
0L, 0L,
null); null);
++changeCount; ++changeCount;
} }
/** /**
* Should be called if the backend configuration changes. * Should be called if the backend configuration changes.
* *
* Params: * Params:
* watcher = Watcher. * watcher = Watcher.
* oldEvents = The events were already set. * oldEvents = The events were already set.
* events = The events should be set. * events = The events should be set.
* *
* Returns: $(D_KEYWORD true) if the operation was successful. * Returns: $(D_KEYWORD true) if the operation was successful.
*/ */
override protected bool reify(SocketWatcher watcher, override protected bool reify(SocketWatcher watcher,
EventMask oldEvents, EventMask oldEvents,
EventMask events) @nogc EventMask events) @nogc
{ {
if (events != oldEvents) if (events != oldEvents)
{ {
if (oldEvents & Event.read || oldEvents & Event.accept) if (oldEvents & Event.read || oldEvents & Event.accept)
{ {
set(watcher.socket.handle, EVFILT_READ, EV_DELETE); set(watcher.socket.handle, EVFILT_READ, EV_DELETE);
} }
if (oldEvents & Event.write) if (oldEvents & Event.write)
{ {
set(watcher.socket.handle, EVFILT_WRITE, EV_DELETE); set(watcher.socket.handle, EVFILT_WRITE, EV_DELETE);
} }
} }
if (events & (Event.read | events & Event.accept)) if (events & (Event.read | events & Event.accept))
{ {
set(watcher.socket.handle, EVFILT_READ, EV_ADD | EV_ENABLE); set(watcher.socket.handle, EVFILT_READ, EV_ADD | EV_ENABLE);
} }
if (events & Event.write) if (events & Event.write)
{ {
set(watcher.socket.handle, EVFILT_WRITE, EV_ADD | EV_DISPATCH); set(watcher.socket.handle, EVFILT_WRITE, EV_ADD | EV_DISPATCH);
} }
return true; return true;
} }
/** /**
* Does the actual polling. * Does the actual polling.
*/ */
protected override void poll() @nogc protected override void poll() @nogc
{ {
timespec ts; timespec ts;
blockTime.split!("seconds", "nsecs")(ts.tv_sec, ts.tv_nsec); blockTime.split!("seconds", "nsecs")(ts.tv_sec, ts.tv_nsec);
if (changeCount > maxEvents) if (changeCount > maxEvents)
{ {
events.length = changes.length; events.length = changes.length;
} }
auto eventCount = kevent(fd, auto eventCount = kevent(fd,
changes.get().ptr, changes.get().ptr,
cast(int) changeCount, cast(int) changeCount,
events.get().ptr, events.get().ptr,
maxEvents, maxEvents,
&ts); &ts);
changeCount = 0; changeCount = 0;
if (eventCount < 0) if (eventCount < 0)
{ {
if (errno != EINTR) if (errno != EINTR)
{ {
throw defaultAllocator.make!BadLoopException(); throw defaultAllocator.make!BadLoopException();
} }
return; return;
} }
for (int i; i < eventCount; ++i) for (int i; i < eventCount; ++i)
{ {
assert(connections.length > events[i].ident); assert(connections.length > events[i].ident);
auto transport = cast(StreamTransport) connections[events[i].ident]; auto transport = cast(StreamTransport) connections[events[i].ident];
// If it is a ConnectionWatcher. Accept connections. // If it is a ConnectionWatcher. Accept connections.
if (transport is null) if (transport is null)
{ {
auto connection = cast(ConnectionWatcher) connections[events[i].ident]; auto connection = cast(ConnectionWatcher) connections[events[i].ident];
assert(connection !is null); assert(connection !is null);
acceptConnections(connection); acceptConnections(connection);
} }
else if (events[i].flags & EV_ERROR) else if (events[i].flags & EV_ERROR)
{ {
kill(transport); kill(transport);
} }
else if (events[i].filter == EVFILT_READ) else if (events[i].filter == EVFILT_READ)
{ {
SocketException exception; SocketException exception;
try try
{ {
ptrdiff_t received; ptrdiff_t received;
do do
{ {
received = transport.socket.receive(transport.output[]); received = transport.socket.receive(transport.output[]);
transport.output += received; transport.output += received;
} }
while (received); while (received);
} }
catch (SocketException e) catch (SocketException e)
{ {
exception = e; exception = e;
} }
if (transport.socket.disconnected) if (transport.socket.disconnected)
{ {
kill(transport, exception); kill(transport, exception);
} }
else if (transport.output.length) else if (transport.output.length)
{ {
pendings.enqueue(transport); pendings.enqueue(transport);
} }
} }
else if (events[i].filter == EVFILT_WRITE) else if (events[i].filter == EVFILT_WRITE)
{ {
transport.writeReady = true; transport.writeReady = true;
if (transport.input.length) if (transport.input.length)
{ {
feed(transport); feed(transport);
} }
} }
} }
} }
/** /**
* Returns: The blocking time. * Returns: The blocking time.
*/ */
override protected @property inout(Duration) blockTime() override protected @property inout(Duration) blockTime()
inout @nogc @safe pure nothrow inout @nogc @safe pure nothrow
{ {
return min(super.blockTime, 1.dur!"seconds"); return min(super.blockTime, 1.dur!"seconds");
} }
/** /**
* If the transport couldn't send the data, the further sending should * If the transport couldn't send the data, the further sending should
* be handled by the event loop. * be handled by the event loop.
* *
* Params: * Params:
* transport = Transport. * transport = Transport.
* exception = Exception thrown on sending. * exception = Exception thrown on sending.
* *
* Returns: $(D_KEYWORD true) if the operation could be successfully * Returns: $(D_KEYWORD true) if the operation could be successfully
* completed or scheduled, $(D_KEYWORD false) otherwise (the * completed or scheduled, $(D_KEYWORD false) otherwise (the
* transport will be destroyed then). * transport will be destroyed then).
*/ */
protected override bool feed(StreamTransport transport, protected override bool feed(StreamTransport transport,
SocketException exception = null) @nogc SocketException exception = null) @nogc
{ {
if (!super.feed(transport, exception)) if (!super.feed(transport, exception))
{ {
return false; return false;
} }
if (!transport.writeReady) if (!transport.writeReady)
{ {
set(transport.socket.handle, EVFILT_WRITE, EV_DISPATCH); set(transport.socket.handle, EVFILT_WRITE, EV_DISPATCH);
return true; return true;
} }
return false; return false;
} }
} }

View File

@ -17,7 +17,7 @@ import tanya.async.protocol;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.buffer; import tanya.container.buffer;
import tanya.container.vector; import tanya.container.array;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
@ -27,371 +27,374 @@ import tanya.network.socket;
*/ */
package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{ {
private SelectorLoop loop; private SelectorLoop loop;
private SocketException exception; private SocketException exception;
package ReadBuffer!ubyte output; package ReadBuffer!ubyte output;
package WriteBuffer!ubyte input; package WriteBuffer!ubyte input;
private Protocol protocol_; private Protocol protocol_;
private bool closing; private bool closing;
/// Received notification that the underlying socket is write-ready. /// Received notification that the underlying socket is write-ready.
package bool writeReady; package bool writeReady;
/** /**
* Params: * Params:
* loop = Event loop. * loop = Event loop.
* socket = Socket. * socket = Socket.
* *
* Precondition: $(D_INLINECODE loop !is null && socket !is null) * Precondition: $(D_INLINECODE loop !is null && socket !is null)
*/ */
this(SelectorLoop loop, ConnectedSocket socket) @nogc this(SelectorLoop loop, ConnectedSocket socket) @nogc
in in
{ {
assert(loop !is null); assert(loop !is null);
} }
body body
{ {
super(socket); super(socket);
this.loop = loop; this.loop = loop;
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance); output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
input = WriteBuffer!ubyte(8192, MmapPool.instance); input = WriteBuffer!ubyte(8192, MmapPool.instance);
active = true; active = true;
} }
/** /**
* Returns: Socket. * Returns: Socket.
* *
* Postcondition: $(D_INLINECODE socket !is null) * Postcondition: $(D_INLINECODE socket !is null)
*/ */
override @property ConnectedSocket socket() pure nothrow @safe @nogc override @property ConnectedSocket socket() pure nothrow @safe @nogc
out (socket) out (socket)
{ {
assert(socket !is null); assert(socket !is null);
} }
body body
{ {
return cast(ConnectedSocket) socket_; return cast(ConnectedSocket) socket_;
} }
private @property void socket(ConnectedSocket socket) pure nothrow @safe @nogc private @property void socket(ConnectedSocket socket)
in pure nothrow @safe @nogc
{ in
assert(socket !is null); {
} assert(socket !is null);
body }
{ body
socket_ = socket; {
} socket_ = socket;
}
/** /**
* Returns: Application protocol. * Returns: Application protocol.
*/ */
@property Protocol protocol() pure nothrow @safe @nogc @property Protocol protocol() pure nothrow @safe @nogc
{ {
return protocol_; return protocol_;
} }
/** /**
* Switches the protocol. * Switches the protocol.
* *
* The protocol is deallocated by the event loop, it should currently be * The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool). * allocated with $(D_PSYMBOL MmapPool).
* *
* Params: * Params:
* protocol = Application protocol. * protocol = Application protocol.
* *
* Precondition: $(D_INLINECODE protocol !is null) * Precondition: $(D_INLINECODE protocol !is null)
*/ */
@property void protocol(Protocol protocol) pure nothrow @safe @nogc @property void protocol(Protocol protocol) pure nothrow @safe @nogc
in in
{ {
assert(protocol !is null); assert(protocol !is null);
} }
body body
{ {
protocol_ = protocol; protocol_ = protocol;
} }
/** /**
* Returns $(D_PARAM true) if the transport is closing or closed. * Returns $(D_PARAM true) if the transport is closing or closed.
*/ */
bool isClosing() const pure nothrow @safe @nogc bool isClosing() const pure nothrow @safe @nogc
{ {
return closing; return closing;
} }
/** /**
* Close the transport. * Close the transport.
* *
* Buffered data will be flushed. No more data will be received. * Buffered data will be flushed. No more data will be received.
*/ */
void close() @nogc void close() @nogc
{ {
closing = true; closing = true;
loop.reify(this, EventMask(Event.read, Event.write), EventMask(Event.write)); loop.reify(this,
} EventMask(Event.read, Event.write),
EventMask(Event.write));
}
/** /**
* Invokes the watcher callback. * Invokes the watcher callback.
*/ */
override void invoke() @nogc override void invoke() @nogc
{ {
if (output.length) if (output.length)
{ {
protocol.received(output[0 .. $]); protocol.received(output[0 .. $]);
output.clear(); output.clear();
if (isClosing() && input.length == 0) if (isClosing() && input.length == 0)
{ {
loop.kill(this); loop.kill(this);
} }
} }
else else
{ {
protocol.disconnected(exception); protocol.disconnected(exception);
MmapPool.instance.dispose(protocol_); MmapPool.instance.dispose(protocol_);
defaultAllocator.dispose(exception); defaultAllocator.dispose(exception);
active = false; active = false;
} }
} }
/** /**
* Write some data to the transport. * Write some data to the transport.
* *
* Params: * Params:
* data = Data to send. * data = Data to send.
*/ */
void write(ubyte[] data) @nogc void write(ubyte[] data) @nogc
{ {
if (!data.length) if (!data.length)
{ {
return; return;
} }
// Try to write if the socket is write ready. // Try to write if the socket is write ready.
if (writeReady) if (writeReady)
{ {
ptrdiff_t sent; ptrdiff_t sent;
SocketException exception; SocketException exception;
try try
{ {
sent = socket.send(data); sent = socket.send(data);
if (sent == 0) if (sent == 0)
{ {
writeReady = false; writeReady = false;
} }
} }
catch (SocketException e) catch (SocketException e)
{ {
writeReady = false; writeReady = false;
exception = e; exception = e;
} }
if (sent < data.length) if (sent < data.length)
{ {
input ~= data[sent..$]; input ~= data[sent..$];
loop.feed(this, exception); loop.feed(this, exception);
} }
} }
else else
{ {
input ~= data; input ~= data;
} }
} }
} }
abstract class SelectorLoop : Loop abstract class SelectorLoop : Loop
{ {
/// Pending connections. /// Pending connections.
protected Vector!SocketWatcher connections; protected Array!SocketWatcher connections;
this() @nogc this() @nogc
{ {
super(); super();
connections = Vector!SocketWatcher(maxEvents, MmapPool.instance); connections = Array!SocketWatcher(maxEvents, MmapPool.instance);
} }
~this() @nogc ~this() @nogc
{ {
foreach (ref connection; connections) foreach (ref connection; connections)
{ {
// We want to free only the transports. ConnectionWatcher are created by the // We want to free only the transports. ConnectionWatcher are
// user and should be freed by himself. // created by the user and should be freed by himself.
if (cast(StreamTransport) connection !is null) if (cast(StreamTransport) connection !is null)
{ {
MmapPool.instance.dispose(connection); MmapPool.instance.dispose(connection);
} }
} }
} }
/** /**
* Should be called if the backend configuration changes. * Should be called if the backend configuration changes.
* *
* Params: * Params:
* watcher = Watcher. * watcher = Watcher.
* oldEvents = The events were already set. * oldEvents = The events were already set.
* events = The events should be set. * events = The events should be set.
* *
* Returns: $(D_KEYWORD true) if the operation was successful. * Returns: $(D_KEYWORD true) if the operation was successful.
*/ */
override abstract protected bool reify(SocketWatcher watcher, override abstract protected bool reify(SocketWatcher watcher,
EventMask oldEvents, EventMask oldEvents,
EventMask events) @nogc; EventMask events) @nogc;
/** /**
* Kills the watcher and closes the connection. * Kills the watcher and closes the connection.
* *
* Params: * Params:
* transport = Transport. * transport = Transport.
* exception = Occurred exception. * exception = Occurred exception.
*/ */
protected void kill(StreamTransport transport, protected void kill(StreamTransport transport,
SocketException exception = null) @nogc SocketException exception = null) @nogc
in in
{ {
assert(transport !is null); assert(transport !is null);
} }
body body
{ {
transport.socket.shutdown(); transport.socket.shutdown();
defaultAllocator.dispose(transport.socket); defaultAllocator.dispose(transport.socket);
transport.exception = exception; transport.exception = exception;
pendings.enqueue(transport); pendings.enqueue(transport);
} }
/** /**
* If the transport couldn't send the data, the further sending should * If the transport couldn't send the data, the further sending should
* be handled by the event loop. * be handled by the event loop.
* *
* Params: * Params:
* transport = Transport. * transport = Transport.
* exception = Exception thrown on sending. * exception = Exception thrown on sending.
* *
* Returns: $(D_KEYWORD true) if the operation could be successfully * Returns: $(D_KEYWORD true) if the operation could be successfully
* completed or scheduled, $(D_KEYWORD false) otherwise (the * completed or scheduled, $(D_KEYWORD false) otherwise (the
* transport will be destroyed then). * transport will be destroyed then).
*/ */
protected bool feed(StreamTransport transport, protected bool feed(StreamTransport transport,
SocketException exception = null) @nogc SocketException exception = null) @nogc
in in
{ {
assert(transport !is null); assert(transport !is null);
} }
body body
{ {
while (transport.input.length && transport.writeReady) while (transport.input.length && transport.writeReady)
{ {
try try
{ {
ptrdiff_t sent = transport.socket.send(transport.input[]); ptrdiff_t sent = transport.socket.send(transport.input[]);
if (sent == 0) if (sent == 0)
{ {
transport.writeReady = false; transport.writeReady = false;
} }
else else
{ {
transport.input += sent; transport.input += sent;
} }
} }
catch (SocketException e) catch (SocketException e)
{ {
exception = e; exception = e;
transport.writeReady = false; transport.writeReady = false;
} }
} }
if (exception !is null) if (exception !is null)
{ {
kill(transport, exception); kill(transport, exception);
return false; return false;
} }
if (transport.input.length == 0 && transport.isClosing()) if (transport.input.length == 0 && transport.isClosing())
{ {
kill(transport); kill(transport);
} }
return true; return true;
} }
/** /**
* Start watching. * Start watching.
* *
* Params: * Params:
* watcher = Watcher. * watcher = Watcher.
*/ */
override void start(ConnectionWatcher watcher) @nogc override void start(ConnectionWatcher watcher) @nogc
{ {
if (watcher.active) if (watcher.active)
{ {
return; return;
} }
if (connections.length <= watcher.socket) if (connections.length <= watcher.socket)
{ {
connections.length = watcher.socket.handle + maxEvents / 2; connections.length = watcher.socket.handle + maxEvents / 2;
} }
connections[watcher.socket.handle] = watcher; connections[watcher.socket.handle] = watcher;
super.start(watcher); super.start(watcher);
} }
/** /**
* Accept incoming connections. * Accept incoming connections.
* *
* Params: * Params:
* connection = Connection watcher ready to accept. * connection = Connection watcher ready to accept.
*/ */
package void acceptConnections(ConnectionWatcher connection) @nogc package void acceptConnections(ConnectionWatcher connection) @nogc
in in
{ {
assert(connection !is null); assert(connection !is null);
} }
body body
{ {
while (true) while (true)
{ {
ConnectedSocket client; ConnectedSocket client;
try try
{ {
client = (cast(StreamSocket) connection.socket).accept(); client = (cast(StreamSocket) connection.socket).accept();
} }
catch (SocketException e) catch (SocketException e)
{ {
defaultAllocator.dispose(e); defaultAllocator.dispose(e);
break; break;
} }
if (client is null) if (client is null)
{ {
break; break;
} }
StreamTransport transport; StreamTransport transport;
if (connections.length > client.handle) if (connections.length > client.handle)
{ {
transport = cast(StreamTransport) connections[client.handle]; transport = cast(StreamTransport) connections[client.handle];
} }
else else
{ {
connections.length = client.handle + maxEvents / 2; connections.length = client.handle + maxEvents / 2;
} }
if (transport is null) if (transport is null)
{ {
transport = MmapPool.instance.make!StreamTransport(this, client); transport = MmapPool.instance.make!StreamTransport(this, client);
connections[client.handle] = transport; connections[client.handle] = transport;
} }
else else
{ {
transport.socket = client; transport.socket = client;
} }
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write)); reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
connection.incoming.enqueue(transport); connection.incoming.enqueue(transport);
} }
if (!connection.incoming.empty) if (!connection.incoming.empty)
{ {
pendings.enqueue(connection); pendings.enqueue(connection);
} }
} }
} }

View File

@ -21,12 +21,12 @@ import core.sys.windows.windef;
*/ */
class State class State
{ {
/// For internal use by Windows API. /// For internal use by Windows API.
align(1) OVERLAPPED overlapped; align(1) OVERLAPPED overlapped;
/// File/socket handle. /// File/socket handle.
HANDLE handle; HANDLE handle;
/// For keeping events or event masks. /// For keeping events or event masks.
int event; int event;
} }

View File

@ -15,50 +15,50 @@
* *
* class EchoProtocol : TransmissionControlProtocol * class EchoProtocol : TransmissionControlProtocol
* { * {
* private DuplexTransport transport; * private DuplexTransport transport;
* *
* void received(in ubyte[] data) @nogc * void received(in ubyte[] data) @nogc
* { * {
* transport.write(data); * transport.write(data);
* } * }
* *
* void connected(DuplexTransport transport) @nogc * void connected(DuplexTransport transport) @nogc
* { * {
* this.transport = transport; * this.transport = transport;
* } * }
* *
* void disconnected(SocketException e) @nogc * void disconnected(SocketException e) @nogc
* { * {
* } * }
* } * }
* *
* void main() * void main()
* { * {
* auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192); * auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192);
* *
* version (Windows) * version (Windows)
* { * {
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.INET); * auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.INET);
* } * }
* else * else
* { * {
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.INET); * auto sock = defaultAllocator.make!StreamSocket(AddressFamily.INET);
* sock.blocking = false; * sock.blocking = false;
* } * }
* *
* sock.bind(address); * sock.bind(address);
* sock.listen(5); * sock.listen(5);
* *
* auto io = defaultAllocator.make!ConnectionWatcher(sock); * auto io = defaultAllocator.make!ConnectionWatcher(sock);
* io.setProtocol!EchoProtocol; * io.setProtocol!EchoProtocol;
* *
* defaultLoop.start(io); * defaultLoop.start(io);
* defaultLoop.run(); * defaultLoop.run();
* *
* sock.shutdown(); * sock.shutdown();
* defaultAllocator.dispose(io); * defaultAllocator.dispose(io);
* defaultAllocator.dispose(sock); * defaultAllocator.dispose(sock);
* defaultAllocator.dispose(address); * defaultAllocator.dispose(address);
* } * }
* --- * ---
*/ */
@ -81,33 +81,33 @@ version (DisableBackends)
} }
else version (linux) else version (linux)
{ {
import tanya.async.event.epoll; import tanya.async.event.epoll;
version = Epoll; version = Epoll;
} }
else version (Windows) else version (Windows)
{ {
import tanya.async.event.iocp; import tanya.async.event.iocp;
version = IOCP; version = IOCP;
} }
else version (OSX) else version (OSX)
{ {
version = Kqueue; version = Kqueue;
} }
else version (iOS) else version (iOS)
{ {
version = Kqueue; version = Kqueue;
} }
else version (FreeBSD) else version (FreeBSD)
{ {
version = Kqueue; version = Kqueue;
} }
else version (OpenBSD) else version (OpenBSD)
{ {
version = Kqueue; version = Kqueue;
} }
else version (DragonFlyBSD) else version (DragonFlyBSD)
{ {
version = Kqueue; version = Kqueue;
} }
/** /**
@ -115,11 +115,11 @@ else version (DragonFlyBSD)
*/ */
enum Event : uint enum Event : uint
{ {
none = 0x00, /// No events. none = 0x00, /// No events.
read = 0x01, /// Non-blocking read call. read = 0x01, /// Non-blocking read call.
write = 0x02, /// Non-blocking write call. write = 0x02, /// Non-blocking write call.
accept = 0x04, /// Connection made. accept = 0x04, /// Connection made.
error = 0x80000000, /// Sent when an error occurs. error = 0x80000000, /// Sent when an error occurs.
} }
alias EventMask = BitFlags!Event; alias EventMask = BitFlags!Event;
@ -129,150 +129,150 @@ alias EventMask = BitFlags!Event;
*/ */
abstract class Loop abstract class Loop
{ {
private bool done; private bool done;
/// Pending watchers. /// Pending watchers.
protected Queue!Watcher pendings; protected Queue!Watcher pendings;
/** /**
* Returns: Maximal event count can be got at a time * Returns: Maximal event count can be got at a time
* (should be supported by the backend). * (should be supported by the backend).
*/ */
protected @property uint maxEvents() protected @property uint maxEvents()
const pure nothrow @safe @nogc const pure nothrow @safe @nogc
{ {
return 128U; return 128U;
} }
/** /**
* Initializes the loop. * Initializes the loop.
*/ */
this() @nogc this() @nogc
{ {
pendings = Queue!Watcher(MmapPool.instance); pendings = Queue!Watcher(MmapPool.instance);
} }
/** /**
* Frees loop internals. * Frees loop internals.
*/ */
~this() @nogc ~this() @nogc
{ {
foreach (w; pendings) foreach (w; pendings)
{ {
MmapPool.instance.dispose(w); MmapPool.instance.dispose(w);
} }
} }
/** /**
* Starts the loop. * Starts the loop.
*/ */
void run() @nogc void run() @nogc
{ {
done = false; done = false;
do do
{ {
poll(); poll();
// Invoke pendings // Invoke pendings
foreach (ref w; pendings) foreach (ref w; pendings)
{ {
w.invoke(); w.invoke();
} }
} }
while (!done); while (!done);
} }
/** /**
* Break out of the loop. * Break out of the loop.
*/ */
void unloop() @safe pure nothrow @nogc void unloop() @safe pure nothrow @nogc
{ {
done = true; done = true;
} }
/** /**
* Start watching. * Start watching.
* *
* Params: * Params:
* watcher = Watcher. * watcher = Watcher.
*/ */
void start(ConnectionWatcher watcher) @nogc void start(ConnectionWatcher watcher) @nogc
{ {
if (watcher.active) if (watcher.active)
{ {
return; return;
} }
watcher.active = true; watcher.active = true;
reify(watcher, EventMask(Event.none), EventMask(Event.accept)); reify(watcher, EventMask(Event.none), EventMask(Event.accept));
} }
/** /**
* Stop watching. * Stop watching.
* *
* Params: * Params:
* watcher = Watcher. * watcher = Watcher.
*/ */
void stop(ConnectionWatcher watcher) @nogc void stop(ConnectionWatcher watcher) @nogc
{ {
if (!watcher.active) if (!watcher.active)
{ {
return; return;
} }
watcher.active = false; watcher.active = false;
reify(watcher, EventMask(Event.accept), EventMask(Event.none)); reify(watcher, EventMask(Event.accept), EventMask(Event.none));
} }
/** /**
* Should be called if the backend configuration changes. * Should be called if the backend configuration changes.
* *
* Params: * Params:
* watcher = Watcher. * watcher = Watcher.
* oldEvents = The events were already set. * oldEvents = The events were already set.
* events = The events should be set. * events = The events should be set.
* *
* Returns: $(D_KEYWORD true) if the operation was successful. * Returns: $(D_KEYWORD true) if the operation was successful.
*/ */
abstract protected bool reify(SocketWatcher watcher, abstract protected bool reify(SocketWatcher watcher,
EventMask oldEvents, EventMask oldEvents,
EventMask events) @nogc; EventMask events) @nogc;
/** /**
* Returns: The blocking time. * Returns: The blocking time.
*/ */
protected @property inout(Duration) blockTime() protected @property inout(Duration) blockTime()
inout @safe pure nothrow @nogc inout @safe pure nothrow @nogc
{ {
// Don't block if we have to do. // Don't block if we have to do.
return pendings.empty ? blockTime_ : Duration.zero; return pendings.empty ? blockTime_ : Duration.zero;
} }
/** /**
* Sets the blocking time for IO watchers. * Sets the blocking time for IO watchers.
* *
* Params: * Params:
* blockTime = The blocking time. Cannot be larger than * blockTime = The blocking time. Cannot be larger than
* $(D_PSYMBOL maxBlockTime). * $(D_PSYMBOL maxBlockTime).
*/ */
protected @property void blockTime(in Duration blockTime) @safe pure nothrow @nogc protected @property void blockTime(in Duration blockTime) @safe pure nothrow @nogc
in in
{ {
assert(blockTime <= 1.dur!"hours", "Too long to wait."); assert(blockTime <= 1.dur!"hours", "Too long to wait.");
assert(!blockTime.isNegative); assert(!blockTime.isNegative);
} }
body body
{ {
blockTime_ = blockTime; blockTime_ = blockTime;
} }
/** /**
* Does the actual polling. * Does the actual polling.
*/ */
abstract protected void poll() @nogc; abstract protected void poll() @nogc;
/// Maximal block time. /// Maximal block time.
protected Duration blockTime_ = 1.dur!"minutes"; protected Duration blockTime_ = 1.dur!"minutes";
} }
/** /**
@ -280,17 +280,17 @@ abstract class Loop
*/ */
class BadLoopException : Exception class BadLoopException : Exception
{ {
/** /**
* Params: * Params:
* file = The file where the exception occurred. * file = The file where the exception occurred.
* line = The line number where the exception occurred. * line = The line number where the exception occurred.
* next = The previous exception in the chain of exceptions, if any. * next = The previous exception in the chain of exceptions, if any.
*/ */
this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) this(string file = __FILE__, size_t line = __LINE__, Throwable next = null)
pure nothrow const @safe @nogc pure nothrow const @safe @nogc
{ {
super("Event loop cannot be initialized.", file, line, next); super("Event loop cannot be initialized.", file, line, next);
} }
} }
/** /**
@ -302,24 +302,24 @@ class BadLoopException : Exception
*/ */
@property Loop defaultLoop() @nogc @property Loop defaultLoop() @nogc
{ {
if (defaultLoop_ !is null) if (defaultLoop_ !is null)
{ {
return defaultLoop_; return defaultLoop_;
} }
version (Epoll) version (Epoll)
{ {
defaultLoop_ = MmapPool.instance.make!EpollLoop; defaultLoop_ = MmapPool.instance.make!EpollLoop;
} }
else version (IOCP) else version (IOCP)
{ {
defaultLoop_ = MmapPool.instance.make!IOCPLoop; defaultLoop_ = MmapPool.instance.make!IOCPLoop;
} }
else version (Kqueue) else version (Kqueue)
{ {
import tanya.async.event.kqueue; import tanya.async.event.kqueue;
defaultLoop_ = MmapPool.instance.make!KqueueLoop; defaultLoop_ = MmapPool.instance.make!KqueueLoop;
} }
return defaultLoop_; return defaultLoop_;
} }
/** /**
@ -331,16 +331,16 @@ class BadLoopException : Exception
* your implementation to this property. * your implementation to this property.
* *
* Params: * Params:
* loop = The event loop. * loop = The event loop.
*/ */
@property void defaultLoop(Loop loop) @nogc @property void defaultLoop(Loop loop) @nogc
in in
{ {
assert(loop !is null); assert(loop !is null);
} }
body body
{ {
defaultLoop_ = loop; defaultLoop_ = loop;
} }
private Loop defaultLoop_; private Loop defaultLoop_;

View File

@ -18,28 +18,28 @@ import tanya.async.transport;
*/ */
interface Protocol interface Protocol
{ {
/** /**
* Params: * Params:
* data = Read data. * data = Read data.
*/ */
void received(in ubyte[] data) @nogc; void received(in ubyte[] data) @nogc;
/** /**
* Called when a connection is made. * Called when a connection is made.
* *
* Params: * Params:
* transport = Protocol transport. * transport = Protocol transport.
*/ */
void connected(DuplexTransport transport) @nogc; void connected(DuplexTransport transport) @nogc;
/** /**
* Called when a connection is lost. * Called when a connection is lost.
* *
* Params: * Params:
* exception = $(D_PSYMBOL Exception) if an error caused * exception = $(D_PSYMBOL Exception) if an error caused
* the disconnect, $(D_KEYWORD null) otherwise. * the disconnect, $(D_KEYWORD null) otherwise.
*/ */
void disconnected(SocketException exception) @nogc; void disconnected(SocketException exception) @nogc;
} }
/** /**

View File

@ -32,13 +32,13 @@ interface ReadTransport : Transport
*/ */
interface WriteTransport : Transport interface WriteTransport : Transport
{ {
/** /**
* Write some data to the transport. * Write some data to the transport.
* *
* Params: * Params:
* data = Data to send. * data = Data to send.
*/ */
void write(ubyte[] data) @nogc; void write(ubyte[] data) @nogc;
} }
/** /**
@ -46,46 +46,46 @@ interface WriteTransport : Transport
*/ */
interface DuplexTransport : ReadTransport, WriteTransport interface DuplexTransport : ReadTransport, WriteTransport
{ {
/** /**
* Returns: Application protocol. * Returns: Application protocol.
* *
* Postcondition: $(D_INLINECODE protocol !is null) * Postcondition: $(D_INLINECODE protocol !is null)
*/ */
@property Protocol protocol() pure nothrow @safe @nogc @property Protocol protocol() pure nothrow @safe @nogc
out (protocol) out (protocol)
{ {
assert(protocol !is null); assert(protocol !is null);
} }
/** /**
* Switches the protocol. * Switches the protocol.
* *
* The protocol is deallocated by the event loop, it should currently be * The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool). * allocated with $(D_PSYMBOL MmapPool).
* *
* Params: * Params:
* protocol = Application protocol. * protocol = Application protocol.
* *
* Precondition: $(D_INLINECODE protocol !is null) * Precondition: $(D_INLINECODE protocol !is null)
*/ */
@property void protocol(Protocol protocol) pure nothrow @safe @nogc @property void protocol(Protocol protocol) pure nothrow @safe @nogc
in in
{ {
assert(protocol !is null); assert(protocol !is null);
} }
/** /**
* Returns $(D_PARAM true) if the transport is closing or closed. * Returns $(D_PARAM true) if the transport is closing or closed.
*/ */
bool isClosing() const pure nothrow @safe @nogc; bool isClosing() const pure nothrow @safe @nogc;
/** /**
* Close the transport. * Close the transport.
* *
* Buffered data will be flushed. No more data will be received. * Buffered data will be flushed. No more data will be received.
*/ */
void close() @nogc; void close() @nogc;
} }
/** /**
@ -93,8 +93,8 @@ interface DuplexTransport : ReadTransport, WriteTransport
*/ */
interface SocketTransport : Transport interface SocketTransport : Transport
{ {
/** /**
* Returns: Socket. * Returns: Socket.
*/ */
@property Socket socket() pure nothrow @safe @nogc; @property Socket socket() pure nothrow @safe @nogc;
} }

View File

@ -27,13 +27,13 @@ import tanya.network.socket;
*/ */
abstract class Watcher abstract class Watcher
{ {
/// Whether the watcher is active. /// Whether the watcher is active.
bool active; bool active;
/** /**
* Invoke some action on event. * Invoke some action on event.
*/ */
void invoke() @nogc; void invoke() @nogc;
} }
/** /**
@ -41,32 +41,32 @@ abstract class Watcher
*/ */
abstract class SocketWatcher : Watcher abstract class SocketWatcher : Watcher
{ {
/// Watched socket. /// Watched socket.
protected Socket socket_; protected Socket socket_;
/** /**
* Params: * Params:
* socket = Socket. * socket = Socket.
* *
* Precondition: $(D_INLINECODE socket !is null) * Precondition: $(D_INLINECODE socket !is null)
*/ */
this(Socket socket) pure nothrow @safe @nogc this(Socket socket) pure nothrow @safe @nogc
in in
{ {
assert(socket !is null); assert(socket !is null);
} }
body body
{ {
socket_ = socket; socket_ = socket;
} }
/** /**
* Returns: Socket. * Returns: Socket.
*/ */
@property Socket socket() pure nothrow @safe @nogc @property Socket socket() pure nothrow @safe @nogc
{ {
return socket_; return socket_;
} }
} }
/** /**
@ -74,44 +74,44 @@ abstract class SocketWatcher : Watcher
*/ */
class ConnectionWatcher : SocketWatcher class ConnectionWatcher : SocketWatcher
{ {
/// Incoming connection queue. /// Incoming connection queue.
Queue!DuplexTransport incoming; Queue!DuplexTransport incoming;
private Protocol delegate() @nogc protocolFactory; private Protocol delegate() @nogc protocolFactory;
/** /**
* Params: * Params:
* socket = Socket. * socket = Socket.
*/ */
this(Socket socket) @nogc this(Socket socket) @nogc
{ {
super(socket); super(socket);
incoming = Queue!DuplexTransport(MmapPool.instance); incoming = Queue!DuplexTransport(MmapPool.instance);
} }
/** /**
* Params: * Params:
* P = Protocol should be used. * P = Protocol should be used.
*/ */
void setProtocol(P : Protocol)() @nogc void setProtocol(P : Protocol)() @nogc
{ {
this.protocolFactory = () @nogc => cast(Protocol) MmapPool.instance.make!P; this.protocolFactory = () @nogc => cast(Protocol) MmapPool.instance.make!P;
} }
/** /**
* Invokes new connection callback. * Invokes new connection callback.
*/ */
override void invoke() @nogc override void invoke() @nogc
in in
{ {
assert(protocolFactory !is null, "Protocol isn't set."); assert(protocolFactory !is null, "Protocol isn't set.");
} }
body body
{ {
foreach (transport; incoming) foreach (transport; incoming)
{ {
transport.protocol = protocolFactory(); transport.protocol = protocolFactory();
transport.protocol.connected(transport); transport.protocol.connected(transport);
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,8 @@
*/ */
module tanya.container; module tanya.container;
public import tanya.container.array;
public import tanya.container.buffer; public import tanya.container.buffer;
public import tanya.container.list; public import tanya.container.list;
public import tanya.container.string; public import tanya.container.string;
public import tanya.container.vector;
public import tanya.container.queue; public import tanya.container.queue;

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,7 @@ import std.algorithm;
import std.ascii; import std.ascii;
import std.range; import std.range;
import std.traits; import std.traits;
import tanya.container.vector; import tanya.container.array;
import tanya.memory; import tanya.memory;
/** /**
@ -1444,26 +1444,26 @@ struct Integer
/** /**
* Returns: Two's complement representation of the integer. * Returns: Two's complement representation of the integer.
*/ */
Vector!ubyte toVector() const nothrow @safe @nogc Array!ubyte toArray() const nothrow @safe @nogc
out (vector) out (array)
{ {
assert(vector.length == length); assert(array.length == length);
} }
body body
{ {
Vector!ubyte vector; Array!ubyte array;
if (this.size == 0) if (this.size == 0)
{ {
return vector; return array;
} }
const bc = countBits(); const bc = countBits();
const remainingBits = bc & 0x07; const remainingBits = bc & 0x07;
vector.reserve(bc / 8); array.reserve(bc / 8);
if (remainingBits == 0) if (remainingBits == 0)
{ {
vector.insertBack(ubyte.init); array.insertBack(ubyte.init);
} }
@ -1486,14 +1486,14 @@ struct Integer
do do
{ {
vector.insertBack(cast(ubyte) (tmp.rep[0] & 0xff)); array.insertBack(cast(ubyte) (tmp.rep[0] & 0xff));
tmp >>= 8; tmp >>= 8;
} }
while (tmp != 0); while (tmp != 0);
vector[].reverse(); array[].reverse();
return vector; return array;
} }
/// ///
@ -1503,15 +1503,15 @@ struct Integer
auto integer = Integer(0x66778899aabbddee); auto integer = Integer(0x66778899aabbddee);
ubyte[8] expected = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ]; ubyte[8] expected = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
auto vector = integer.toVector(); auto array = integer.toArray();
assert(equal(vector[], expected[])); assert(equal(array[], expected[]));
} }
{ {
auto integer = Integer(0x03); auto integer = Integer(0x03);
ubyte[1] expected = [ 0x03 ]; ubyte[1] expected = [ 0x03 ];
auto vector = integer.toVector(); auto array = integer.toArray();
assert(equal(vector[], expected[])); assert(equal(array[], expected[]));
} }
{ {
ubyte[63] expected = [ ubyte[63] expected = [
@ -1526,8 +1526,8 @@ struct Integer
]; ];
auto integer = Integer(Sign.positive, expected[]); auto integer = Integer(Sign.positive, expected[]);
auto vector = integer.toVector(); auto array = integer.toArray();
assert(equal(vector[], expected[])); assert(equal(array[], expected[]));
} }
{ {
ubyte[14] expected = [ ubyte[14] expected = [
@ -1536,8 +1536,8 @@ struct Integer
]; ];
auto integer = Integer(Sign.positive, expected[]); auto integer = Integer(Sign.positive, expected[]);
auto vector = integer.toVector(); auto array = integer.toArray();
assert(equal(vector[], expected[])); assert(equal(array[], expected[]));
} }
} }