yet another event loop
Eugene Wissner via Digitalmars-d-announce
digitalmars-d-announce at puremagic.com
Wed Aug 24 11:03:39 PDT 2016
https://github.com/caraus-ecms/tanya
Ok there are not so many event loops in D and here an another one
and its name is "tanya".
I want it to become not an event loop only but a general purpose
library that has an event loop.
What once started as a libev rewrite, hasn't much common with
libev now except some general concepts like watchers.
Regarding libev:
1) tanya is very, very basic and it hasn't a lot of important
features yet like signals, UDP, threads and so on. I had to begin
somewhere and stripped out everything that isn't relevant for a
basic event loop. Features will be added with the time.
2) Only epoll is currently supported. But I tried to create an
API that can be easily extended, so you have to extend one class
and implement a few methods to add other backends.
3) In another thread chmike (many thanks again!) pointed me to
Windows IOCP. I'm not completely sure I understand how the
completion ports work, but I implemented the loop in the way that
you haven't to care about file descriptors and sockets but get
notified if the data are really available. And you write aswell
not to a socket but into a buffer, and the event loop takes care
of passing it then to the socket. I hope it can make the work
with the loop more pleasant and can make it possible to create a
performant Windows implementation.
Other points:
1) The library is 100% @nogc. I know there were some discussions
that this @nogc is pure marketing thing, but I find it helpful
that the compiler can say if you allocate somewhere in a language
where GC allocations can happen behind the scenes.
2) The loop throws a few exceptions that should be freed, but I'm
thinking to switch to some data type "either exception or return
value" and make the loop nothrow. It has nothing to do with
@nogc. It is just kind of not very cool if an exception can kill
the event loop if something goes wrong.
3) The library isn't thread safe. I will work on it later.
4) libev wasn't the only source of inspiration. tanya is a mix of
libev and asyncio and asynchronous. I took over the concept of
protocols and transports from asyncio/asynchronous since I
believe they make the writing of applications really pleasant.
The difference is that they aren't a kind of "wrapper" around the
actual event loop, but are first-class citizens. It could make it
difficult to write such wrappers like that ones that exist for
libasync, but on the other side it kills some unneeded
abstractions and makes the code structure simplier, that could
also give some additional performance.
5) I tried to write unittests and short descriptions everywhere,
so there is some documentation and examples. For an usage example
skip the crap I'm writing here and look at the end of this
message.
There are already some "extras":
tanya.memory: has a simple allocator (Ullocator) that uses
mmap/munmap (tested on Linux, will theoretically work on other
platforms aswell). "allocator" package has some functions like
"finalize" that can be used in @nogc code instead of dispose or
"resizeArray" that is similar to shrinkArray/expandArray from
std.experimental.allocator, but doesn't take a delta as argument
but just the length, that the array should have. The allocator
was the most difficult part of the library for me, but very
interesting. I had to rewrite it 3 times till I got something
working. I just advice everyone to write their own malloc/free
implementaion, it is a frustrating, but awsome experience!
tanya.container: Queue, Singly-linked list and In-/Output Buffer
(useful in C-style functions that take a void pointer and the
length as argument and return bytes read/written). I wrote them
for the event loop, not sure they are good as general-purpose
containers, but I would be anyway interested to make them
suitable for other use-cases. They are also differently concepted
than phobos containers. Phobos containers as far as I've seen are
containers that implement ranges functionality in
substructs/subclasses. tanya's containers are a mix of containers
and ranges.
tanya.math: has "pow" function that calculates x**y mod z. The
algorithm is similar to the one used by phobos. The return type
and arguments are currently ulong but it will change, I will need
larger numbers probably.
tanya.random: has an "Entropy" class that can generate 64-byte
blocks of random data (uses getrandom syscall). The generic logic
is stolen from mbedtls.
tanya.crypto.padding: implements some algorithms to pad
128/192/258-byte blocks of data. But you cannot remove the
padding :) Sorry, it will be added soon. Just started.
I made some tests with an echo-client written in Go (just found
one benchmarking one-page Go echo-client in the internet). Here
is an usage example, just to give some feeling how the library
works (Examples and description will be added to the repository
soon):
import tanya.memory;
import tanya.event.loop;
import tanya.event.protocol;
import tanya.event.transport;
import tanya.event.watcher;
import core.stdc.stdio;
import std.exception;
import core.stdc.string;
import core.sys.posix.netinet.in_;
import core.sys.posix.fcntl;
import core.sys.posix.unistd : close;
class EchoProtocol : TransmissionControlProtocol
{
@nogc:
private DuplexTransport transport;
void received(ubyte[] data)
{
transport.write(data);
printf("%.*s", data.length, data.ptr);
}
void connected(DuplexTransport transport)
{
this.transport = transport;
printf("Got connection.\n");
}
void disconnected()
{
printf("Disconnected.\n");
}
}
void main()
{
sockaddr_in addr;
int s = socket(AF_INET, SOCK_STREAM, 0);
auto loop = getDefaultLoop();
// Echo server
printf("Listening on port 8192\n");
addr.sin_family = AF_INET;
addr.sin_port = htons(cast(ushort)8192);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(s, cast(sockaddr *)&addr, addr.sizeof) != 0)
{
throw make!Exception(defaultAllocator, "bind");
}
fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0) | O_NONBLOCK);
listen(s, 5);
auto io = make!ConnectionWatcher(defaultAllocator,
() => cast(Protocol) make!EchoProtocol(defaultAllocator),
s);
loop.start(io);
loop.run();
shutdown(s, SHUT_RDWR);
close(s);
}
Sorry for that sockaddr_in stuff, I want to add a Socket class in
the next commits, that would eliminate the need of this
impossible boilerplate.
So far.. I would say it is the first test release and I'm
beginning with testing and continue to write. Enjoy, I hope it
doesn't leak too much memory...
More information about the Digitalmars-d-announce
mailing list