Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

mail.pool

import mail

mail exposes this as mail.pool, so import mail is enough and the names are called as mail.pool.*. import mail.pool reaches the same definitions directly.

Running a mail server on more than one connection at a time.

Both servers here serve one connection to the end before taking the next, which is the right shape for the protocols and the wrong shape for more than one client. serve() puts a pool of isolates behind one listening socket: the main isolate accepts, and each worker takes a connection and sees it through.

import mail.pool
import .my_server

pool.serve(my_server.build, { host: '0.0.0.0', port: 143, workers: 8 })

build is a function in a module of its own, not a closure, because an isolate resolves a module’s function by name on its own side and cannot be handed one that closes over anything here.

# my_server.zu
import mail.imap { ImapServer, MaildirStore }

def build() {
  var store = MaildirStore('/var/mail')

  store.add_account('ann', 'secret')

  return ImapServer({}, store)
}

An IMAP connection can be open for hours, so the pool is a ceiling on how many clients can be served at once, not on how fast they are served. Size it accordingly.

Functions

worker_main()

mail.pool.worker_main(connections, build)

What one worker isolate runs: build a server of its own, then serve whatever connections the acceptor hands it.

Called by serve(). It is public because an isolate has to be able to find it by name.

Parameters

  • connections (Channel) — Accepted sockets arrive here.
  • build (function(0)) — Returns this worker’s server.

start()

mail.pool.start(build, options: ?dict) -> Cluster

Starts a pool without running the accept loop, so the address is known before the first connection.

Parameters

  • build (function(0)) — A module’s function returning a server.
  • options (?dict) — host, port and workers.

Returns Cluster

serve()

mail.pool.serve(build, options: ?dict) -> Cluster

Starts a pool and runs it until something closes it.

Parameters

  • build (function(0))
  • options (?dict) — As start().

Returns Cluster

Classes

Cluster

class mail.pool.Cluster

A running pool: the socket, the workers, and the loop feeding them.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

mail.pool.Cluster(listener, connections, workers: list)

Parameters

  • listener (TcpStream)
  • connections (Channel)
  • workers (list) — The isolates serving them.

Cluster.address()

mail.pool.Cluster.address() -> SocketAddr

The address the pool is listening on.

Returns SocketAddr

Cluster.run()

mail.pool.Cluster.run() -> Cluster

Accepts connections and hands them out until close().

Returns Cluster — itself.

Cluster.accept_one()

mail.pool.Cluster.accept_one() -> Cluster

Accepts exactly one connection and hands it to a worker, for a program that wants to drive the loop itself.

Returns Cluster — itself.

Cluster.close()

mail.pool.Cluster.close(timeout: ?number) -> Cluster

Stops accepting, tells the workers there is nothing more coming, and waits for them to finish what they hold.

Parameters

  • timeout (?number) — Milliseconds to wait for each worker.

Returns Cluster — itself.

Cluster.to_string()

mail.pool.Cluster.to_string()

2026, Richard Ore and Zuri contributors