cwist
Modern C web framework with libttak runtime
Loading...
Searching...
No Matches
async.h File Reference

Deferred-response (async handler) API for CWIST. More...

#include <cwist/net/http/http.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
Include dependency graph for async.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Typedefs

typedef struct cwist_async cwist_async
 

Functions

cwist_async * cwist_async_defer (cwist_http_request *req, cwist_http_response *res)
 Defer the response for the current request.
 
void cwist_async_wait_dispatched (cwist_async *a)
 Wait until dispatch has acknowledged the handoff of a.
 
cwist_async * cwist_async_retain (cwist_async *a)
 Retain a live handle for a producer that may outlive completion. Must be called while the caller already holds a reference, or before the dispatch handoff can complete. It cannot resurrect an expired handle. Returns a (NULL is accepted). Each retain requires one release.
 
void cwist_async_release (cwist_async *a)
 Release a reference acquired with cwist_async_retain(). Does not complete or abort the exchange. NULL is accepted.
 
void cwist_async_set_timeout (cwist_async *a, uint64_t ms)
 Answer with a 504 Gateway Timeout if the exchange is still pending after ms milliseconds. The timeout routes through the same completion path as a normal response. The timer holds its own handle reference until its callback runs, even if another producer wins. Default is no timeout (0).
 
bool cwist_async_respond (cwist_async *a, cwist_http_status_t status, const char *content_type, const void *body, size_t len)
 Complete the exchange with a simple body response. Thread-safe and one-shot: the first of respond/respond_with/abort wins, later calls return false. body is copied. A winning foreign call may wait for dispatch/middleware to return.
 
bool cwist_async_respond_with (cwist_async *a, cwist_http_response *res)
 Complete the exchange with a caller-built response. On success the framework takes ownership of res (destroyed after send). On false the caller retains ownership. Build the response on the calling producer thread, or explicitly disown its GC-tracked owned allocations on their allocating thread before passing it to this producer. Borrowed data and opaque cleanup-context dependencies must remain live until destruction; the framework transfers a managed payload but cannot traverse its context.
 
bool cwist_async_abort (cwist_async *a, cwist_http_status_t status)
 Complete the exchange with an error status and close the connection.
 
void cwist_async_dispatch_ack (cwist_async *a)
 Internal: on the dispatch thread, after all middleware has unwound. Call exactly once; do not touch the exchange after this release-publication.
 

Detailed Description

Deferred-response (async handler) API for CWIST.

A route handler that cannot produce its response immediately calls cwist_async_defer() and returns. The framework then skips its normal send + destroy steps and hands ownership of the request/response pair to the returned cwist_async handle. Any thread (scheduler job, NATS callback, custom worker) later completes the exchange with exactly one of cwist_async_respond(), cwist_async_respond_with(), or cwist_async_abort().

Completion routing depends on the server mode:

  • C1M (event-driven) mode: the completion is posted to the owning reactor (cwist_reactor_post) and finished on the connection's owner thread, so reactor threads never block on foreign work. While a request is deferred the connection is parked: pipelined bytes stay in the receive stash and are parsed only after the deferred response is sent, keeping responses ordered. Keep-alive connections are re-armed afterwards.
  • Classic (thread pool) mode: the completing thread writes the response itself, then re-arms the connection on the pool (keep-alive) or closes it.
  • HTTP/2 mode: the completing thread enqueues the finished exchange on the owning connection's async queue and pokes its wake fd; the connection thread drains the queue and emits HEADERS/DATA itself, so HPACK state and flow control stay single-threaded. Other streams on the connection keep being served while a response is deferred.

Write-path note (v1): the client fd is O_NONBLOCK in C1M mode; completion temporarily clears it and bounds the write with SO_SNDTIMEO (~5s), so a slow client can occupy a reactor thread for that budget. A POLLOUT-resumable writer is future work (v2).

Deferred responses bypass Big Dumb Reply learning and are never cached.

res->deferred and res->async are read-only for application code. Dispatch decides the handoff from what cwist_async_defer() recorded on its own thread, not from those fields, so a producer that writes them cannot make dispatch send and destroy an exchange it no longer owns. Such a write is reported once on stderr; with CWIST_ASYNC_STRICT=1 it aborts.

Definition in file async.h.

Typedef Documentation

◆ cwist_async

typedef struct cwist_async cwist_async

Definition at line 54 of file async.h.

Function Documentation

◆ cwist_async_abort()

bool cwist_async_abort ( cwist_async *  a,
cwist_http_status_t  status 
)

Complete the exchange with an error status and close the connection.

Definition at line 370 of file async.c.

References cwist_http_response::body, cap, cwist_http_header_add(), cwist_sstring_assign(), cwist_http_response::headers, cwist_http_response::status_code, and cwist_http_response::status_text.

◆ cwist_async_defer()

