Skip to content

Group interrupt

Modules > interrupt

More...

Public Functions

Type Name
void dp_stream_interrupt (void)
Ask every blocking receive in this process to return now.
unsigned dp_stream_interrupt_latency_ms (void)
The interrupt latency in force.
int dp_stream_interrupt_on_signal (int sig)
Install a handler for sig that callsdp_stream_interrupt() .
int dp_stream_interrupted (void)
Non-zero when an interrupt is pending.
int dp_stream_restore_signal (int sig)
Restore the handler that was in place before.
void dp_stream_resume (void)
Clear the interrupt, so blocking receives block again.
void dp_stream_set_interrupt_latency_ms (unsigned ms)
Default interrupt latency, in milliseconds.

Detailed Description

Deprecated

These are the dp_stream_* spellings of a primitive that is not specific to streaming. It moved to dp_interrupt.h in the core library so a build with no NATS can use it; use dp_interrupt(), dp_interrupted(), dp_resume(), dp_interrupt_on_signal() and dp_restore_signal() instead. These forward verbatim and are removed once their callers migrate. See docs/design/io-termination.md.

A blocking *_recv waits inside the NATS client, and a flag your signal handler sets is read by your loop — which the blocking call is keeping you out of. With traffic arriving that is invisible, because every frame returns control to you; the moment a sender stops, Ctrl+C stops working. That is not hypothetical: it shipped, in doppler's own C receiver example.

A bounded *_set_timeout is one answer, and the examples relied on it, but it makes every caller trade latency against responsiveness and get it wrong quietly. This is the other: the library checks a flag of its own inside the wait, so a blocking receive stays blocking and still returns when you ask it to.

Public Functions Documentation

function dp_stream_interrupt

Ask every blocking receive in this process to return now.

void dp_stream_interrupt (
    void
) 

Async-signal-safe by construction — it assigns to a volatile sig_atomic_t and does nothing else — so the intended caller is a signal handler:

static void on_sigint (int sig)
{
  (void)sig;
  dp_stream_interrupt ();
}

Every receive already blocked returns DP_ERR_INTERRUPTED within one internal wait slice (100 ms), and so does every one STARTED while the flag is set — a receive cannot be missed by racing the signal. The flag is process-wide and sticky; dp_stream_resume() clears it.


function dp_stream_interrupt_latency_ms

The interrupt latency in force.

unsigned dp_stream_interrupt_latency_ms (
    void
) 

Returns:

Milliseconds.


function dp_stream_interrupt_on_signal

Install a handler for sig that callsdp_stream_interrupt() .

int dp_stream_interrupt_on_signal (
    int sig
) 

The handler is installed in C, and that is the whole point rather than a convenience. A handler written in a higher-level language runs when its interpreter next regains control, which is precisely what a blocking receive is preventing the flag would be set only after the wait it is meant to end. Measured, not reasoned: a Python signal.signal handler calling the interrupt left a blocked recv() blocked forever.

Whatever handler was installed is chained, not replaced: it runs immediately after the flag is set. Without that, a signal arriving while the program is not inside a receive would set a flag nobody reads and otherwise do nothing fixing the blocking case by breaking the ordinary one. For an embedding interpreter this is what keeps its own Ctrl+C behaviour intact.

Parameters:

  • sig Signal number, e.g. SIGINT.

Returns:

DP_OK, or DP_ERR_INVALID for a signal that cannot be caught.


function dp_stream_interrupted

Non-zero when an interrupt is pending.

int dp_stream_interrupted (
    void
) 

For a loop that wants to notice without calling recv again, and for a caller that keeps its own flag and wants one source of truth.

Returns:

Non-zero when interrupted, 0 otherwise.


function dp_stream_restore_signal

Restore the handler that was in place before.

int dp_stream_restore_signal (
    int sig
) 

Parameters:

Returns:

DP_OK, or DP_ERR_INVALID if that signal was never installed.


function dp_stream_resume

Clear the interrupt, so blocking receives block again.

void dp_stream_resume (
    void
) 

The flag is sticky on purpose: a handler fires once and the loops it unblocks may be several, so an auto-clearing flag would release one caller and leave the rest parked.


function dp_stream_set_interrupt_latency_ms

Default interrupt latency, in milliseconds.

void dp_stream_set_interrupt_latency_ms (
    unsigned ms
) 

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

How soon a blocking receive must notice an interrupt.

The library cannot be woken from the NATS client's wait, so it waits in slices and checks the flag between them. This is the size of that slice, expressed as the thing a caller actually cares about — the worst-case delay between dp_stream_interrupt() and the receive 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 receiver.

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

Parameters: