Skip to content

File dp_interrupt.h

FileList > inc > dp_interrupt.h

Go to the source code of this file

Asking a blocking wait to stop, whatever it is waiting on. More...

  • #include "clib_common.h"
  • #include <signal.h>
  • #include <stddef.h>

Public Functions

Type Name
void dp_interrupt (void)
Ask every blocking wait in this process to stop.
unsigned dp_interrupt_latency_ms (void)
The interrupt latency in force.
int dp_interrupt_on_signal (int sig)
Install a handler for sig that callsdp_interrupt() .
int dp_interrupted (void)
Non-zero when an interrupt is pending.
int dp_restore_signal (int sig)
Put back whatever handler dp_interrupt_on_signal() displaced.
void dp_resume (void)
Clear the interrupt, so blocking waits block again.
void dp_set_interrupt_latency_ms (unsigned ms)
How soon a blocking wait must notice an interrupt.

Macros

Type Name
define DP_INTERRUPT_LATENCY_DEFAULT_MS 100u
Default interrupt latency, in milliseconds.
define DP_INTERRUPT_MAX_SIGNALS 8u
Most signals this facility will handle at once.

Detailed Description

doppler moves samples over three transports — a NATS subject, a double-mapped ring, and a capture file — and every one of them has a consumer-side wait that a caller may need to abandon. This is the one flag all three consult, so that Ctrl+C means the same thing wherever the samples are coming from.

It lives in the core library rather than in the optional stream component because two of its three callers are core: a file writer and a ring buffer are available in a build with no NATS at all. It was in native/src/stream/stream_core.c until it acquired that second caller, which is also when it turned out to have no NATS dependency to begin with — a volatile sig_atomic_t and four accessors over libc.

ONE flag per PROCESS, and in Python that takes a rendezvous. Each extension module links this file statically and CPython imports extensions RTLD_LOCAL, so every .so would otherwise hold its own copy doppler#976, where a stop requested through doppler.interrupt left a ring wait in doppler.buffer spinning on a different variable. The state and the two accessors that share it live in dp_interrupt.c; their names carry the dp_interrupt_guard prefix because the declared COMPONENT is what just-makeit binds, and it generates the capsule hand-off into every module's PyInit_ from that declaration. Nothing here is a C caller's concern: one archive means one copy.

See docs/design/io-termination.md for the contract this is one third of; the other two are end-of-stream and durable completion.

Public Functions Documentation

function dp_interrupt

Ask every blocking wait in this process to stop.

void dp_interrupt (
    void
) 

Assigns to a volatile sig_atomic_t and does nothing else, which is the only thing the C standard promises can be done from a signal handler without tearing — and being callable from a handler is the entire point of this API.

The flag is sticky: one handler firing may have to release several parked loops, so it stays set until dp_resume() clears it.

static void on_sigint (int sig) { (void)sig; dp_interrupt (); }
signal (SIGINT, on_sigint);

function dp_interrupt_latency_ms

The interrupt latency in force.

unsigned dp_interrupt_latency_ms (
    void
) 

Returns:

Milliseconds.


function dp_interrupt_on_signal

Install a handler for sig that callsdp_interrupt() .

int dp_interrupt_on_signal (
    int sig
) 

Uses sigaction and chains to whatever handler was already installed, so adding this to a program does not silently disable the one it had.

Install it EARLY — before opening transports, not after. A signal arriving before this call is not ignored, it terminates the process, and that window is real: measured at ~5 ms for a dynamically linked binary, which is long enough for a supervisor's stop signal to land inside it.

Parameters:

  • sig Signal number, e.g. SIGINT.

Returns:

DP_OK, or DP_ERR_INVALID if the handler could not be installed or all handler slots are in use.


function dp_interrupted

Non-zero when an interrupt is pending.

int dp_interrupted (
    void
) 

The check a hand-written loop makes between blocks. A wait that cannot be sliced — a busy-spin over a ring, a read of a file still being appended to — polls this and gives up when it is set.

Returns:

Non-zero when interrupted, 0 otherwise.


function dp_restore_signal

Put back whatever handler dp_interrupt_on_signal() displaced.

int dp_restore_signal (
    int sig
) 

Parameters:

Returns:

DP_OK, or DP_ERR_INVALID if sig was never installed.


function dp_resume

Clear the interrupt, so blocking waits block again.

void dp_resume (
    void
) 


function dp_set_interrupt_latency_ms

How soon a blocking wait must notice an interrupt.

void dp_set_interrupt_latency_ms (
    unsigned ms
) 

A wait that cannot be woken is taken in slices, with the flag checked between them. This is the size of that slice, expressed as the thing a caller actually cares about — the worst-case delay between dp_interrupt() and the wait returning — rather than as an implementation detail.

It is a knob because the right answer is not the library's to know. A human pressing Ctrl+C cannot perceive 100 ms; a control loop that must hand back within one symbol period can, and a battery-powered sensor would rather wake once a second than ten times. The cost is one wakeup per slice on an otherwise idle waiter.

Process-wide, like the flag it serves. Takes effect on the next slice, so a wait already blocked adopts it within one old slice.

Parameters:


Macro Definition Documentation

define DP_INTERRUPT_LATENCY_DEFAULT_MS

Default interrupt latency, in milliseconds.

#define DP_INTERRUPT_LATENCY_DEFAULT_MS `100u`

Ten wakeups a second on an idle waiter, and a delay no human perceives when they press Ctrl+C. It is a default rather than a constant of the design: see dp_set_interrupt_latency_ms().


define DP_INTERRUPT_MAX_SIGNALS

Most signals this facility will handle at once.

#define DP_INTERRUPT_MAX_SIGNALS `8u`

Not a budget anyone reasoned about it is "more signals than a program sensibly interrupts on". Public because a guard records what it armed in a fixed array of this size, and the two must agree.



The documentation for this class was generated from the following file native/inc/dp_interrupt.h