cwist_async * cwist_async_defer ( cwist_http_request *  req,
cwist_http_response *  res 
)

Defer the response for the current request.

Call from inside a route handler and return immediately afterwards; the handler must not touch req or res after this call. Ownership of both objects transfers to the returned handle. Framework middleware may finish its synchronous posthandler work before dispatch acknowledges the transfer. A foreign producer waits for that acknowledgement before mutating or sending the exchange. Do not join or otherwise wait for a completing producer from the handler/middleware. Inline completion claims and records the response, but sending and cleanup wait until dispatch/middleware unwind. This also permits inline abort when scheduling a producer fails, without another allocation or scheduler.

Returns
Handle to complete later, or NULL on allocation failure (the framework falls back to answering whatever the handler wrote).

Definition at line 103 of file async.c.

References cwist_http_request::app, cwist_http_response::async, cwist_http_request::async_conn, cap, cwist_http_request::client_fd, cwist_alloc(), cwist_async_record_pending(), CWIST_ASYNC_ST_PENDING, cwist_free(), cwist_full_gc_enabled(), cwist_gc_scope_disown(), cwist_h2_async_queue_acquire(), cwist_http_reactor_generation(), cwist_http_response::deferred, cwist_http_request::h2_queue, cwist_http_request::https_conn, cwist_http_request::keep_alive, cwist_http_response::keep_alive, cwist_http_async_conn::reactor, and cwist_http_request::stream_id.

◆ cwist_async_dispatch_ack()

void cwist_async_dispatch_ack ( cwist_async *  a)

Internal: on the dispatch thread, after all middleware has unwound. Call exactly once; do not touch the exchange after this release-publication.

Definition at line 167 of file async.c.

References cap, and cwist_async_forget_handle().

Referenced by cwist_http2_serve_connection_ex().

◆ cwist_async_release()

void cwist_async_release ( cwist_async *  a)

Release a reference acquired with cwist_async_retain(). Does not complete or abort the exchange. NULL is accepted.

Definition at line 159 of file async.c.

References cap, cwist_async_forget_handle(), and cwist_free().

Referenced by cwist_async_set_timeout().

◆ cwist_async_respond()

bool cwist_async_respond ( cwist_async *  a,
cwist_http_status_t  status,
const char *  content_type,
const void *  body,
size_t  len 
)

Complete the exchange with a simple body response. Thread-safe and one-shot: the first of respond/respond_with/abort wins, later calls return false. body is copied. A winning foreign call may wait for dispatch/middleware to return.

Definition at line 340 of file async.c.

References cwist_http_response::body, cap, cwist_http_header_add(), cwist_sstring_assign(), cwist_sstring_assign_len(), cwist_http_response::headers, cwist_http_response::status_code, and cwist_http_response::status_text.

◆ cwist_async_respond_with()

bool cwist_async_respond_with ( cwist_async *  a,
cwist_http_response *  res 
)

Complete the exchange with a caller-built response. On success the framework takes ownership of res (destroyed after send). On false the caller retains ownership. Build the response on the calling producer thread, or explicitly disown its GC-tracked owned allocations on their allocating thread before passing it to this producer. Borrowed data and opaque cleanup-context dependencies must remain live until destruction; the framework transfers a managed payload but cannot traverse its context.

Definition at line 359 of file async.c.

References cap.

◆ cwist_async_retain()

cwist_async * cwist_async_retain ( cwist_async *  a)

Retain a live handle for a producer that may outlive completion. Must be called while the caller already holds a reference, or before the dispatch handoff can complete. It cannot resurrect an expired handle. Returns a (NULL is accepted). Each retain requires one release.

Definition at line 154 of file async.c.

References cap.

Referenced by cwist_async_set_timeout().

◆ cwist_async_set_timeout()

void cwist_async_set_timeout ( cwist_async *  a,
uint64_t  ms 
)

Answer with a 504 Gateway Timeout if the exchange is still pending after ms milliseconds. The timeout routes through the same completion path as a normal response. The timer holds its own handle reference until its callback runs, even if another producer wins. Default is no timeout (0).

Definition at line 328 of file async.c.

References cap, cwist_async_release(), cwist_async_retain(), and cwist_scheduler_schedule().

◆ cwist_async_wait_dispatched()

void cwist_async_wait_dispatched ( cwist_async *  a)

Wait until dispatch has acknowledged the handoff of a.

A producer on another thread must not read or mutate the deferred request or response until dispatch and the middleware chain have unwound: until then the dispatch thread still reads the response (including res->deferred) and may send and destroy it. Call this on the producer before touching req/res directly; respond, respond_with and abort already wait on their own. Returns immediately on the dispatch thread itself (an inline producer runs before the acknowledgement by construction) and for NULL. The wait is short: dispatch acknowledges as soon as the handler and middleware return.

Definition at line 185 of file async.c.

References cap.