Skip to content

File dp_once.h

FileList > doppler > dp_once.h

Go to the source code of this file

Run a function exactly once, however many threads ask at once. More...

  • #include <pthread.h>

Classes

Type Name
struct dp_once_t
The state of one one-time build; start it at DP_ONCE_INIT .

Public Functions

Type Name
void dp_once (dp_once_t * once, void(*)(void) fn)
Run fn once, and return only after it has finished.

Macros

Type Name
define DP_ONCE_INIT { PTHREAD\_ONCE\_INIT }
The initializer for a dp_once_t whose build has not run.

Detailed Description

The library builds some tables lazily, on first use: the AWGN and LO sine LUTs, the CCSDS Reed-Solomon field tables and its marker's bits. A plain static int ready flag guarding such a table is a DATA RACE, not a harmless double build. Nothing orders the flag's store after the table's stores for another thread, so that thread can see ready == 1 and read a table still being written (arm64 reorders those stores, and so may the compiler). Two threads can also both see 0 and both write. First use is concurrent in shipped code: Plan.prepare fans the per-source builds across dp_parallel.h, and every block method releases the GIL (doppler#2328, gh-817).

dp_once() is the one guard for all of them, and it is the platform's own once primitive behind one signature: pthread_once on POSIX, InitOnceExecuteOnce on Windows, the pair rs.c used privately since gh-817. The first caller runs the build; every other caller BLOCKS in the primitive until it has finished, then returns and sees everything it wrote. A blocked waiter sleeps rather than spins, so a waiter that outranks the builder on its core cannot starve it, and an oversubscribed machine spends nothing on the wait. glibc also restarts, in a forked child, a once whose build fork() interrupted, where a hand-rolled flag would wait forever.

The implementation lives in dp_once.c, so <windows.h> stays in that one file: a component that builds a table includes this header and pulls in <pthread.h> at most. On glibc before 2.34 pthread_once needs -lpthread, which the dp_once_core target carries for every component that links it.

Public Functions Documentation

function dp_once

Run fn once, and return only after it has finished.

void dp_once (
    dp_once_t * once,
    void(*)(void) fn
) 

Every caller, on any thread, returns after the single call of fn has completed, and sees everything fn wrote. The callers that lose the race to run it block until it is done.

Parameters:

  • once Its state, statically initialized with DP_ONCE_INIT.
  • fn The build; it must not call dp_once() on the same once.
#include <doppler/dp_once.h>
#include <stdio.h>

static float     table[256];
static dp_once_t table_once = DP_ONCE_INIT;

static void
table_build (void)
{
  for (int i = 0; i < 256; i++)
    table[i] = (float)i;
}

// Every function that reads the table calls dp_once first.
static float
table_at (int i)
{
  dp_once (&table_once, table_build);
  return table[i];
}

int
main (void)
{
  printf ("%g\n", table_at (255)); // 255
  return 0;
}

Macro Definition Documentation

define DP_ONCE_INIT

The initializer for a dp_once_t whose build has not run.

#define DP_ONCE_INIT `{ PTHREAD_ONCE_INIT }`



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