yimmo-wsgi

Developer documentation for yimmo-wsgi.

Source Layout

The yimmo-wsgi source is laid out like so:

Source

Contents

main.c

Runtime entrypoint.

yimmo_wsgi.h

Common macros and type declarations.

ymo_wsgi_proc.h/ymo_wsgi_proc.c

Process startup and control.

ymo_wsgi_worker.h/ymo_wsgi_worker.c

Thread worker implementation.

ymo_wsgi_server.h/ymo_wsgi_server.c

IO thread implmenentation.

ymo_wsgi_exchange.h/ymo_wsgi_exchange.c

HTTP Exchange type and functions.

ymo_wsgi_session.h/ymo_wsgi_session.c

Yimmo WSGI session type and functions.

ymo_wsgi_context.h/ymo_wsgi_context.c

C implementation of the python yimmo.Context class.

ymo_wsgi_mod.h/ymo_wsgi_mod.c

Defines the yimmo module.

ymo_wsgi_util.h/ymo_wsgi_util.c

Miscelaneous utility functions.

ymo_wsgi_cli.h/ymo_wsgi_cli.c

Command line interface.

Thread Interaction

A Request/Response pair gets grouped into a box labeled, "exchange". All the exchanges for a connection are grouped into a box labeled "session."

A request and it’s associated response form an exchange. A session encapsulates all of the exchanges on a single TCP connection.

Every worker process has at least two threads:

  1. One “server thread” that does I/O using yimmo, ev, and bsat.

  2. One or more “worker threads” that handle requests (providing the PEP3333 interface to the application).

There are some locks and some reference counting involved. It is tolerable. Don’t worry.

Queues (ymo_queue_t) are used to ferry HTTP information between the two. Each worker thread has two queues, one for input, one for output. Each queue has an associated lock (pthread_mutex_t).

The WSGI module implements an HTTP request handler callback (ymo_http_cb_t) which combines the request (ymo_http_request_t) and response (ymo_http_response_t) into a single unit, the exchange (ymo_wsgi_exchange_t).

After creating the exchange, the callback locks the input queue for the worker associated with the connection, enqueueus the exchange, and signals the worker thread using a pthread condition variable.

Upon waking (or after completing the task at hand — the worker may have been handling another request in parallel while libyimmo_http was doing IO) the worker thread acquires the input queue lock, dequeues the exchange, acquires the GIL, packages the encapsulated information up as a python yimmo.Context, and invokes the WSGI application, passing environ and yimmo.Context.start_response().

Multi-Processing

If YIMMO_WSGO_NO_PROC==1: you get the two threads described above.

If YIMMO_WSGI_NO_PROC > 1, a “manager process” starts which spawns YIMMO_WSGI_NO_PROC worker processes, handles signals, and restarts failed worker processes. This results in YIMMO_WSGI_NO_PROC + 1 total processes, but the main process spends most of its time sleeping.

Multi-processing reduces GIL contention. Generally, the GIL is really a non-issue. On the other hand, it does take time to acquire and can be a real bottleneck for CPU-bound tasks. In this case, Python is the CPU-bound task — best bet is to set YIMMO_WSGI_NO_PROC to the number of cores you wish to allow yimmo-wsgi to use at the same time.

Yimmo WSGI Single-Process, Single Thread Worker

Yimmo-WSGI configured with a single process worker and one thread worker.

Yimmo WSGI Multiple Process Workers, Single Thread Worker

Yimmo-WSGI configured with two process workers and one thread worker.

Multi-Threading

As stated earlier, every process has at least two threads. The YIMMO_WSGI_NO_THREADS environment variable is used to control the number of worker threads per process.

Multi-threading slightly reduces the overall throughput (the threads can do a lot in parallel, but they will still access the Python VM core serially, due to the GIL). On the flip side, it distributes the HTTP request latency and response times more equally — i.e. long-running requests are less likely to block shorter requests, but some short requests will suffer here and there from a context switch. With YIMMO_WSGI_NO_PROC < 12 you won’t notice too much, though — if your load is consistent and requests are handled quickly — you will definitely get your highest throughput by pinning threads to 1.

Yimmo WSGI Single-Process, Multiple Thread Workers

Yimmo-WSGI configured with a single process worker and multiple thread workers.

Yimmo WSGI Multiple Process Workers, Multiple Thread Workers

Yimmo-WSGI configured with two process workers and two thread workers per process.

Events

Yimmo WSGI Events

Concurrency and Synchronization