nquic_dispatch (nquic v1.0.0)

View Source

Striped connection dispatch table.

Distributes connection ID mappings across S ETS tables (one per stripe), selected by hashing the DCID. This reduces write contention when many connections register/unregister CIDs concurrently, while maintaining O(1) lookup on the fast path.

Each stripe is an independent ETS set with {read_concurrency, true} and {write_concurrency, true}. A separate bag table indexed by owning Pid holds the reverse mapping so reregister/3 can transfer a single connection's CIDs in O(K) time (K = CIDs of that connection) instead of O(N) (N = all registered CIDs across stripes).

Also provides atomics-based packet counters for lock-free telemetry.

Summary

Functions

Destroy all stripes. Only call during shutdown.

Look up the listener manager (nquic_listener_mgr) pid published at listener startup. Returns undefined before the mgr has registered itself (during the brief boot window of nquic_listener_sup).

Look up the partition supervisor pid published under the given 1-based index. Returns undefined if the slot is empty (e.g. between a partition crash and the supervisor's restart).

Return the published partition count, or undefined before nquic_partitions_sup:init/1 has run.

Add ByteCount to byte counter for receiver I (1-based).

Increment packet count for receiver I (1-based).

Look up the process for a connection ID.

Return the per-listener metrics handle attached to this dispatch, if any.

Create a striped dispatch table with default stripe count (number of schedulers).

Create a striped dispatch table with S stripes.

Create packet counters for N receivers.

Return the distinct owner pids currently registered across all CIDs. Reads the per-pid reverse index (a bag of {Pid, CID}) and folds it to a deduplicated pid list. Used only by the listener drain broadcast (nquic:stop_listener/1,2, mode => cascade); never on the per-packet path.

Read byte count for receiver I (1-based).

Read packet count for receiver I (1-based).

Register a connection ID to a process.

Re-register all entries pointing to OldPid to NewPid. Walks the per-pid reverse index for OldPid and updates only that connection's CIDs (O(K)), instead of scanning every stripe (O(N)). Used during connection export (accept_ctx) to transfer dispatch entries from the gen_statem to the library-mode owner process.

Publish the listener manager pid. Called from nquic_listener_mgr:init/1 so receivers and protocol-level helpers can route accept/establishment notifications via nquic_dispatch:get_mgr/1.

Publish a partition supervisor pid under its 1-based index. Each nquic_server_sup partition calls this from its init/1 (or after restart) so start_conn_child/3 can route work without a tuple republish step. Avoids the set_sups tuple write-then-replace loop that the legacy nquic_listener did on every partition restart.

Publish the total number of partition supervisors. Set once by nquic_partitions_sup:init/1 before any partition starts so start_conn_child/3 can hash the DCID into the right slot.

Start a connection child under the partition supervisor selected by hashing the DCID. Resolves the partition pid via two ETS reads (count

Return the total number of registered connection IDs across all stripes.

Remove a connection ID from the dispatch table.

Types

counters()

-type counters() :: #counters{ref :: atomics:atomics_ref(), n :: pos_integer()}.

t()

-type t() ::
          #dispatch{tables :: tuple(),
                    stripes :: pos_integer(),
                    pid_index :: ets:tid(),
                    sups_table :: ets:tid(),
                    metrics :: nquic_metrics:t() | undefined}.

Functions

destroy/1

-spec destroy(t()) -> ok.

Destroy all stripes. Only call during shutdown.

get_mgr/1

-spec get_mgr(t()) -> pid() | undefined.

Look up the listener manager (nquic_listener_mgr) pid published at listener startup. Returns undefined before the mgr has registered itself (during the brief boot window of nquic_listener_sup).

get_partition/2

-spec get_partition(t(), pos_integer()) -> pid() | undefined.

Look up the partition supervisor pid published under the given 1-based index. Returns undefined if the slot is empty (e.g. between a partition crash and the supervisor's restart).

get_partition_count/1

-spec get_partition_count(t()) -> pos_integer() | undefined.

Return the published partition count, or undefined before nquic_partitions_sup:init/1 has run.

inc_bytes/3

-spec inc_bytes(counters(), pos_integer(), non_neg_integer()) -> ok.

Add ByteCount to byte counter for receiver I (1-based).

inc_packets/2

-spec inc_packets(counters(), pos_integer()) -> ok.

Increment packet count for receiver I (1-based).

lookup/2

-spec lookup(t(), binary()) -> pid() | undefined.

Look up the process for a connection ID.

metrics/1

-spec metrics(t()) -> nquic_metrics:t() | undefined.

Return the per-listener metrics handle attached to this dispatch, if any.

new()

-spec new() -> t().

Create a striped dispatch table with default stripe count (number of schedulers).

new(Stripes)

-spec new(pos_integer()) -> t().

Create a striped dispatch table with S stripes.

new_counters(N)

-spec new_counters(pos_integer()) -> counters().

Create packet counters for N receivers.

owner_pids/1

-spec owner_pids(t()) -> [pid()].

Return the distinct owner pids currently registered across all CIDs. Reads the per-pid reverse index (a bag of {Pid, CID}) and folds it to a deduplicated pid list. Used only by the listener drain broadcast (nquic:stop_listener/1,2, mode => cascade); never on the per-packet path.

read_bytes/2

-spec read_bytes(counters(), pos_integer()) -> non_neg_integer().

Read byte count for receiver I (1-based).

read_packets/2

-spec read_packets(counters(), pos_integer()) -> non_neg_integer().

Read packet count for receiver I (1-based).

register/3

-spec register(t(), binary(), pid()) -> true.

Register a connection ID to a process.

reregister/3

-spec reregister(t(), pid(), pid()) -> ok.

Re-register all entries pointing to OldPid to NewPid. Walks the per-pid reverse index for OldPid and updates only that connection's CIDs (O(K)), instead of scanning every stripe (O(N)). Used during connection export (accept_ctx) to transfer dispatch entries from the gen_statem to the library-mode owner process.

set_mgr/2

-spec set_mgr(t(), pid()) -> true.

Publish the listener manager pid. Called from nquic_listener_mgr:init/1 so receivers and protocol-level helpers can route accept/establishment notifications via nquic_dispatch:get_mgr/1.

set_partition/3

-spec set_partition(t(), pos_integer(), pid()) -> true.

Publish a partition supervisor pid under its 1-based index. Each nquic_server_sup partition calls this from its init/1 (or after restart) so start_conn_child/3 can route work without a tuple republish step. Avoids the set_sups tuple write-then-replace loop that the legacy nquic_listener did on every partition restart.

set_partition_count/2

-spec set_partition_count(t(), pos_integer()) -> true.

Publish the total number of partition supervisors. Set once by nquic_partitions_sup:init/1 before any partition starts so start_conn_child/3 can hash the DCID into the right slot.

start_conn_child/3

-spec start_conn_child(t(), binary(), map()) -> {ok, pid()} | {ok, pid(), term()} | {error, term()}.

Start a connection child under the partition supervisor selected by hashing the DCID. Resolves the partition pid via two ETS reads (count

  • slot), so partition restart is observed immediately without any republish step.

table_size/1

-spec table_size(t()) -> non_neg_integer().

Return the total number of registered connection IDs across all stripes.

unregister/2

-spec unregister(t(), binary()) -> true.

Remove a connection ID from the dispatch table.