Date | Commit message (Collapse) |
|
Our hacks in EvCleanup::next_tick and EvCleanup::asap were due
to the fact "closed" sockets were deferred and could not wake
up the event loop, causing certain actions to be delayed until
an event fired.
Instead, ensure we don't sleep if there are pending sockets to
close.
We can then remove most of the EvCleanup stuff
While we're at it, split out immediate timer handling into a
separate array so we don't need to deal with time calculations
for the event loop.
|
|
Don't use epoll or kqueue to watch for anything unless we hit
EAGAIN, since we don't know if a socket is SSL or not.
|
|
We'll be reusing requeue in other places to reduce trips to
the kernel to retrieve "hot" descriptors.
|
|
Doing this for HTTP cuts the memory usage of 10K
idle-after-one-request HTTP clients from 92 MB to 47 MB.
The savings over the equivalent NNTP change in commit
6f173864f5acac89769a67739b8c377510711d49,
("nntp: lazily allocate and stash rbuf") seems down to the
size of HTTP requests and the fact HTTP is a client-sends-first
protocol where as NNTP is server-sends-first.
|
|
On Linux systems with epoll support, we don't want to be
clobbering defined subs in the t/ds-poll.t test; so use
OO ->method dispatch instead and require users to explicitly
import subs via EXPORT_OK.
|
|
We need to account for ->write(CODE) calls doing ->write(SCALARREF),
otherwise flush_write may see the wrong ->{wbuf} field.
|
|
We end up buffering giant things to the FS sometimes, and open()
is not a cheap syscall; so being forced to do it twice to get a
file description with O_APPEND is gross when we can just use
O_EXCL ourselves and loop on EEXIST.
|
|
At least the subset of epoll we use. EPOLLET might be
difficult to emulate if we end up using it.
|
|
We don't need to code multiple event loops or have branches in
watch() if we can easily make the IO::KQueue-based interface
look like our lower-level epoll_* API.
|
|
We may need to rely on cleanup code running in enqueued
callbacks, so ensure we call it when flush_write happens.
|
|
kqueue EV_ONESHOT semantics are different than epoll
EPOLLONESHOT. epoll only disables watches for that event while
keeping the item in the rbtree for future EPOLL_CTL_MOD. kqueue
removes the watch from the filter set entirely, necessitating
the use of EV_ADD for future modifications.
|
|
We can bypass buffering when wbuf is empty when it's called
from a CODE reference passed to ->write.
|
|
This is in accordance with TLS standards and will be needed
to support session caching/reuse in the future. However, we
don't issue shutdown(2) since we know not to inadvertantly
share our sockets with other processes.
|
|
IO::Socket::SSL will try to re-bless back to the original class
on TLS negotiation failure. Unfortunately, the original class
is 'GLOB', and re-blessing to 'GLOB' takes away all the IO::Handle
methods, because Filehandle/IO are a special case in Perl5.
Anyways, since we already use syswrite() and sysread() as functions
on our socket, we might as well use CORE::close(), as well (and
it plays nicely with tied classes).
|
|
It kinda, barely works, and I'm most happy I got it working
without any modifications to the main NNTP::event_step callback
thanks to the DS->write(CODE) support we inherited from
Danga::Socket.
|
|
Instead of ENOMEM (or fragmentation/swap storms), using tempfile
buffers opens us up to filesystem and storage-related errors
(e.g. ENOSPC, EFBIG, EIO, EROFS). Log these errors, drop the
particular client, and try to limp by with whateve we have left.
|
|
It may make sense to use PerlIO::mmap or PerlIO::scalar for
DS write buffering with IO::Socket::SSL or similar (since we can't
use MSG_MORE), so that means we need to go through buffering
in userspace for the common case; while still being easily
compatible with slow clients.
And it also simplifies GitHTTPBackend slightly.
Maybe it can make sense for HTTP input buffering, too...
|
|
Both NNTP and HTTP have common needs and we can factor
out some common code to make dealing with IO::Socket::SSL
easier.
|
|
They're never called; the only way to break out of that loop
is the PostEventLoop callback.
|
|
We can reduce the amount of short-lived anonymous subs we
create by passing $self to code references.
|
|
This is cleaner in most cases and may allow Perl to reuse memory
from unused fields.
We can do this now that we no longer support Perl 5.8; since
Danga::Socket was written with struct-like pseudo-hash support
in mind, and Perl 5.9+ dropped support for pseudo-hashes over
a decade ago.
|
|
We don't need to keep track of that field since we always
know what events we're interested in when using one-shot
wakeups.
|
|
It may be reinstated at a later time if there's interest; but I
want to be able to use one-shot notifications for certain events
while retaining level-triggered notifications others.
OTOH, I intend to fully support kqueue; via IO::KQueue for now,
but via syscall() eventually to take advantage of the syscall
reduction kevent(2) can provide over (current) epoll APIs.
|
|
There was much duplicate logic between watch_read and
watch_write. Share that logic, and give us room to enable
edge-triggered or one-shot notifications in the future.
|
|
Make the rest of our IO::KQueue-using code less verbose and
closer to the C equivalent.
|
|
We can avoid the EPOLL_CTL_ADD && EPOLL_CTL_MOD sequence with
a single EPOLL_CTL_ADD.
|
|
EPOLL* constants already match their POLL* counterparts and
there's no way Linux can ever diverge or change the values
of those constants. So we'll favor the EPOLL* ones since we
use EPOLLEXCLUSIVE, already.
For weird stuff like kqueue, we'd need to keep maintaining
the mapping, anyways.
|
|
Data which can't fit into a generously-sized socket buffer,
has no business being stored in heap.
|
|
No sense in having similar Linux-specific functionality in
both our NNTP.pm and HTTP.pm
|
|
We call ->flush_write directly, now; so we can eliminate a
needless check.
|
|
There's no point in passing a mask of interesting events
when removing an item from the epoll watch set.
|
|
We don't need write buffering unless we encounter slow clients
requesting large responses. So don't waste a hash slot or
(empty) arrayref for it.
|
|
Get rid of the confusing $need_queue variable and all
the associated documentation for it. Instead, make it
obvious that we're either skipping the write buffer or
flushing the write buffer by splitting the sub in two.
|
|
Since Perl 5.10+, "fields" makes a restricted hash; not a
compile-time-defined array (struct) with fixed offsets as
it did in Perl <= 5.8.
Thus in-use fields cost memory, and since the write buffer
offset is rarely needed; stop relying on it.
|
|
Wrong place to be wrapping this method.
|
|
We rely on immediate timers often, so we can avoid the overhead
of an extra subroutine call to retrieve the monotonic time (and
a sometimes-system call on some platforms).
|
|
All of our internal timing code should use monotonic clocks
for consistency against system clock adjustments.
This can be shared by our Daemon and NNTP packages.
|
|
Over a decade of using Danga::Socket and I never found the
built-in debug functionality useful.
|
|
Merely checking the presence of the {sock} field is
enough, and having multiple sources of truth increases
confusion and the likelyhood of bugs.
|
|
Having separate read/write callbacks in every class is too
confusing to my easily-confused mind. Instead, give every class
an "event_step" callback which is easier to wrap my head around.
This will make future code to support IO::Socket::SSL-wrapped
sockets easier-to-digest, since SSL_write() can require waiting
on POLLIN events, and SSL_read() can require waiting on POLLOUT
events.
|
|
If we got something to write, then write it. Otherwise, try
reading; and continue dealing with errors which normally occur
along the way.
Trying to read requests while we need to buffer in luserspace
is suicidal from a memory management standpoint.
The only adjustment needed for existing callers is EvCleanup;
where we need to ensure we're always calling the dummy
EvCleanup::event_write callback to accomplish nothing.
|
|
In my experience, both are worthless as any normal read/write
call path will be wanting to check errors and deal with them
appropriately; so we can just call event_read, for now.
Eventually, there'll probably be only one callback for dealing
with all in/out/err/hup events to simplify logic, especially w.r.t
TLS socket negotiation.
|
|
Since we stop using it in NNTP, we don't need it at all.
|
|
We won't be needing it, not even for TLS support.
|
|
Storing the file descriptor was redundant as we can quickly call
fileno($self->{sock}) and not have to store an extra hash table
entry. Multiple sources of truth leads to confusion, confusion
leads to bugs.
|
|
ECONNRESET and EPIPE are common on a big Internet filled with
unreliable connections, and there's nothing our code can do
about it.
So no point in wasting code to log them and there are plenty of
tracing tools to choose from if such diagnostics are needed.
|
|
Stop importing unused constants, and favor integer comparisons
of `$!' over `$!{EFOO}' hash lookups. Integer comparisons are
slightly faster, even:
Benchmark: timing 30 iterations of cmp_eq, cmp_ne, hash_hit, hash_miss...
cmp_eq: 1 wallclock secs ( 1.61 usr + 0.00 sys = 1.61 CPU) @ 18.63/s (n=30)
cmp_ne: 2 wallclock secs ( 1.57 usr + 0.00 sys = 1.57 CPU) @ 19.11/s (n=30)
hash_hit: 4 wallclock secs ( 3.85 usr + 0.00 sys = 3.85 CPU) @ 7.79/s (n=30)
hash_miss: 4 wallclock secs ( 3.74 usr + 0.00 sys = 3.74 CPU) @ 8.02/s (n=30)
#!/usr/bin/perl -w
use Benchmark qw(:all);
use Errno qw(EAGAIN EINTR);
my ($r, $w);
pipe($r, $w) or die 'pipe';
require IO::Handle;
$r->blocking(0);
my $buf;
my $n = 30000;
timethese(30, {
hash_hit => sub {
sysread($r, $buf, 1);
for (0..$n) {
next if $!{EAGAIN};
die 'FAIL';
}
}
,
'cmp_eq' => sub {
sysread($r, $buf, 1);
for (0..$n) {
next if $! == EAGAIN;
die 'FAIL';
}
},
hash_miss => sub {
sysread($r, $buf, 1);
for (0..$n) {
die 'FAIL' if $!{EINTR};
}
},
'cmp_ne' => sub {
sysread($r, $buf, 1);
for (0..$n) {
die 'FAIL' if $! == EINTR;
}
},
});
|
|
Keeping track of write_buf_size was redundant and pointless when
we can simply check the number of elements in the buffer array.
Multiple sources of truth leads to confusion; confusion leads to
bugs.
Finally, rename the prefixes to 'wbuf' to ensure we loudly
(instead of silently) break any external dependencies being
ported over from Danga::Socket, as further changes are pending.
|
|
We don't need and won't be needing per-socket PostLoopCallbacks.
|
|
We never enable write watches ourselves for HTTP and NNTP,
and only enable the write watch with EvCleanup because it's
an "always on" watch.
|