Rename Vector to Array
For consistency with Phobos.
This commit is contained in:
parent
58664570f9
commit
8c42cbfd63
@ -18,7 +18,7 @@ import tanya.async.event.selector;
|
||||
import tanya.async.loop;
|
||||
import tanya.async.transport;
|
||||
import tanya.async.watcher;
|
||||
import tanya.container.vector;
|
||||
import tanya.container.array;
|
||||
import tanya.memory;
|
||||
import tanya.memory.mmappool;
|
||||
import tanya.network.socket;
|
||||
@ -29,153 +29,153 @@ import std.algorithm.comparison;
|
||||
|
||||
extern (C) nothrow @nogc
|
||||
{
|
||||
int epoll_create1(int flags);
|
||||
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_create1(int flags);
|
||||
int epoll_ctl (int epfd, int op, int fd, epoll_event *event);
|
||||
int epoll_wait (int epfd, epoll_event *events, int maxevents, int timeout);
|
||||
}
|
||||
|
||||
final class EpollLoop : SelectorLoop
|
||||
{
|
||||
protected int fd;
|
||||
private Vector!epoll_event events;
|
||||
protected int fd;
|
||||
private Array!epoll_event events;
|
||||
|
||||
/**
|
||||
* Initializes the loop.
|
||||
*/
|
||||
this() @nogc
|
||||
{
|
||||
if ((fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
|
||||
{
|
||||
throw defaultAllocator.make!BadLoopException("epoll initialization failed");
|
||||
}
|
||||
super();
|
||||
events = Vector!epoll_event(maxEvents, MmapPool.instance);
|
||||
}
|
||||
/**
|
||||
* Initializes the loop.
|
||||
*/
|
||||
this() @nogc
|
||||
{
|
||||
if ((fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
|
||||
{
|
||||
throw defaultAllocator.make!BadLoopException("epoll initialization failed");
|
||||
}
|
||||
super();
|
||||
events = Array!epoll_event(maxEvents, MmapPool.instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees loop internals.
|
||||
*/
|
||||
~this() @nogc
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
/**
|
||||
* Frees loop internals.
|
||||
*/
|
||||
~this() @nogc
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called if the backend configuration changes.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
* oldEvents = The events were already set.
|
||||
* events = The events should be set.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation was successful.
|
||||
*/
|
||||
protected override bool reify(SocketWatcher watcher,
|
||||
EventMask oldEvents,
|
||||
EventMask events) @nogc
|
||||
{
|
||||
int op = EPOLL_CTL_DEL;
|
||||
epoll_event ev;
|
||||
/**
|
||||
* Should be called if the backend configuration changes.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
* oldEvents = The events were already set.
|
||||
* events = The events should be set.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation was successful.
|
||||
*/
|
||||
protected override bool reify(SocketWatcher watcher,
|
||||
EventMask oldEvents,
|
||||
EventMask events) @nogc
|
||||
{
|
||||
int op = EPOLL_CTL_DEL;
|
||||
epoll_event ev;
|
||||
|
||||
if (events == oldEvents)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (events && oldEvents)
|
||||
{
|
||||
op = EPOLL_CTL_MOD;
|
||||
}
|
||||
else if (events && !oldEvents)
|
||||
{
|
||||
op = EPOLL_CTL_ADD;
|
||||
}
|
||||
if (events == oldEvents)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (events && oldEvents)
|
||||
{
|
||||
op = EPOLL_CTL_MOD;
|
||||
}
|
||||
else if (events && !oldEvents)
|
||||
{
|
||||
op = EPOLL_CTL_ADD;
|
||||
}
|
||||
|
||||
ev.data.fd = watcher.socket.handle;
|
||||
ev.events = (events & (Event.read | Event.accept) ? EPOLLIN | EPOLLPRI : 0)
|
||||
| (events & Event.write ? EPOLLOUT : 0)
|
||||
| EPOLLET;
|
||||
ev.data.fd = watcher.socket.handle;
|
||||
ev.events = (events & (Event.read | Event.accept) ? EPOLLIN | EPOLLPRI : 0)
|
||||
| (events & Event.write ? EPOLLOUT : 0)
|
||||
| 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.
|
||||
*/
|
||||
protected override void poll() @nogc
|
||||
{
|
||||
// Don't block
|
||||
immutable timeout = cast(immutable int) blockTime.total!"msecs";
|
||||
auto eventCount = epoll_wait(fd, events.get().ptr, maxEvents, timeout);
|
||||
/**
|
||||
* Does the actual polling.
|
||||
*/
|
||||
protected override void poll() @nogc
|
||||
{
|
||||
// Don't block
|
||||
immutable timeout = cast(immutable int) blockTime.total!"msecs";
|
||||
auto eventCount = epoll_wait(fd, events.get().ptr, maxEvents, timeout);
|
||||
|
||||
if (eventCount < 0)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
{
|
||||
throw defaultAllocator.make!BadLoopException();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (eventCount < 0)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
{
|
||||
throw defaultAllocator.make!BadLoopException();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < eventCount; ++i)
|
||||
{
|
||||
auto transport = cast(StreamTransport) connections[events[i].data.fd];
|
||||
for (auto i = 0; i < eventCount; ++i)
|
||||
{
|
||||
auto transport = cast(StreamTransport) connections[events[i].data.fd];
|
||||
|
||||
if (transport is null)
|
||||
{
|
||||
auto connection = cast(ConnectionWatcher) connections[events[i].data.fd];
|
||||
assert(connection !is null);
|
||||
if (transport is null)
|
||||
{
|
||||
auto connection = cast(ConnectionWatcher) connections[events[i].data.fd];
|
||||
assert(connection !is null);
|
||||
|
||||
acceptConnections(connection);
|
||||
}
|
||||
else if (events[i].events & EPOLLERR)
|
||||
{
|
||||
kill(transport);
|
||||
continue;
|
||||
}
|
||||
else if (events[i].events & (EPOLLIN | EPOLLPRI | EPOLLHUP))
|
||||
{
|
||||
SocketException exception;
|
||||
try
|
||||
{
|
||||
ptrdiff_t received;
|
||||
do
|
||||
{
|
||||
received = transport.socket.receive(transport.output[]);
|
||||
transport.output += received;
|
||||
}
|
||||
while (received);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
exception = e;
|
||||
}
|
||||
if (transport.socket.disconnected)
|
||||
{
|
||||
kill(transport, exception);
|
||||
continue;
|
||||
}
|
||||
else if (transport.output.length)
|
||||
{
|
||||
pendings.enqueue(transport);
|
||||
}
|
||||
}
|
||||
if (events[i].events & EPOLLOUT)
|
||||
{
|
||||
transport.writeReady = true;
|
||||
if (transport.input.length)
|
||||
{
|
||||
feed(transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
acceptConnections(connection);
|
||||
}
|
||||
else if (events[i].events & EPOLLERR)
|
||||
{
|
||||
kill(transport);
|
||||
continue;
|
||||
}
|
||||
else if (events[i].events & (EPOLLIN | EPOLLPRI | EPOLLHUP))
|
||||
{
|
||||
SocketException exception;
|
||||
try
|
||||
{
|
||||
ptrdiff_t received;
|
||||
do
|
||||
{
|
||||
received = transport.socket.receive(transport.output[]);
|
||||
transport.output += received;
|
||||
}
|
||||
while (received);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
exception = e;
|
||||
}
|
||||
if (transport.socket.disconnected)
|
||||
{
|
||||
kill(transport, exception);
|
||||
continue;
|
||||
}
|
||||
else if (transport.output.length)
|
||||
{
|
||||
pendings.enqueue(transport);
|
||||
}
|
||||
}
|
||||
if (events[i].events & EPOLLOUT)
|
||||
{
|
||||
transport.writeReady = true;
|
||||
if (transport.input.length)
|
||||
{
|
||||
feed(transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: The blocking time.
|
||||
*/
|
||||
override protected @property inout(Duration) blockTime()
|
||||
inout @safe pure nothrow
|
||||
{
|
||||
return min(super.blockTime, 1.dur!"seconds");
|
||||
}
|
||||
/**
|
||||
* Returns: The blocking time.
|
||||
*/
|
||||
override protected @property inout(Duration) blockTime()
|
||||
inout @safe pure nothrow
|
||||
{
|
||||
return min(super.blockTime, 1.dur!"seconds");
|
||||
}
|
||||
}
|
||||
|
@ -31,354 +31,354 @@ import core.sys.windows.winsock2;
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* Params:
|
||||
* socket = Socket.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE socket !is null)
|
||||
*/
|
||||
this(OverlappedConnectedSocket socket) @nogc
|
||||
{
|
||||
super(socket);
|
||||
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
|
||||
input = WriteBuffer!ubyte(8192, MmapPool.instance);
|
||||
active = true;
|
||||
}
|
||||
/**
|
||||
* Creates new completion port transport.
|
||||
*
|
||||
* Params:
|
||||
* socket = Socket.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE socket !is null)
|
||||
*/
|
||||
this(OverlappedConnectedSocket socket) @nogc
|
||||
{
|
||||
super(socket);
|
||||
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
|
||||
input = WriteBuffer!ubyte(8192, MmapPool.instance);
|
||||
active = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Socket.
|
||||
*
|
||||
* Postcondition: $(D_INLINECODE socket !is null)
|
||||
*/
|
||||
override @property OverlappedConnectedSocket socket() pure nothrow @safe @nogc
|
||||
out (socket)
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
return cast(OverlappedConnectedSocket) socket_;
|
||||
}
|
||||
/**
|
||||
* Returns: Socket.
|
||||
*
|
||||
* Postcondition: $(D_INLINECODE socket !is null)
|
||||
*/
|
||||
override @property OverlappedConnectedSocket socket() pure nothrow @safe @nogc
|
||||
out (socket)
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
return cast(OverlappedConnectedSocket) socket_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns $(D_PARAM true) if the transport is closing or closed.
|
||||
*/
|
||||
bool isClosing() const pure nothrow @safe @nogc
|
||||
{
|
||||
return closing;
|
||||
}
|
||||
/**
|
||||
* Returns $(D_PARAM true) if the transport is closing or closed.
|
||||
*/
|
||||
bool isClosing() const pure nothrow @safe @nogc
|
||||
{
|
||||
return closing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the transport.
|
||||
*
|
||||
* Buffered data will be flushed. No more data will be received.
|
||||
*/
|
||||
void close() pure nothrow @safe @nogc
|
||||
{
|
||||
closing = true;
|
||||
}
|
||||
/**
|
||||
* Close the transport.
|
||||
*
|
||||
* Buffered data will be flushed. No more data will be received.
|
||||
*/
|
||||
void close() pure nothrow @safe @nogc
|
||||
{
|
||||
closing = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write some data to the transport.
|
||||
*
|
||||
* Params:
|
||||
* data = Data to send.
|
||||
*/
|
||||
void write(ubyte[] data) @nogc
|
||||
{
|
||||
input ~= data;
|
||||
}
|
||||
/**
|
||||
* Write some data to the transport.
|
||||
*
|
||||
* Params:
|
||||
* data = Data to send.
|
||||
*/
|
||||
void write(ubyte[] data) @nogc
|
||||
{
|
||||
input ~= data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Application protocol.
|
||||
*/
|
||||
@property Protocol protocol() pure nothrow @safe @nogc
|
||||
{
|
||||
return protocol_;
|
||||
}
|
||||
/**
|
||||
* Returns: Application protocol.
|
||||
*/
|
||||
@property Protocol protocol() pure nothrow @safe @nogc
|
||||
{
|
||||
return protocol_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the protocol.
|
||||
*
|
||||
* The protocol is deallocated by the event loop, it should currently be
|
||||
* allocated with $(D_PSYMBOL MmapPool).
|
||||
*
|
||||
* Params:
|
||||
* protocol = Application protocol.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE protocol !is null)
|
||||
*/
|
||||
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(protocol !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
protocol_ = protocol;
|
||||
}
|
||||
/**
|
||||
* Switches the protocol.
|
||||
*
|
||||
* The protocol is deallocated by the event loop, it should currently be
|
||||
* allocated with $(D_PSYMBOL MmapPool).
|
||||
*
|
||||
* Params:
|
||||
* protocol = Application protocol.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE protocol !is null)
|
||||
*/
|
||||
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(protocol !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
protocol_ = protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the watcher callback.
|
||||
*/
|
||||
override void invoke() @nogc
|
||||
{
|
||||
if (output.length)
|
||||
{
|
||||
immutable empty = input.length == 0;
|
||||
protocol.received(output[0 .. $]);
|
||||
output.clear();
|
||||
if (empty)
|
||||
{
|
||||
SocketState overlapped;
|
||||
try
|
||||
{
|
||||
overlapped = MmapPool.instance.make!SocketState;
|
||||
socket.beginSend(input[], overlapped);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
MmapPool.instance.dispose(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol.disconnected(exception);
|
||||
MmapPool.instance.dispose(protocol_);
|
||||
defaultAllocator.dispose(exception);
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Invokes the watcher callback.
|
||||
*/
|
||||
override void invoke() @nogc
|
||||
{
|
||||
if (output.length)
|
||||
{
|
||||
immutable empty = input.length == 0;
|
||||
protocol.received(output[0 .. $]);
|
||||
output.clear();
|
||||
if (empty)
|
||||
{
|
||||
SocketState overlapped;
|
||||
try
|
||||
{
|
||||
overlapped = MmapPool.instance.make!SocketState;
|
||||
socket.beginSend(input[], overlapped);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
MmapPool.instance.dispose(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol.disconnected(exception);
|
||||
MmapPool.instance.dispose(protocol_);
|
||||
defaultAllocator.dispose(exception);
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class IOCPLoop : Loop
|
||||
{
|
||||
protected HANDLE completionPort;
|
||||
protected HANDLE completionPort;
|
||||
|
||||
protected OVERLAPPED overlap;
|
||||
protected OVERLAPPED overlap;
|
||||
|
||||
/**
|
||||
* Initializes the loop.
|
||||
*/
|
||||
this() @nogc
|
||||
{
|
||||
super();
|
||||
/**
|
||||
* Initializes the loop.
|
||||
*/
|
||||
this() @nogc
|
||||
{
|
||||
super();
|
||||
|
||||
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
||||
if (!completionPort)
|
||||
{
|
||||
throw make!BadLoopException(defaultAllocator,
|
||||
"Creating completion port failed");
|
||||
}
|
||||
}
|
||||
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
||||
if (!completionPort)
|
||||
{
|
||||
throw make!BadLoopException(defaultAllocator,
|
||||
"Creating completion port failed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called if the backend configuration changes.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
* oldEvents = The events were already set.
|
||||
* events = The events should be set.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation was successful.
|
||||
*/
|
||||
override protected bool reify(SocketWatcher watcher,
|
||||
EventMask oldEvents,
|
||||
EventMask events) @nogc
|
||||
{
|
||||
SocketState overlapped;
|
||||
if (!(oldEvents & Event.accept) && (events & Event.accept))
|
||||
{
|
||||
auto socket = cast(OverlappedStreamSocket) watcher.socket;
|
||||
assert(socket !is null);
|
||||
/**
|
||||
* Should be called if the backend configuration changes.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
* oldEvents = The events were already set.
|
||||
* events = The events should be set.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation was successful.
|
||||
*/
|
||||
override protected bool reify(SocketWatcher watcher,
|
||||
EventMask oldEvents,
|
||||
EventMask events) @nogc
|
||||
{
|
||||
SocketState overlapped;
|
||||
if (!(oldEvents & Event.accept) && (events & Event.accept))
|
||||
{
|
||||
auto socket = cast(OverlappedStreamSocket) watcher.socket;
|
||||
assert(socket !is null);
|
||||
|
||||
if (CreateIoCompletionPort(cast(HANDLE) socket.handle,
|
||||
completionPort,
|
||||
cast(ULONG_PTR) (cast(void*) watcher),
|
||||
0) !is completionPort)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (CreateIoCompletionPort(cast(HANDLE) socket.handle,
|
||||
completionPort,
|
||||
cast(ULONG_PTR) (cast(void*) watcher),
|
||||
0) !is completionPort)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
overlapped = MmapPool.instance.make!SocketState;
|
||||
socket.beginAccept(overlapped);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
defaultAllocator.dispose(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!(oldEvents & Event.read) && (events & Event.read)
|
||||
|| !(oldEvents & Event.write) && (events & Event.write))
|
||||
{
|
||||
auto transport = cast(StreamTransport) watcher;
|
||||
assert(transport !is null);
|
||||
try
|
||||
{
|
||||
overlapped = MmapPool.instance.make!SocketState;
|
||||
socket.beginAccept(overlapped);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
defaultAllocator.dispose(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!(oldEvents & Event.read) && (events & Event.read)
|
||||
|| !(oldEvents & Event.write) && (events & Event.write))
|
||||
{
|
||||
auto transport = cast(StreamTransport) watcher;
|
||||
assert(transport !is null);
|
||||
|
||||
if (CreateIoCompletionPort(cast(HANDLE) transport.socket.handle,
|
||||
completionPort,
|
||||
cast(ULONG_PTR) (cast(void*) watcher),
|
||||
0) !is completionPort)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (CreateIoCompletionPort(cast(HANDLE) transport.socket.handle,
|
||||
completionPort,
|
||||
cast(ULONG_PTR) (cast(void*) watcher),
|
||||
0) !is completionPort)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Begin to read
|
||||
if (!(oldEvents & Event.read) && (events & Event.read))
|
||||
{
|
||||
try
|
||||
{
|
||||
overlapped = MmapPool.instance.make!SocketState;
|
||||
transport.socket.beginReceive(transport.output[], overlapped);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
defaultAllocator.dispose(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Begin to read
|
||||
if (!(oldEvents & Event.read) && (events & Event.read))
|
||||
{
|
||||
try
|
||||
{
|
||||
overlapped = MmapPool.instance.make!SocketState;
|
||||
transport.socket.beginReceive(transport.output[], overlapped);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
defaultAllocator.dispose(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void kill(StreamTransport transport,
|
||||
SocketException exception = null) @nogc
|
||||
in
|
||||
{
|
||||
assert(transport !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
transport.socket.shutdown();
|
||||
defaultAllocator.dispose(transport.socket);
|
||||
transport.exception = exception;
|
||||
pendings.enqueue(transport);
|
||||
}
|
||||
private void kill(StreamTransport transport,
|
||||
SocketException exception = null) @nogc
|
||||
in
|
||||
{
|
||||
assert(transport !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
transport.socket.shutdown();
|
||||
defaultAllocator.dispose(transport.socket);
|
||||
transport.exception = exception;
|
||||
pendings.enqueue(transport);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual polling.
|
||||
*/
|
||||
override protected void poll() @nogc
|
||||
{
|
||||
DWORD lpNumberOfBytes;
|
||||
ULONG_PTR key;
|
||||
LPOVERLAPPED overlap;
|
||||
immutable timeout = cast(immutable int) blockTime.total!"msecs";
|
||||
/**
|
||||
* Does the actual polling.
|
||||
*/
|
||||
override protected void poll() @nogc
|
||||
{
|
||||
DWORD lpNumberOfBytes;
|
||||
ULONG_PTR key;
|
||||
LPOVERLAPPED overlap;
|
||||
immutable timeout = cast(immutable int) blockTime.total!"msecs";
|
||||
|
||||
auto result = GetQueuedCompletionStatus(completionPort,
|
||||
&lpNumberOfBytes,
|
||||
&key,
|
||||
&overlap,
|
||||
timeout);
|
||||
if (result == FALSE && overlap == NULL)
|
||||
{
|
||||
return; // Timeout
|
||||
}
|
||||
auto result = GetQueuedCompletionStatus(completionPort,
|
||||
&lpNumberOfBytes,
|
||||
&key,
|
||||
&overlap,
|
||||
timeout);
|
||||
if (result == FALSE && overlap == NULL)
|
||||
{
|
||||
return; // Timeout
|
||||
}
|
||||
|
||||
auto overlapped = (cast(SocketState) ((cast(void*) overlap) - 8));
|
||||
assert(overlapped !is null);
|
||||
scope (failure)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
}
|
||||
auto overlapped = (cast(SocketState) ((cast(void*) overlap) - 8));
|
||||
assert(overlapped !is null);
|
||||
scope (failure)
|
||||
{
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
}
|
||||
|
||||
switch (overlapped.event)
|
||||
{
|
||||
case OverlappedSocketEvent.accept:
|
||||
auto connection = cast(ConnectionWatcher) (cast(void*) key);
|
||||
assert(connection !is null);
|
||||
switch (overlapped.event)
|
||||
{
|
||||
case OverlappedSocketEvent.accept:
|
||||
auto connection = cast(ConnectionWatcher) (cast(void*) key);
|
||||
assert(connection !is null);
|
||||
|
||||
auto listener = cast(OverlappedStreamSocket) connection.socket;
|
||||
assert(listener !is null);
|
||||
auto listener = cast(OverlappedStreamSocket) connection.socket;
|
||||
assert(listener !is null);
|
||||
|
||||
auto socket = listener.endAccept(overlapped);
|
||||
auto transport = MmapPool.instance.make!StreamTransport(socket);
|
||||
auto socket = listener.endAccept(overlapped);
|
||||
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);
|
||||
listener.beginAccept(overlapped);
|
||||
break;
|
||||
case OverlappedSocketEvent.read:
|
||||
auto transport = cast(StreamTransport) (cast(void*) key);
|
||||
assert(transport !is null);
|
||||
pendings.enqueue(connection);
|
||||
listener.beginAccept(overlapped);
|
||||
break;
|
||||
case OverlappedSocketEvent.read:
|
||||
auto transport = cast(StreamTransport) (cast(void*) key);
|
||||
assert(transport !is null);
|
||||
|
||||
if (!transport.active)
|
||||
{
|
||||
MmapPool.instance.dispose(transport);
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
return;
|
||||
}
|
||||
if (!transport.active)
|
||||
{
|
||||
MmapPool.instance.dispose(transport);
|
||||
MmapPool.instance.dispose(overlapped);
|
||||
return;
|
||||
}
|
||||
|
||||
int received;
|
||||
SocketException exception;
|
||||
try
|
||||
{
|
||||
received = transport.socket.endReceive(overlapped);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
exception = e;
|
||||
}
|
||||
if (transport.socket.disconnected)
|
||||
{
|
||||
// We want to get one last notification to destroy the watcher.
|
||||
transport.socket.beginReceive(transport.output[], overlapped);
|
||||
kill(transport, exception);
|
||||
}
|
||||
else if (received > 0)
|
||||
{
|
||||
immutable full = transport.output.free == received;
|
||||
int received;
|
||||
SocketException exception;
|
||||
try
|
||||
{
|
||||
received = transport.socket.endReceive(overlapped);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
exception = e;
|
||||
}
|
||||
if (transport.socket.disconnected)
|
||||
{
|
||||
// We want to get one last notification to destroy the watcher.
|
||||
transport.socket.beginReceive(transport.output[], overlapped);
|
||||
kill(transport, exception);
|
||||
}
|
||||
else if (received > 0)
|
||||
{
|
||||
immutable full = transport.output.free == received;
|
||||
|
||||
transport.output += received;
|
||||
// Receive was interrupted because the buffer is full. We have to continue.
|
||||
if (full)
|
||||
{
|
||||
transport.socket.beginReceive(transport.output[], overlapped);
|
||||
}
|
||||
pendings.enqueue(transport);
|
||||
}
|
||||
break;
|
||||
case OverlappedSocketEvent.write:
|
||||
auto transport = cast(StreamTransport) (cast(void*) key);
|
||||
assert(transport !is null);
|
||||
transport.output += received;
|
||||
// Receive was interrupted because the buffer is full. We have to continue.
|
||||
if (full)
|
||||
{
|
||||
transport.socket.beginReceive(transport.output[], overlapped);
|
||||
}
|
||||
pendings.enqueue(transport);
|
||||
}
|
||||
break;
|
||||
case OverlappedSocketEvent.write:
|
||||
auto transport = cast(StreamTransport) (cast(void*) key);
|
||||
assert(transport !is null);
|
||||
|
||||
transport.input += transport.socket.endSend(overlapped);
|
||||
if (transport.input.length > 0)
|
||||
{
|
||||
transport.socket.beginSend(transport.input[], overlapped);
|
||||
}
|
||||
else
|
||||
{
|
||||
transport.socket.beginReceive(transport.output[], overlapped);
|
||||
if (transport.isClosing())
|
||||
{
|
||||
kill(transport);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false, "Unknown event");
|
||||
}
|
||||
}
|
||||
}
|
||||
transport.input += transport.socket.endSend(overlapped);
|
||||
if (transport.input.length > 0)
|
||||
{
|
||||
transport.socket.beginSend(transport.input[], overlapped);
|
||||
}
|
||||
else
|
||||
{
|
||||
transport.socket.beginReceive(transport.output[], overlapped);
|
||||
if (transport.isClosing())
|
||||
{
|
||||
kill(transport);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false, "Unknown event");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,31 +12,31 @@ module tanya.async.event.kqueue;
|
||||
|
||||
version (OSX)
|
||||
{
|
||||
version = MacBSD;
|
||||
version = MacBSD;
|
||||
}
|
||||
else version (iOS)
|
||||
{
|
||||
version = MacBSD;
|
||||
version = MacBSD;
|
||||
}
|
||||
else version (TVOS)
|
||||
{
|
||||
version = MacBSD;
|
||||
version = MacBSD;
|
||||
}
|
||||
else version (WatchOS)
|
||||
{
|
||||
version = MacBSD;
|
||||
version = MacBSD;
|
||||
}
|
||||
else version (FreeBSD)
|
||||
{
|
||||
version = MacBSD;
|
||||
version = MacBSD;
|
||||
}
|
||||
else version (OpenBSD)
|
||||
{
|
||||
version = MacBSD;
|
||||
version = MacBSD;
|
||||
}
|
||||
else version (DragonFlyBSD)
|
||||
{
|
||||
version = MacBSD;
|
||||
version = MacBSD;
|
||||
}
|
||||
|
||||
version (MacBSD):
|
||||
@ -50,62 +50,62 @@ import tanya.async.event.selector;
|
||||
import tanya.async.loop;
|
||||
import tanya.async.transport;
|
||||
import tanya.async.watcher;
|
||||
import tanya.container.vector;
|
||||
import tanya.container.array;
|
||||
import tanya.memory;
|
||||
import tanya.memory.mmappool;
|
||||
import tanya.network.socket;
|
||||
|
||||
void EV_SET(kevent_t* kevp, typeof(kevent_t.tupleof) args) pure nothrow @nogc
|
||||
{
|
||||
*kevp = kevent_t(args);
|
||||
*kevp = kevent_t(args);
|
||||
}
|
||||
|
||||
enum : short
|
||||
{
|
||||
EVFILT_READ = -1,
|
||||
EVFILT_WRITE = -2,
|
||||
EVFILT_AIO = -3, /* attached to aio requests */
|
||||
EVFILT_VNODE = -4, /* attached to vnodes */
|
||||
EVFILT_PROC = -5, /* attached to struct proc */
|
||||
EVFILT_SIGNAL = -6, /* attached to struct proc */
|
||||
EVFILT_TIMER = -7, /* timers */
|
||||
EVFILT_MACHPORT = -8, /* Mach portsets */
|
||||
EVFILT_FS = -9, /* filesystem events */
|
||||
EVFILT_USER = -10, /* User events */
|
||||
EVFILT_VM = -12, /* virtual memory events */
|
||||
EVFILT_SYSCOUNT = 11
|
||||
EVFILT_READ = -1,
|
||||
EVFILT_WRITE = -2,
|
||||
EVFILT_AIO = -3, /* attached to aio requests */
|
||||
EVFILT_VNODE = -4, /* attached to vnodes */
|
||||
EVFILT_PROC = -5, /* attached to struct proc */
|
||||
EVFILT_SIGNAL = -6, /* attached to struct proc */
|
||||
EVFILT_TIMER = -7, /* timers */
|
||||
EVFILT_MACHPORT = -8, /* Mach portsets */
|
||||
EVFILT_FS = -9, /* filesystem events */
|
||||
EVFILT_USER = -10, /* User events */
|
||||
EVFILT_VM = -12, /* virtual memory events */
|
||||
EVFILT_SYSCOUNT = 11
|
||||
}
|
||||
|
||||
struct kevent_t
|
||||
{
|
||||
uintptr_t ident; /* identifier for this event */
|
||||
short filter; /* filter for event */
|
||||
ushort flags;
|
||||
uint fflags;
|
||||
intptr_t data;
|
||||
void *udata; /* opaque user data identifier */
|
||||
uintptr_t ident; /* identifier for this event */
|
||||
short filter; /* filter for event */
|
||||
ushort flags;
|
||||
uint fflags;
|
||||
intptr_t data;
|
||||
void *udata; /* opaque user data identifier */
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
/* actions */
|
||||
EV_ADD = 0x0001, /* add event to kq (implies enable) */
|
||||
EV_DELETE = 0x0002, /* delete event from kq */
|
||||
EV_ENABLE = 0x0004, /* enable event */
|
||||
EV_DISABLE = 0x0008, /* disable event (not reported) */
|
||||
/* actions */
|
||||
EV_ADD = 0x0001, /* add event to kq (implies enable) */
|
||||
EV_DELETE = 0x0002, /* delete event from kq */
|
||||
EV_ENABLE = 0x0004, /* enable event */
|
||||
EV_DISABLE = 0x0008, /* disable event (not reported) */
|
||||
|
||||
/* flags */
|
||||
EV_ONESHOT = 0x0010, /* only report one occurrence */
|
||||
EV_CLEAR = 0x0020, /* clear event state after reporting */
|
||||
EV_RECEIPT = 0x0040, /* force EV_ERROR on success, data=0 */
|
||||
EV_DISPATCH = 0x0080, /* disable event after reporting */
|
||||
/* flags */
|
||||
EV_ONESHOT = 0x0010, /* only report one occurrence */
|
||||
EV_CLEAR = 0x0020, /* clear event state after reporting */
|
||||
EV_RECEIPT = 0x0040, /* force EV_ERROR on success, data=0 */
|
||||
EV_DISPATCH = 0x0080, /* disable event after reporting */
|
||||
|
||||
EV_SYSFLAGS = 0xF000, /* reserved by system */
|
||||
EV_FLAG1 = 0x2000, /* filter-specific flag */
|
||||
EV_SYSFLAGS = 0xF000, /* reserved by system */
|
||||
EV_FLAG1 = 0x2000, /* filter-specific flag */
|
||||
|
||||
/* returned values */
|
||||
EV_EOF = 0x8000, /* EOF detected */
|
||||
EV_ERROR = 0x4000, /* error, data contains errno */
|
||||
/* returned values */
|
||||
EV_EOF = 0x8000, /* EOF detected */
|
||||
EV_ERROR = 0x4000, /* error, data contains errno */
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
protected int fd;
|
||||
private Vector!kevent_t events;
|
||||
private Vector!kevent_t changes;
|
||||
private size_t changeCount;
|
||||
protected int fd;
|
||||
private Array!kevent_t events;
|
||||
private Array!kevent_t changes;
|
||||
private size_t changeCount;
|
||||
|
||||
/**
|
||||
* Returns: Maximal event count can be got at a time
|
||||
* (should be supported by the backend).
|
||||
*/
|
||||
override protected @property uint maxEvents()
|
||||
const pure nothrow @safe @nogc
|
||||
{
|
||||
return cast(uint) events.length;
|
||||
}
|
||||
/**
|
||||
* Returns: Maximal event count can be got at a time
|
||||
* (should be supported by the backend).
|
||||
*/
|
||||
override protected @property uint maxEvents()
|
||||
const pure nothrow @safe @nogc
|
||||
{
|
||||
return cast(uint) events.length;
|
||||
}
|
||||
|
||||
this() @nogc
|
||||
{
|
||||
super();
|
||||
this() @nogc
|
||||
{
|
||||
super();
|
||||
|
||||
if ((fd = kqueue()) == -1)
|
||||
{
|
||||
throw make!BadLoopException(defaultAllocator,
|
||||
"kqueue initialization failed");
|
||||
}
|
||||
events = Vector!kevent_t(64, MmapPool.instance);
|
||||
changes = Vector!kevent_t(64, MmapPool.instance);
|
||||
}
|
||||
if ((fd = kqueue()) == -1)
|
||||
{
|
||||
throw make!BadLoopException(defaultAllocator,
|
||||
"kqueue initialization failed");
|
||||
}
|
||||
events = Array!kevent_t(64, MmapPool.instance);
|
||||
changes = Array!kevent_t(64, MmapPool.instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees loop internals.
|
||||
*/
|
||||
~this() @nogc
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
/**
|
||||
* Frees loop internals.
|
||||
*/
|
||||
~this() @nogc
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
private void set(socket_t socket, short filter, ushort flags) @nogc
|
||||
{
|
||||
if (changes.length <= changeCount)
|
||||
{
|
||||
changes.length = changeCount + maxEvents;
|
||||
}
|
||||
EV_SET(&changes[changeCount],
|
||||
cast(ulong) socket,
|
||||
filter,
|
||||
flags,
|
||||
0U,
|
||||
0L,
|
||||
null);
|
||||
++changeCount;
|
||||
}
|
||||
private void set(socket_t socket, short filter, ushort flags) @nogc
|
||||
{
|
||||
if (changes.length <= changeCount)
|
||||
{
|
||||
changes.length = changeCount + maxEvents;
|
||||
}
|
||||
EV_SET(&changes[changeCount],
|
||||
cast(ulong) socket,
|
||||
filter,
|
||||
flags,
|
||||
0U,
|
||||
0L,
|
||||
null);
|
||||
++changeCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called if the backend configuration changes.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
* oldEvents = The events were already set.
|
||||
* events = The events should be set.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation was successful.
|
||||
*/
|
||||
override protected bool reify(SocketWatcher watcher,
|
||||
EventMask oldEvents,
|
||||
EventMask events) @nogc
|
||||
{
|
||||
if (events != oldEvents)
|
||||
{
|
||||
if (oldEvents & Event.read || oldEvents & Event.accept)
|
||||
{
|
||||
set(watcher.socket.handle, EVFILT_READ, EV_DELETE);
|
||||
}
|
||||
if (oldEvents & Event.write)
|
||||
{
|
||||
set(watcher.socket.handle, EVFILT_WRITE, EV_DELETE);
|
||||
}
|
||||
}
|
||||
if (events & (Event.read | events & Event.accept))
|
||||
{
|
||||
set(watcher.socket.handle, EVFILT_READ, EV_ADD | EV_ENABLE);
|
||||
}
|
||||
if (events & Event.write)
|
||||
{
|
||||
set(watcher.socket.handle, EVFILT_WRITE, EV_ADD | EV_DISPATCH);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Should be called if the backend configuration changes.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
* oldEvents = The events were already set.
|
||||
* events = The events should be set.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation was successful.
|
||||
*/
|
||||
override protected bool reify(SocketWatcher watcher,
|
||||
EventMask oldEvents,
|
||||
EventMask events) @nogc
|
||||
{
|
||||
if (events != oldEvents)
|
||||
{
|
||||
if (oldEvents & Event.read || oldEvents & Event.accept)
|
||||
{
|
||||
set(watcher.socket.handle, EVFILT_READ, EV_DELETE);
|
||||
}
|
||||
if (oldEvents & Event.write)
|
||||
{
|
||||
set(watcher.socket.handle, EVFILT_WRITE, EV_DELETE);
|
||||
}
|
||||
}
|
||||
if (events & (Event.read | events & Event.accept))
|
||||
{
|
||||
set(watcher.socket.handle, EVFILT_READ, EV_ADD | EV_ENABLE);
|
||||
}
|
||||
if (events & Event.write)
|
||||
{
|
||||
set(watcher.socket.handle, EVFILT_WRITE, EV_ADD | EV_DISPATCH);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual polling.
|
||||
*/
|
||||
protected override void poll() @nogc
|
||||
{
|
||||
timespec ts;
|
||||
blockTime.split!("seconds", "nsecs")(ts.tv_sec, ts.tv_nsec);
|
||||
/**
|
||||
* Does the actual polling.
|
||||
*/
|
||||
protected override void poll() @nogc
|
||||
{
|
||||
timespec ts;
|
||||
blockTime.split!("seconds", "nsecs")(ts.tv_sec, ts.tv_nsec);
|
||||
|
||||
if (changeCount > maxEvents)
|
||||
{
|
||||
events.length = changes.length;
|
||||
}
|
||||
if (changeCount > maxEvents)
|
||||
{
|
||||
events.length = changes.length;
|
||||
}
|
||||
|
||||
auto eventCount = kevent(fd,
|
||||
changes.get().ptr,
|
||||
cast(int) changeCount,
|
||||
events.get().ptr,
|
||||
maxEvents,
|
||||
&ts);
|
||||
changeCount = 0;
|
||||
auto eventCount = kevent(fd,
|
||||
changes.get().ptr,
|
||||
cast(int) changeCount,
|
||||
events.get().ptr,
|
||||
maxEvents,
|
||||
&ts);
|
||||
changeCount = 0;
|
||||
|
||||
if (eventCount < 0)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
{
|
||||
throw defaultAllocator.make!BadLoopException();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (eventCount < 0)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
{
|
||||
throw defaultAllocator.make!BadLoopException();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i; i < eventCount; ++i)
|
||||
{
|
||||
assert(connections.length > events[i].ident);
|
||||
for (int i; i < eventCount; ++i)
|
||||
{
|
||||
assert(connections.length > events[i].ident);
|
||||
|
||||
auto transport = cast(StreamTransport) connections[events[i].ident];
|
||||
// If it is a ConnectionWatcher. Accept connections.
|
||||
if (transport is null)
|
||||
{
|
||||
auto connection = cast(ConnectionWatcher) connections[events[i].ident];
|
||||
assert(connection !is null);
|
||||
auto transport = cast(StreamTransport) connections[events[i].ident];
|
||||
// If it is a ConnectionWatcher. Accept connections.
|
||||
if (transport is null)
|
||||
{
|
||||
auto connection = cast(ConnectionWatcher) connections[events[i].ident];
|
||||
assert(connection !is null);
|
||||
|
||||
acceptConnections(connection);
|
||||
}
|
||||
else if (events[i].flags & EV_ERROR)
|
||||
{
|
||||
kill(transport);
|
||||
}
|
||||
else if (events[i].filter == EVFILT_READ)
|
||||
{
|
||||
SocketException exception;
|
||||
try
|
||||
{
|
||||
ptrdiff_t received;
|
||||
do
|
||||
{
|
||||
received = transport.socket.receive(transport.output[]);
|
||||
transport.output += received;
|
||||
}
|
||||
while (received);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
exception = e;
|
||||
}
|
||||
if (transport.socket.disconnected)
|
||||
{
|
||||
kill(transport, exception);
|
||||
}
|
||||
else if (transport.output.length)
|
||||
{
|
||||
pendings.enqueue(transport);
|
||||
}
|
||||
}
|
||||
else if (events[i].filter == EVFILT_WRITE)
|
||||
{
|
||||
transport.writeReady = true;
|
||||
if (transport.input.length)
|
||||
{
|
||||
feed(transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
acceptConnections(connection);
|
||||
}
|
||||
else if (events[i].flags & EV_ERROR)
|
||||
{
|
||||
kill(transport);
|
||||
}
|
||||
else if (events[i].filter == EVFILT_READ)
|
||||
{
|
||||
SocketException exception;
|
||||
try
|
||||
{
|
||||
ptrdiff_t received;
|
||||
do
|
||||
{
|
||||
received = transport.socket.receive(transport.output[]);
|
||||
transport.output += received;
|
||||
}
|
||||
while (received);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
exception = e;
|
||||
}
|
||||
if (transport.socket.disconnected)
|
||||
{
|
||||
kill(transport, exception);
|
||||
}
|
||||
else if (transport.output.length)
|
||||
{
|
||||
pendings.enqueue(transport);
|
||||
}
|
||||
}
|
||||
else if (events[i].filter == EVFILT_WRITE)
|
||||
{
|
||||
transport.writeReady = true;
|
||||
if (transport.input.length)
|
||||
{
|
||||
feed(transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: The blocking time.
|
||||
*/
|
||||
override protected @property inout(Duration) blockTime()
|
||||
inout @nogc @safe pure nothrow
|
||||
{
|
||||
return min(super.blockTime, 1.dur!"seconds");
|
||||
}
|
||||
/**
|
||||
* Returns: The blocking time.
|
||||
*/
|
||||
override protected @property inout(Duration) blockTime()
|
||||
inout @nogc @safe pure nothrow
|
||||
{
|
||||
return min(super.blockTime, 1.dur!"seconds");
|
||||
}
|
||||
|
||||
/**
|
||||
* If the transport couldn't send the data, the further sending should
|
||||
* be handled by the event loop.
|
||||
*
|
||||
* Params:
|
||||
* transport = Transport.
|
||||
* exception = Exception thrown on sending.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation could be successfully
|
||||
* completed or scheduled, $(D_KEYWORD false) otherwise (the
|
||||
* transport will be destroyed then).
|
||||
*/
|
||||
protected override bool feed(StreamTransport transport,
|
||||
SocketException exception = null) @nogc
|
||||
{
|
||||
if (!super.feed(transport, exception))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!transport.writeReady)
|
||||
{
|
||||
set(transport.socket.handle, EVFILT_WRITE, EV_DISPATCH);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* If the transport couldn't send the data, the further sending should
|
||||
* be handled by the event loop.
|
||||
*
|
||||
* Params:
|
||||
* transport = Transport.
|
||||
* exception = Exception thrown on sending.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation could be successfully
|
||||
* completed or scheduled, $(D_KEYWORD false) otherwise (the
|
||||
* transport will be destroyed then).
|
||||
*/
|
||||
protected override bool feed(StreamTransport transport,
|
||||
SocketException exception = null) @nogc
|
||||
{
|
||||
if (!super.feed(transport, exception))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!transport.writeReady)
|
||||
{
|
||||
set(transport.socket.handle, EVFILT_WRITE, EV_DISPATCH);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import tanya.async.protocol;
|
||||
import tanya.async.transport;
|
||||
import tanya.async.watcher;
|
||||
import tanya.container.buffer;
|
||||
import tanya.container.vector;
|
||||
import tanya.container.array;
|
||||
import tanya.memory;
|
||||
import tanya.memory.mmappool;
|
||||
import tanya.network.socket;
|
||||
@ -27,371 +27,374 @@ import tanya.network.socket;
|
||||
*/
|
||||
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.
|
||||
package bool writeReady;
|
||||
/// Received notification that the underlying socket is write-ready.
|
||||
package bool writeReady;
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* loop = Event loop.
|
||||
* socket = Socket.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE loop !is null && socket !is null)
|
||||
*/
|
||||
this(SelectorLoop loop, ConnectedSocket socket) @nogc
|
||||
in
|
||||
{
|
||||
assert(loop !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
super(socket);
|
||||
this.loop = loop;
|
||||
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
|
||||
input = WriteBuffer!ubyte(8192, MmapPool.instance);
|
||||
active = true;
|
||||
}
|
||||
/**
|
||||
* Params:
|
||||
* loop = Event loop.
|
||||
* socket = Socket.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE loop !is null && socket !is null)
|
||||
*/
|
||||
this(SelectorLoop loop, ConnectedSocket socket) @nogc
|
||||
in
|
||||
{
|
||||
assert(loop !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
super(socket);
|
||||
this.loop = loop;
|
||||
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
|
||||
input = WriteBuffer!ubyte(8192, MmapPool.instance);
|
||||
active = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Socket.
|
||||
*
|
||||
* Postcondition: $(D_INLINECODE socket !is null)
|
||||
*/
|
||||
override @property ConnectedSocket socket() pure nothrow @safe @nogc
|
||||
out (socket)
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
return cast(ConnectedSocket) socket_;
|
||||
}
|
||||
/**
|
||||
* Returns: Socket.
|
||||
*
|
||||
* Postcondition: $(D_INLINECODE socket !is null)
|
||||
*/
|
||||
override @property ConnectedSocket socket() pure nothrow @safe @nogc
|
||||
out (socket)
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
return cast(ConnectedSocket) socket_;
|
||||
}
|
||||
|
||||
private @property void socket(ConnectedSocket socket) pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
socket_ = socket;
|
||||
}
|
||||
private @property void socket(ConnectedSocket socket)
|
||||
pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
socket_ = socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Application protocol.
|
||||
*/
|
||||
@property Protocol protocol() pure nothrow @safe @nogc
|
||||
{
|
||||
return protocol_;
|
||||
}
|
||||
/**
|
||||
* Returns: Application protocol.
|
||||
*/
|
||||
@property Protocol protocol() pure nothrow @safe @nogc
|
||||
{
|
||||
return protocol_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the protocol.
|
||||
*
|
||||
* The protocol is deallocated by the event loop, it should currently be
|
||||
* allocated with $(D_PSYMBOL MmapPool).
|
||||
*
|
||||
* Params:
|
||||
* protocol = Application protocol.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE protocol !is null)
|
||||
*/
|
||||
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(protocol !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
protocol_ = protocol;
|
||||
}
|
||||
/**
|
||||
* Switches the protocol.
|
||||
*
|
||||
* The protocol is deallocated by the event loop, it should currently be
|
||||
* allocated with $(D_PSYMBOL MmapPool).
|
||||
*
|
||||
* Params:
|
||||
* protocol = Application protocol.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE protocol !is null)
|
||||
*/
|
||||
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(protocol !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
protocol_ = protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns $(D_PARAM true) if the transport is closing or closed.
|
||||
*/
|
||||
bool isClosing() const pure nothrow @safe @nogc
|
||||
{
|
||||
return closing;
|
||||
}
|
||||
/**
|
||||
* Returns $(D_PARAM true) if the transport is closing or closed.
|
||||
*/
|
||||
bool isClosing() const pure nothrow @safe @nogc
|
||||
{
|
||||
return closing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the transport.
|
||||
*
|
||||
* Buffered data will be flushed. No more data will be received.
|
||||
*/
|
||||
void close() @nogc
|
||||
{
|
||||
closing = true;
|
||||
loop.reify(this, EventMask(Event.read, Event.write), EventMask(Event.write));
|
||||
}
|
||||
/**
|
||||
* Close the transport.
|
||||
*
|
||||
* Buffered data will be flushed. No more data will be received.
|
||||
*/
|
||||
void close() @nogc
|
||||
{
|
||||
closing = true;
|
||||
loop.reify(this,
|
||||
EventMask(Event.read, Event.write),
|
||||
EventMask(Event.write));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the watcher callback.
|
||||
*/
|
||||
override void invoke() @nogc
|
||||
{
|
||||
if (output.length)
|
||||
{
|
||||
protocol.received(output[0 .. $]);
|
||||
output.clear();
|
||||
if (isClosing() && input.length == 0)
|
||||
{
|
||||
loop.kill(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol.disconnected(exception);
|
||||
MmapPool.instance.dispose(protocol_);
|
||||
defaultAllocator.dispose(exception);
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Invokes the watcher callback.
|
||||
*/
|
||||
override void invoke() @nogc
|
||||
{
|
||||
if (output.length)
|
||||
{
|
||||
protocol.received(output[0 .. $]);
|
||||
output.clear();
|
||||
if (isClosing() && input.length == 0)
|
||||
{
|
||||
loop.kill(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol.disconnected(exception);
|
||||
MmapPool.instance.dispose(protocol_);
|
||||
defaultAllocator.dispose(exception);
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write some data to the transport.
|
||||
*
|
||||
* Params:
|
||||
* data = Data to send.
|
||||
*/
|
||||
void write(ubyte[] data) @nogc
|
||||
{
|
||||
if (!data.length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Try to write if the socket is write ready.
|
||||
if (writeReady)
|
||||
{
|
||||
ptrdiff_t sent;
|
||||
SocketException exception;
|
||||
try
|
||||
{
|
||||
sent = socket.send(data);
|
||||
if (sent == 0)
|
||||
{
|
||||
writeReady = false;
|
||||
}
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
writeReady = false;
|
||||
exception = e;
|
||||
}
|
||||
if (sent < data.length)
|
||||
{
|
||||
input ~= data[sent..$];
|
||||
loop.feed(this, exception);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
input ~= data;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Write some data to the transport.
|
||||
*
|
||||
* Params:
|
||||
* data = Data to send.
|
||||
*/
|
||||
void write(ubyte[] data) @nogc
|
||||
{
|
||||
if (!data.length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Try to write if the socket is write ready.
|
||||
if (writeReady)
|
||||
{
|
||||
ptrdiff_t sent;
|
||||
SocketException exception;
|
||||
try
|
||||
{
|
||||
sent = socket.send(data);
|
||||
if (sent == 0)
|
||||
{
|
||||
writeReady = false;
|
||||
}
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
writeReady = false;
|
||||
exception = e;
|
||||
}
|
||||
if (sent < data.length)
|
||||
{
|
||||
input ~= data[sent..$];
|
||||
loop.feed(this, exception);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
input ~= data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SelectorLoop : Loop
|
||||
{
|
||||
/// Pending connections.
|
||||
protected Vector!SocketWatcher connections;
|
||||
/// Pending connections.
|
||||
protected Array!SocketWatcher connections;
|
||||
|
||||
this() @nogc
|
||||
{
|
||||
super();
|
||||
connections = Vector!SocketWatcher(maxEvents, MmapPool.instance);
|
||||
}
|
||||
this() @nogc
|
||||
{
|
||||
super();
|
||||
connections = Array!SocketWatcher(maxEvents, MmapPool.instance);
|
||||
}
|
||||
|
||||
~this() @nogc
|
||||
{
|
||||
foreach (ref connection; connections)
|
||||
{
|
||||
// We want to free only the transports. ConnectionWatcher are created by the
|
||||
// user and should be freed by himself.
|
||||
if (cast(StreamTransport) connection !is null)
|
||||
{
|
||||
MmapPool.instance.dispose(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
~this() @nogc
|
||||
{
|
||||
foreach (ref connection; connections)
|
||||
{
|
||||
// We want to free only the transports. ConnectionWatcher are
|
||||
// created by the user and should be freed by himself.
|
||||
if (cast(StreamTransport) connection !is null)
|
||||
{
|
||||
MmapPool.instance.dispose(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called if the backend configuration changes.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
* oldEvents = The events were already set.
|
||||
* events = The events should be set.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation was successful.
|
||||
*/
|
||||
override abstract protected bool reify(SocketWatcher watcher,
|
||||
EventMask oldEvents,
|
||||
EventMask events) @nogc;
|
||||
/**
|
||||
* Should be called if the backend configuration changes.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
* oldEvents = The events were already set.
|
||||
* events = The events should be set.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation was successful.
|
||||
*/
|
||||
override abstract protected bool reify(SocketWatcher watcher,
|
||||
EventMask oldEvents,
|
||||
EventMask events) @nogc;
|
||||
|
||||
/**
|
||||
* Kills the watcher and closes the connection.
|
||||
*
|
||||
* Params:
|
||||
* transport = Transport.
|
||||
* exception = Occurred exception.
|
||||
*/
|
||||
protected void kill(StreamTransport transport,
|
||||
SocketException exception = null) @nogc
|
||||
in
|
||||
{
|
||||
assert(transport !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
transport.socket.shutdown();
|
||||
defaultAllocator.dispose(transport.socket);
|
||||
transport.exception = exception;
|
||||
pendings.enqueue(transport);
|
||||
}
|
||||
/**
|
||||
* Kills the watcher and closes the connection.
|
||||
*
|
||||
* Params:
|
||||
* transport = Transport.
|
||||
* exception = Occurred exception.
|
||||
*/
|
||||
protected void kill(StreamTransport transport,
|
||||
SocketException exception = null) @nogc
|
||||
in
|
||||
{
|
||||
assert(transport !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
transport.socket.shutdown();
|
||||
defaultAllocator.dispose(transport.socket);
|
||||
transport.exception = exception;
|
||||
pendings.enqueue(transport);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the transport couldn't send the data, the further sending should
|
||||
* be handled by the event loop.
|
||||
*
|
||||
* Params:
|
||||
* transport = Transport.
|
||||
* exception = Exception thrown on sending.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation could be successfully
|
||||
* completed or scheduled, $(D_KEYWORD false) otherwise (the
|
||||
* transport will be destroyed then).
|
||||
*/
|
||||
protected bool feed(StreamTransport transport,
|
||||
SocketException exception = null) @nogc
|
||||
in
|
||||
{
|
||||
assert(transport !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
while (transport.input.length && transport.writeReady)
|
||||
{
|
||||
try
|
||||
{
|
||||
ptrdiff_t sent = transport.socket.send(transport.input[]);
|
||||
if (sent == 0)
|
||||
{
|
||||
transport.writeReady = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
transport.input += sent;
|
||||
}
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
exception = e;
|
||||
transport.writeReady = false;
|
||||
}
|
||||
}
|
||||
if (exception !is null)
|
||||
{
|
||||
kill(transport, exception);
|
||||
return false;
|
||||
}
|
||||
if (transport.input.length == 0 && transport.isClosing())
|
||||
{
|
||||
kill(transport);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* If the transport couldn't send the data, the further sending should
|
||||
* be handled by the event loop.
|
||||
*
|
||||
* Params:
|
||||
* transport = Transport.
|
||||
* exception = Exception thrown on sending.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation could be successfully
|
||||
* completed or scheduled, $(D_KEYWORD false) otherwise (the
|
||||
* transport will be destroyed then).
|
||||
*/
|
||||
protected bool feed(StreamTransport transport,
|
||||
SocketException exception = null) @nogc
|
||||
in
|
||||
{
|
||||
assert(transport !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
while (transport.input.length && transport.writeReady)
|
||||
{
|
||||
try
|
||||
{
|
||||
ptrdiff_t sent = transport.socket.send(transport.input[]);
|
||||
if (sent == 0)
|
||||
{
|
||||
transport.writeReady = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
transport.input += sent;
|
||||
}
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
exception = e;
|
||||
transport.writeReady = false;
|
||||
}
|
||||
}
|
||||
if (exception !is null)
|
||||
{
|
||||
kill(transport, exception);
|
||||
return false;
|
||||
}
|
||||
if (transport.input.length == 0 && transport.isClosing())
|
||||
{
|
||||
kill(transport);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start watching.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
*/
|
||||
override void start(ConnectionWatcher watcher) @nogc
|
||||
{
|
||||
if (watcher.active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Start watching.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
*/
|
||||
override void start(ConnectionWatcher watcher) @nogc
|
||||
{
|
||||
if (watcher.active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (connections.length <= watcher.socket)
|
||||
{
|
||||
connections.length = watcher.socket.handle + maxEvents / 2;
|
||||
}
|
||||
connections[watcher.socket.handle] = watcher;
|
||||
if (connections.length <= watcher.socket)
|
||||
{
|
||||
connections.length = watcher.socket.handle + maxEvents / 2;
|
||||
}
|
||||
connections[watcher.socket.handle] = watcher;
|
||||
|
||||
super.start(watcher);
|
||||
}
|
||||
super.start(watcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept incoming connections.
|
||||
*
|
||||
* Params:
|
||||
* connection = Connection watcher ready to accept.
|
||||
*/
|
||||
package void acceptConnections(ConnectionWatcher connection) @nogc
|
||||
in
|
||||
{
|
||||
assert(connection !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ConnectedSocket client;
|
||||
try
|
||||
{
|
||||
client = (cast(StreamSocket) connection.socket).accept();
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
defaultAllocator.dispose(e);
|
||||
break;
|
||||
}
|
||||
if (client is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* Accept incoming connections.
|
||||
*
|
||||
* Params:
|
||||
* connection = Connection watcher ready to accept.
|
||||
*/
|
||||
package void acceptConnections(ConnectionWatcher connection) @nogc
|
||||
in
|
||||
{
|
||||
assert(connection !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ConnectedSocket client;
|
||||
try
|
||||
{
|
||||
client = (cast(StreamSocket) connection.socket).accept();
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
defaultAllocator.dispose(e);
|
||||
break;
|
||||
}
|
||||
if (client is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
StreamTransport transport;
|
||||
StreamTransport transport;
|
||||
|
||||
if (connections.length > client.handle)
|
||||
{
|
||||
transport = cast(StreamTransport) connections[client.handle];
|
||||
}
|
||||
else
|
||||
{
|
||||
connections.length = client.handle + maxEvents / 2;
|
||||
}
|
||||
if (transport is null)
|
||||
{
|
||||
transport = MmapPool.instance.make!StreamTransport(this, client);
|
||||
connections[client.handle] = transport;
|
||||
}
|
||||
else
|
||||
{
|
||||
transport.socket = client;
|
||||
}
|
||||
if (connections.length > client.handle)
|
||||
{
|
||||
transport = cast(StreamTransport) connections[client.handle];
|
||||
}
|
||||
else
|
||||
{
|
||||
connections.length = client.handle + maxEvents / 2;
|
||||
}
|
||||
if (transport is null)
|
||||
{
|
||||
transport = MmapPool.instance.make!StreamTransport(this, client);
|
||||
connections[client.handle] = transport;
|
||||
}
|
||||
else
|
||||
{
|
||||
transport.socket = client;
|
||||
}
|
||||
|
||||
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
|
||||
connection.incoming.enqueue(transport);
|
||||
}
|
||||
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
|
||||
connection.incoming.enqueue(transport);
|
||||
}
|
||||
|
||||
if (!connection.incoming.empty)
|
||||
{
|
||||
pendings.enqueue(connection);
|
||||
}
|
||||
}
|
||||
if (!connection.incoming.empty)
|
||||
{
|
||||
pendings.enqueue(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,12 +21,12 @@ import core.sys.windows.windef;
|
||||
*/
|
||||
class State
|
||||
{
|
||||
/// For internal use by Windows API.
|
||||
align(1) OVERLAPPED overlapped;
|
||||
/// For internal use by Windows API.
|
||||
align(1) OVERLAPPED overlapped;
|
||||
|
||||
/// File/socket handle.
|
||||
HANDLE handle;
|
||||
/// File/socket handle.
|
||||
HANDLE handle;
|
||||
|
||||
/// For keeping events or event masks.
|
||||
int event;
|
||||
/// For keeping events or event masks.
|
||||
int event;
|
||||
}
|
||||
|
@ -15,50 +15,50 @@
|
||||
*
|
||||
* class EchoProtocol : TransmissionControlProtocol
|
||||
* {
|
||||
* private DuplexTransport transport;
|
||||
* private DuplexTransport transport;
|
||||
*
|
||||
* void received(in ubyte[] data) @nogc
|
||||
* {
|
||||
* transport.write(data);
|
||||
* }
|
||||
* void received(in ubyte[] data) @nogc
|
||||
* {
|
||||
* transport.write(data);
|
||||
* }
|
||||
*
|
||||
* void connected(DuplexTransport transport) @nogc
|
||||
* {
|
||||
* this.transport = transport;
|
||||
* }
|
||||
* void connected(DuplexTransport transport) @nogc
|
||||
* {
|
||||
* this.transport = transport;
|
||||
* }
|
||||
*
|
||||
* void disconnected(SocketException e) @nogc
|
||||
* {
|
||||
* }
|
||||
* void disconnected(SocketException e) @nogc
|
||||
* {
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 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)
|
||||
* {
|
||||
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.INET);
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.INET);
|
||||
* sock.blocking = false;
|
||||
* }
|
||||
* version (Windows)
|
||||
* {
|
||||
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.INET);
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.INET);
|
||||
* sock.blocking = false;
|
||||
* }
|
||||
*
|
||||
* sock.bind(address);
|
||||
* sock.listen(5);
|
||||
* sock.bind(address);
|
||||
* sock.listen(5);
|
||||
*
|
||||
* auto io = defaultAllocator.make!ConnectionWatcher(sock);
|
||||
* io.setProtocol!EchoProtocol;
|
||||
* auto io = defaultAllocator.make!ConnectionWatcher(sock);
|
||||
* io.setProtocol!EchoProtocol;
|
||||
*
|
||||
* defaultLoop.start(io);
|
||||
* defaultLoop.run();
|
||||
* defaultLoop.start(io);
|
||||
* defaultLoop.run();
|
||||
*
|
||||
* sock.shutdown();
|
||||
* defaultAllocator.dispose(io);
|
||||
* defaultAllocator.dispose(sock);
|
||||
* defaultAllocator.dispose(address);
|
||||
* sock.shutdown();
|
||||
* defaultAllocator.dispose(io);
|
||||
* defaultAllocator.dispose(sock);
|
||||
* defaultAllocator.dispose(address);
|
||||
* }
|
||||
* ---
|
||||
*/
|
||||
@ -81,33 +81,33 @@ version (DisableBackends)
|
||||
}
|
||||
else version (linux)
|
||||
{
|
||||
import tanya.async.event.epoll;
|
||||
version = Epoll;
|
||||
import tanya.async.event.epoll;
|
||||
version = Epoll;
|
||||
}
|
||||
else version (Windows)
|
||||
{
|
||||
import tanya.async.event.iocp;
|
||||
version = IOCP;
|
||||
import tanya.async.event.iocp;
|
||||
version = IOCP;
|
||||
}
|
||||
else version (OSX)
|
||||
{
|
||||
version = Kqueue;
|
||||
version = Kqueue;
|
||||
}
|
||||
else version (iOS)
|
||||
{
|
||||
version = Kqueue;
|
||||
version = Kqueue;
|
||||
}
|
||||
else version (FreeBSD)
|
||||
{
|
||||
version = Kqueue;
|
||||
version = Kqueue;
|
||||
}
|
||||
else version (OpenBSD)
|
||||
{
|
||||
version = Kqueue;
|
||||
version = Kqueue;
|
||||
}
|
||||
else version (DragonFlyBSD)
|
||||
{
|
||||
version = Kqueue;
|
||||
version = Kqueue;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,11 +115,11 @@ else version (DragonFlyBSD)
|
||||
*/
|
||||
enum Event : uint
|
||||
{
|
||||
none = 0x00, /// No events.
|
||||
read = 0x01, /// Non-blocking read call.
|
||||
write = 0x02, /// Non-blocking write call.
|
||||
accept = 0x04, /// Connection made.
|
||||
error = 0x80000000, /// Sent when an error occurs.
|
||||
none = 0x00, /// No events.
|
||||
read = 0x01, /// Non-blocking read call.
|
||||
write = 0x02, /// Non-blocking write call.
|
||||
accept = 0x04, /// Connection made.
|
||||
error = 0x80000000, /// Sent when an error occurs.
|
||||
}
|
||||
|
||||
alias EventMask = BitFlags!Event;
|
||||
@ -129,150 +129,150 @@ alias EventMask = BitFlags!Event;
|
||||
*/
|
||||
abstract class Loop
|
||||
{
|
||||
private bool done;
|
||||
private bool done;
|
||||
|
||||
/// Pending watchers.
|
||||
protected Queue!Watcher pendings;
|
||||
/// Pending watchers.
|
||||
protected Queue!Watcher pendings;
|
||||
|
||||
/**
|
||||
* Returns: Maximal event count can be got at a time
|
||||
* (should be supported by the backend).
|
||||
*/
|
||||
protected @property uint maxEvents()
|
||||
const pure nothrow @safe @nogc
|
||||
{
|
||||
return 128U;
|
||||
}
|
||||
/**
|
||||
* Returns: Maximal event count can be got at a time
|
||||
* (should be supported by the backend).
|
||||
*/
|
||||
protected @property uint maxEvents()
|
||||
const pure nothrow @safe @nogc
|
||||
{
|
||||
return 128U;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the loop.
|
||||
*/
|
||||
this() @nogc
|
||||
{
|
||||
pendings = Queue!Watcher(MmapPool.instance);
|
||||
}
|
||||
/**
|
||||
* Initializes the loop.
|
||||
*/
|
||||
this() @nogc
|
||||
{
|
||||
pendings = Queue!Watcher(MmapPool.instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees loop internals.
|
||||
*/
|
||||
~this() @nogc
|
||||
{
|
||||
foreach (w; pendings)
|
||||
{
|
||||
MmapPool.instance.dispose(w);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Frees loop internals.
|
||||
*/
|
||||
~this() @nogc
|
||||
{
|
||||
foreach (w; pendings)
|
||||
{
|
||||
MmapPool.instance.dispose(w);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the loop.
|
||||
*/
|
||||
void run() @nogc
|
||||
{
|
||||
done = false;
|
||||
do
|
||||
{
|
||||
poll();
|
||||
/**
|
||||
* Starts the loop.
|
||||
*/
|
||||
void run() @nogc
|
||||
{
|
||||
done = false;
|
||||
do
|
||||
{
|
||||
poll();
|
||||
|
||||
// Invoke pendings
|
||||
foreach (ref w; pendings)
|
||||
{
|
||||
w.invoke();
|
||||
}
|
||||
}
|
||||
while (!done);
|
||||
}
|
||||
// Invoke pendings
|
||||
foreach (ref w; pendings)
|
||||
{
|
||||
w.invoke();
|
||||
}
|
||||
}
|
||||
while (!done);
|
||||
}
|
||||
|
||||
/**
|
||||
* Break out of the loop.
|
||||
*/
|
||||
void unloop() @safe pure nothrow @nogc
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
/**
|
||||
* Break out of the loop.
|
||||
*/
|
||||
void unloop() @safe pure nothrow @nogc
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start watching.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
*/
|
||||
void start(ConnectionWatcher watcher) @nogc
|
||||
{
|
||||
if (watcher.active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
watcher.active = true;
|
||||
/**
|
||||
* Start watching.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
*/
|
||||
void start(ConnectionWatcher watcher) @nogc
|
||||
{
|
||||
if (watcher.active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
watcher.active = true;
|
||||
|
||||
reify(watcher, EventMask(Event.none), EventMask(Event.accept));
|
||||
}
|
||||
reify(watcher, EventMask(Event.none), EventMask(Event.accept));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop watching.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
*/
|
||||
void stop(ConnectionWatcher watcher) @nogc
|
||||
{
|
||||
if (!watcher.active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
watcher.active = false;
|
||||
/**
|
||||
* Stop watching.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
*/
|
||||
void stop(ConnectionWatcher watcher) @nogc
|
||||
{
|
||||
if (!watcher.active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
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.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
* oldEvents = The events were already set.
|
||||
* events = The events should be set.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation was successful.
|
||||
*/
|
||||
abstract protected bool reify(SocketWatcher watcher,
|
||||
EventMask oldEvents,
|
||||
EventMask events) @nogc;
|
||||
/**
|
||||
* Should be called if the backend configuration changes.
|
||||
*
|
||||
* Params:
|
||||
* watcher = Watcher.
|
||||
* oldEvents = The events were already set.
|
||||
* events = The events should be set.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if the operation was successful.
|
||||
*/
|
||||
abstract protected bool reify(SocketWatcher watcher,
|
||||
EventMask oldEvents,
|
||||
EventMask events) @nogc;
|
||||
|
||||
/**
|
||||
* Returns: The blocking time.
|
||||
*/
|
||||
protected @property inout(Duration) blockTime()
|
||||
inout @safe pure nothrow @nogc
|
||||
{
|
||||
// Don't block if we have to do.
|
||||
return pendings.empty ? blockTime_ : Duration.zero;
|
||||
}
|
||||
/**
|
||||
* Returns: The blocking time.
|
||||
*/
|
||||
protected @property inout(Duration) blockTime()
|
||||
inout @safe pure nothrow @nogc
|
||||
{
|
||||
// Don't block if we have to do.
|
||||
return pendings.empty ? blockTime_ : Duration.zero;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the blocking time for IO watchers.
|
||||
*
|
||||
* Params:
|
||||
* blockTime = The blocking time. Cannot be larger than
|
||||
* $(D_PSYMBOL maxBlockTime).
|
||||
*/
|
||||
protected @property void blockTime(in Duration blockTime) @safe pure nothrow @nogc
|
||||
in
|
||||
{
|
||||
assert(blockTime <= 1.dur!"hours", "Too long to wait.");
|
||||
assert(!blockTime.isNegative);
|
||||
}
|
||||
body
|
||||
{
|
||||
blockTime_ = blockTime;
|
||||
}
|
||||
/**
|
||||
* Sets the blocking time for IO watchers.
|
||||
*
|
||||
* Params:
|
||||
* blockTime = The blocking time. Cannot be larger than
|
||||
* $(D_PSYMBOL maxBlockTime).
|
||||
*/
|
||||
protected @property void blockTime(in Duration blockTime) @safe pure nothrow @nogc
|
||||
in
|
||||
{
|
||||
assert(blockTime <= 1.dur!"hours", "Too long to wait.");
|
||||
assert(!blockTime.isNegative);
|
||||
}
|
||||
body
|
||||
{
|
||||
blockTime_ = blockTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual polling.
|
||||
*/
|
||||
abstract protected void poll() @nogc;
|
||||
/**
|
||||
* Does the actual polling.
|
||||
*/
|
||||
abstract protected void poll() @nogc;
|
||||
|
||||
/// Maximal block time.
|
||||
protected Duration blockTime_ = 1.dur!"minutes";
|
||||
/// Maximal block time.
|
||||
protected Duration blockTime_ = 1.dur!"minutes";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,17 +280,17 @@ abstract class Loop
|
||||
*/
|
||||
class BadLoopException : Exception
|
||||
{
|
||||
/**
|
||||
* Params:
|
||||
* file = The file where the exception occurred.
|
||||
* line = The line number where the exception occurred.
|
||||
* next = The previous exception in the chain of exceptions, if any.
|
||||
*/
|
||||
this(string file = __FILE__, size_t line = __LINE__, Throwable next = null)
|
||||
pure nothrow const @safe @nogc
|
||||
{
|
||||
super("Event loop cannot be initialized.", file, line, next);
|
||||
}
|
||||
/**
|
||||
* Params:
|
||||
* file = The file where the exception occurred.
|
||||
* line = The line number where the exception occurred.
|
||||
* next = The previous exception in the chain of exceptions, if any.
|
||||
*/
|
||||
this(string file = __FILE__, size_t line = __LINE__, Throwable next = null)
|
||||
pure nothrow const @safe @nogc
|
||||
{
|
||||
super("Event loop cannot be initialized.", file, line, next);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -302,24 +302,24 @@ class BadLoopException : Exception
|
||||
*/
|
||||
@property Loop defaultLoop() @nogc
|
||||
{
|
||||
if (defaultLoop_ !is null)
|
||||
{
|
||||
return defaultLoop_;
|
||||
}
|
||||
version (Epoll)
|
||||
{
|
||||
defaultLoop_ = MmapPool.instance.make!EpollLoop;
|
||||
}
|
||||
else version (IOCP)
|
||||
{
|
||||
defaultLoop_ = MmapPool.instance.make!IOCPLoop;
|
||||
}
|
||||
else version (Kqueue)
|
||||
{
|
||||
import tanya.async.event.kqueue;
|
||||
defaultLoop_ = MmapPool.instance.make!KqueueLoop;
|
||||
}
|
||||
return defaultLoop_;
|
||||
if (defaultLoop_ !is null)
|
||||
{
|
||||
return defaultLoop_;
|
||||
}
|
||||
version (Epoll)
|
||||
{
|
||||
defaultLoop_ = MmapPool.instance.make!EpollLoop;
|
||||
}
|
||||
else version (IOCP)
|
||||
{
|
||||
defaultLoop_ = MmapPool.instance.make!IOCPLoop;
|
||||
}
|
||||
else version (Kqueue)
|
||||
{
|
||||
import tanya.async.event.kqueue;
|
||||
defaultLoop_ = MmapPool.instance.make!KqueueLoop;
|
||||
}
|
||||
return defaultLoop_;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -331,16 +331,16 @@ class BadLoopException : Exception
|
||||
* your implementation to this property.
|
||||
*
|
||||
* Params:
|
||||
* loop = The event loop.
|
||||
* loop = The event loop.
|
||||
*/
|
||||
@property void defaultLoop(Loop loop) @nogc
|
||||
in
|
||||
{
|
||||
assert(loop !is null);
|
||||
assert(loop !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
defaultLoop_ = loop;
|
||||
defaultLoop_ = loop;
|
||||
}
|
||||
|
||||
private Loop defaultLoop_;
|
||||
|
@ -18,28 +18,28 @@ import tanya.async.transport;
|
||||
*/
|
||||
interface Protocol
|
||||
{
|
||||
/**
|
||||
* Params:
|
||||
* data = Read data.
|
||||
*/
|
||||
void received(in ubyte[] data) @nogc;
|
||||
/**
|
||||
* Params:
|
||||
* data = Read data.
|
||||
*/
|
||||
void received(in ubyte[] data) @nogc;
|
||||
|
||||
/**
|
||||
* Called when a connection is made.
|
||||
*
|
||||
* Params:
|
||||
* transport = Protocol transport.
|
||||
*/
|
||||
void connected(DuplexTransport transport) @nogc;
|
||||
/**
|
||||
* Called when a connection is made.
|
||||
*
|
||||
* Params:
|
||||
* transport = Protocol transport.
|
||||
*/
|
||||
void connected(DuplexTransport transport) @nogc;
|
||||
|
||||
/**
|
||||
* Called when a connection is lost.
|
||||
*
|
||||
* Params:
|
||||
* exception = $(D_PSYMBOL Exception) if an error caused
|
||||
* the disconnect, $(D_KEYWORD null) otherwise.
|
||||
*/
|
||||
void disconnected(SocketException exception) @nogc;
|
||||
/**
|
||||
* Called when a connection is lost.
|
||||
*
|
||||
* Params:
|
||||
* exception = $(D_PSYMBOL Exception) if an error caused
|
||||
* the disconnect, $(D_KEYWORD null) otherwise.
|
||||
*/
|
||||
void disconnected(SocketException exception) @nogc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,13 +32,13 @@ interface ReadTransport : Transport
|
||||
*/
|
||||
interface WriteTransport : Transport
|
||||
{
|
||||
/**
|
||||
* Write some data to the transport.
|
||||
*
|
||||
* Params:
|
||||
* data = Data to send.
|
||||
*/
|
||||
void write(ubyte[] data) @nogc;
|
||||
/**
|
||||
* Write some data to the transport.
|
||||
*
|
||||
* Params:
|
||||
* data = Data to send.
|
||||
*/
|
||||
void write(ubyte[] data) @nogc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,46 +46,46 @@ interface WriteTransport : Transport
|
||||
*/
|
||||
interface DuplexTransport : ReadTransport, WriteTransport
|
||||
{
|
||||
/**
|
||||
* Returns: Application protocol.
|
||||
*
|
||||
* Postcondition: $(D_INLINECODE protocol !is null)
|
||||
*/
|
||||
@property Protocol protocol() pure nothrow @safe @nogc
|
||||
out (protocol)
|
||||
{
|
||||
assert(protocol !is null);
|
||||
}
|
||||
/**
|
||||
* Returns: Application protocol.
|
||||
*
|
||||
* Postcondition: $(D_INLINECODE protocol !is null)
|
||||
*/
|
||||
@property Protocol protocol() pure nothrow @safe @nogc
|
||||
out (protocol)
|
||||
{
|
||||
assert(protocol !is null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the protocol.
|
||||
*
|
||||
* The protocol is deallocated by the event loop, it should currently be
|
||||
* allocated with $(D_PSYMBOL MmapPool).
|
||||
*
|
||||
* Params:
|
||||
* protocol = Application protocol.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE protocol !is null)
|
||||
*/
|
||||
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(protocol !is null);
|
||||
}
|
||||
/**
|
||||
* Switches the protocol.
|
||||
*
|
||||
* The protocol is deallocated by the event loop, it should currently be
|
||||
* allocated with $(D_PSYMBOL MmapPool).
|
||||
*
|
||||
* Params:
|
||||
* protocol = Application protocol.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE protocol !is null)
|
||||
*/
|
||||
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(protocol !is null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns $(D_PARAM true) if the transport is closing or closed.
|
||||
*/
|
||||
bool isClosing() const pure nothrow @safe @nogc;
|
||||
/**
|
||||
* Returns $(D_PARAM true) if the transport is closing or closed.
|
||||
*/
|
||||
bool isClosing() const pure nothrow @safe @nogc;
|
||||
|
||||
/**
|
||||
* Close the transport.
|
||||
*
|
||||
* Buffered data will be flushed. No more data will be received.
|
||||
*/
|
||||
void close() @nogc;
|
||||
/**
|
||||
* Close the transport.
|
||||
*
|
||||
* Buffered data will be flushed. No more data will be received.
|
||||
*/
|
||||
void close() @nogc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,8 +93,8 @@ interface DuplexTransport : ReadTransport, WriteTransport
|
||||
*/
|
||||
interface SocketTransport : Transport
|
||||
{
|
||||
/**
|
||||
* Returns: Socket.
|
||||
*/
|
||||
@property Socket socket() pure nothrow @safe @nogc;
|
||||
/**
|
||||
* Returns: Socket.
|
||||
*/
|
||||
@property Socket socket() pure nothrow @safe @nogc;
|
||||
}
|
||||
|
@ -27,13 +27,13 @@ import tanya.network.socket;
|
||||
*/
|
||||
abstract class Watcher
|
||||
{
|
||||
/// Whether the watcher is active.
|
||||
bool active;
|
||||
/// Whether the watcher is active.
|
||||
bool active;
|
||||
|
||||
/**
|
||||
* Invoke some action on event.
|
||||
*/
|
||||
void invoke() @nogc;
|
||||
/**
|
||||
* Invoke some action on event.
|
||||
*/
|
||||
void invoke() @nogc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,32 +41,32 @@ abstract class Watcher
|
||||
*/
|
||||
abstract class SocketWatcher : Watcher
|
||||
{
|
||||
/// Watched socket.
|
||||
protected Socket socket_;
|
||||
/// Watched socket.
|
||||
protected Socket socket_;
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* socket = Socket.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE socket !is null)
|
||||
*/
|
||||
this(Socket socket) pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
socket_ = socket;
|
||||
}
|
||||
/**
|
||||
* Params:
|
||||
* socket = Socket.
|
||||
*
|
||||
* Precondition: $(D_INLINECODE socket !is null)
|
||||
*/
|
||||
this(Socket socket) pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(socket !is null);
|
||||
}
|
||||
body
|
||||
{
|
||||
socket_ = socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Socket.
|
||||
*/
|
||||
@property Socket socket() pure nothrow @safe @nogc
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
/**
|
||||
* Returns: Socket.
|
||||
*/
|
||||
@property Socket socket() pure nothrow @safe @nogc
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,44 +74,44 @@ abstract class SocketWatcher : Watcher
|
||||
*/
|
||||
class ConnectionWatcher : SocketWatcher
|
||||
{
|
||||
/// Incoming connection queue.
|
||||
Queue!DuplexTransport incoming;
|
||||
/// Incoming connection queue.
|
||||
Queue!DuplexTransport incoming;
|
||||
|
||||
private Protocol delegate() @nogc protocolFactory;
|
||||
private Protocol delegate() @nogc protocolFactory;
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* socket = Socket.
|
||||
*/
|
||||
this(Socket socket) @nogc
|
||||
{
|
||||
super(socket);
|
||||
incoming = Queue!DuplexTransport(MmapPool.instance);
|
||||
}
|
||||
/**
|
||||
* Params:
|
||||
* socket = Socket.
|
||||
*/
|
||||
this(Socket socket) @nogc
|
||||
{
|
||||
super(socket);
|
||||
incoming = Queue!DuplexTransport(MmapPool.instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* P = Protocol should be used.
|
||||
*/
|
||||
void setProtocol(P : Protocol)() @nogc
|
||||
{
|
||||
this.protocolFactory = () @nogc => cast(Protocol) MmapPool.instance.make!P;
|
||||
}
|
||||
/**
|
||||
* Params:
|
||||
* P = Protocol should be used.
|
||||
*/
|
||||
void setProtocol(P : Protocol)() @nogc
|
||||
{
|
||||
this.protocolFactory = () @nogc => cast(Protocol) MmapPool.instance.make!P;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes new connection callback.
|
||||
*/
|
||||
override void invoke() @nogc
|
||||
in
|
||||
{
|
||||
assert(protocolFactory !is null, "Protocol isn't set.");
|
||||
}
|
||||
body
|
||||
{
|
||||
foreach (transport; incoming)
|
||||
{
|
||||
transport.protocol = protocolFactory();
|
||||
transport.protocol.connected(transport);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Invokes new connection callback.
|
||||
*/
|
||||
override void invoke() @nogc
|
||||
in
|
||||
{
|
||||
assert(protocolFactory !is null, "Protocol isn't set.");
|
||||
}
|
||||
body
|
||||
{
|
||||
foreach (transport; incoming)
|
||||
{
|
||||
transport.protocol = protocolFactory();
|
||||
transport.protocol.connected(transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1646
source/tanya/container/array.d
Normal file
1646
source/tanya/container/array.d
Normal file
File diff suppressed because it is too large
Load Diff
@ -12,8 +12,8 @@
|
||||
*/
|
||||
module tanya.container;
|
||||
|
||||
public import tanya.container.array;
|
||||
public import tanya.container.buffer;
|
||||
public import tanya.container.list;
|
||||
public import tanya.container.string;
|
||||
public import tanya.container.vector;
|
||||
public import tanya.container.queue;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,7 @@ import std.algorithm;
|
||||
import std.ascii;
|
||||
import std.range;
|
||||
import std.traits;
|
||||
import tanya.container.vector;
|
||||
import tanya.container.array;
|
||||
import tanya.memory;
|
||||
|
||||
/**
|
||||
@ -1444,26 +1444,26 @@ struct Integer
|
||||
/**
|
||||
* Returns: Two's complement representation of the integer.
|
||||
*/
|
||||
Vector!ubyte toVector() const nothrow @safe @nogc
|
||||
out (vector)
|
||||
Array!ubyte toArray() const nothrow @safe @nogc
|
||||
out (array)
|
||||
{
|
||||
assert(vector.length == length);
|
||||
assert(array.length == length);
|
||||
}
|
||||
body
|
||||
{
|
||||
Vector!ubyte vector;
|
||||
Array!ubyte array;
|
||||
|
||||
if (this.size == 0)
|
||||
{
|
||||
return vector;
|
||||
return array;
|
||||
}
|
||||
const bc = countBits();
|
||||
const remainingBits = bc & 0x07;
|
||||
|
||||
vector.reserve(bc / 8);
|
||||
array.reserve(bc / 8);
|
||||
if (remainingBits == 0)
|
||||
{
|
||||
vector.insertBack(ubyte.init);
|
||||
array.insertBack(ubyte.init);
|
||||
|
||||
}
|
||||
|
||||
@ -1486,14 +1486,14 @@ struct Integer
|
||||
|
||||
do
|
||||
{
|
||||
vector.insertBack(cast(ubyte) (tmp.rep[0] & 0xff));
|
||||
array.insertBack(cast(ubyte) (tmp.rep[0] & 0xff));
|
||||
tmp >>= 8;
|
||||
}
|
||||
while (tmp != 0);
|
||||
|
||||
vector[].reverse();
|
||||
array[].reverse();
|
||||
|
||||
return vector;
|
||||
return array;
|
||||
}
|
||||
|
||||
///
|
||||
@ -1503,15 +1503,15 @@ struct Integer
|
||||
auto integer = Integer(0x66778899aabbddee);
|
||||
ubyte[8] expected = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
|
||||
|
||||
auto vector = integer.toVector();
|
||||
assert(equal(vector[], expected[]));
|
||||
auto array = integer.toArray();
|
||||
assert(equal(array[], expected[]));
|
||||
}
|
||||
{
|
||||
auto integer = Integer(0x03);
|
||||
ubyte[1] expected = [ 0x03 ];
|
||||
|
||||
auto vector = integer.toVector();
|
||||
assert(equal(vector[], expected[]));
|
||||
auto array = integer.toArray();
|
||||
assert(equal(array[], expected[]));
|
||||
}
|
||||
{
|
||||
ubyte[63] expected = [
|
||||
@ -1526,8 +1526,8 @@ struct Integer
|
||||
];
|
||||
auto integer = Integer(Sign.positive, expected[]);
|
||||
|
||||
auto vector = integer.toVector();
|
||||
assert(equal(vector[], expected[]));
|
||||
auto array = integer.toArray();
|
||||
assert(equal(array[], expected[]));
|
||||
}
|
||||
{
|
||||
ubyte[14] expected = [
|
||||
@ -1536,8 +1536,8 @@ struct Integer
|
||||
];
|
||||
auto integer = Integer(Sign.positive, expected[]);
|
||||
|
||||
auto vector = integer.toVector();
|
||||
assert(equal(vector[], expected[]));
|
||||
auto array = integer.toArray();
|
||||
assert(equal(array[], expected[]));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user