user/dev discussion of public-inbox itself
 help / color / mirror / code / Atom feed
a0793ca2337a04e956aaaf0bb5653c0561760a60 blob 4992 bytes (raw)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
 
PublicInbox::DS - event loop and async I/O base class

Our PublicInbox::DS event loop which powers public-inbox-nntpd
and public-inbox-httpd diverges significantly from the
unmaintained Danga::Socket package we forked from.  In fact,
it's probably different from most other event loops out there.

Most notably:

* There is one and only one callback: ->event_step.  Unlike other
  event loops, there are no separate callbacks for read, write,
  error or hangup events.  In fact, we never care which kevent
  filter or poll/epoll event flag (e.g. POLLIN/POLLOUT/POLLHUP)
  triggers a call.

  The lack of read/write callback distinction is driven by the
  fact TLS libraries (e.g. OpenSSL via IO::Socket::SSL) may
  declare SSL_WANT_READ on SSL_write(), and SSL_WANT_READ on
  SSL_read().  So we end up having to let each user object decide
  whether it wants to make read or write calls depending on its
  internal state, completely independent of the event loop.

  Error and hangup (POLLERR and POLLHUP) callbacks are redundant and
  only triggered in rare cases.  They're redundant because the
  result of every read and write call in ->event_step must be
  checked, anyways.  At best, callbacks for POLLHUP and POLLERR can
  save one syscall per socket lifetime and not worth the extra code
  it imposes.

  Reducing the user-supplied code down to a single callback allows
  subclasses to keep their logic self-contained.  The combination
  of this change and one-shot wakeups (see below) for bidirectional
  data flows make asynchronous code easier to reason about.

Other divergences:

* ->write buffering uses temporary files whereas Danga::Socket used
  the heap.  The rationale for this is the kernel already provides
  ample (and configurable) space for socket buffers.  Modern kernels
  also cache FS operations aggressively, so systems with ample RAM
  are unlikely to notice degradation, while small systems are less
  likely to suffer unpredictable heap fragmentation, swap and OOM
  penalties.

  In the future, we may introduce sendfile and mmap+SSL_write to
  reduce data copies, and use FALLOC_FL_PUNCH_HOLE on Linux to
  release space after the buffer is partially cleared.

Augmented features:

* obj->write(CODEREF) passes the object itself to the CODEREF
  Being able to enqueue subroutine calls is a powerful feature in
  Danga::Socket for keeping linear logic in an asynchronous environment.
  Unfortunately, each subroutine takes several kilobytes of memory.
  One small change to Danga::Socket is to pass the receiver object
  (aka "$self") to the CODEREF.  $self can store any necessary
  state it needs for a normal (named) subroutine.  This allows us to
  put the same sub into multiple queues without paying a large
  memory penalty for each one.

  This idea is also more easily ported to C or other languages which
  lack anonymous subroutines (aka "closures").

* ->requeue support.  An optimization of the AddTimer(0, ...) idiom
  for immediately dispatching code at the next event loop iteration.
  public-inbox uses this for fairly generating large responses
  iteratively (see PublicInbox::NNTP::long_response or git_async_cat
  for blob retrievals).

New features

* One-shot wakeups allowed via EPOLLONESHOT or EV_DISPATCH.  These
  flags allow us to simplify code in ->event_step callbacks for
  bidirectional sockets (NNTP and HTTP).  Instead of merely reacting
  to events, control is handed over at ->event_step in one-shot scenarios.
  The event_step caller (NNTP || HTTP) then becomes proactive in declaring
  which (if any) events it's interested in for the next loop iteration.

* Edge-triggering available via EPOLLET or EV_CLEAR.  These reduce wakeups
  for unidirectional classes (e.g. PublicInbox::Listener sockets,
  and pipes via PublicInbox::HTTPD::Async).

* IO::Socket::SSL support (for NNTPS, STARTTLS+NNTP, HTTPS)

* dwaitpid (waitpid wrapper) support for reaping dead children

* reliable signal wakeups are supported via signalfd on Linux,
  EVFILT_SIGNAL on *BSDs via IO::KQueue.

Removed features

* Many fields removed or moved to subclasses, so the underlying
  hash is smaller and suitable for FDs other than stream sockets.
  Some fields we enforce (e.g. wbuf, wbuf_off) are autovivified
  on an as-needed basis to save memory when they're not needed.

* TCP_CORK support removed, instead we use MSG_MORE on non-TLS sockets
  and we may use vectored I/O support via GnuTLS in the future
  for TLS sockets.

* per-FD PLCMap (post-loop callback) removed, we got ->requeue
  support where no extra hash lookups or assignments are necessary.

* read push backs removed.  Some subclasses use a read buffer ({rbuf})
  but they control it, not this event loop.

* Profiling and debug logging removed.  Perl and OS-specific tracers
  and profilers are sufficient.

* ->AddOtherFds support removed, everything watched is a subclass of
  PublicInbox::DS, but we've slimmed down the fields to eliminate
  the memory penalty for objects.
debug log:

solving a0793ca23 ...
found a0793ca23 in public-inbox.git

user/dev discussion of public-inbox itself

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://public-inbox.org/meta
	git clone --mirror http://czquwvybam4bgbro.onion/meta
	git clone --mirror http://hjrcffqmbrq6wope.onion/meta
	git clone --mirror http://ou63pmih66umazou.onion/meta

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V1 meta meta/ https://public-inbox.org/meta \
		meta@public-inbox.org
	public-inbox-index meta

Example config snippet for mirrors.
Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.mail.public-inbox.meta
	nntp://7fh6tueqddpjyxjmgtdiueylzoqt6pt7hec3pukyptlmohoowvhde4yd.onion/inbox.comp.mail.public-inbox.meta
	nntp://ie5yzdi7fg72h7s4sdcztq5evakq23rdt33mfyfcddc5u3ndnw24ogqd.onion/inbox.comp.mail.public-inbox.meta
	nntp://4uok3hntl7oi7b4uf4rtfwefqeexfzil2w6kgk2jn5z2f764irre7byd.onion/inbox.comp.mail.public-inbox.meta
	nntp://news.gmane.io/gmane.mail.public-inbox.general
 note: .onion URLs require Tor: https://www.torproject.org/

code repositories for project(s) associated with this inbox:

	https://80x24.org/public-inbox.git

AGPL code for this site: git clone https://public-inbox.org/public-inbox.git