Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Segfault in csp.dynamic when the trigger basket removes one key and adds another in the same engine cycle

Aperta
#775 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
48/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
cpp, python

Direzione di ricerca

Start by reading cpp/csp/engine/DynamicNode.cpp and cpp/csp/engine/BasketInfo.cpp, especially the dynamic-instance add/remove handling and basket index updates described in the report; then inspect cpp/csp/python/PyDynamicNode.cpp for how csp.attach() chooses its basket index. Reproduce with the provided script or the linked variant matrix. Done means same-cycle removal and addition no longer segfault, with the neighboring variants still behaving as expected.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

type: bug

Describe the bug

A node that outputs a csp.DynamicBasket, calls csp.remove_dynamic_key for one key and outputs a different key in the same engine cycle crashes the process (SIGSEGV, fault address 0x8) as soon as that basket is the trigger of a csp.dynamic using csp.attach(). The same trigger consumed by an ordinary node is fine, and the same key traffic spread over separate cycles is fine, so the remove+add combination is what triggers it.

To Reproduce

"""csp segfaults when a csp.dynamic trigger basket removes one key and adds a
different key in the same engine cycle.

    uv run python repro.py          # or: python repro.py (with csp installed)

Expected: prints the recorded outputs and exits 0.
Observed (csp 0.19.1): SIGSEGV inside PyNodeWrapper_linkFrom while
PyDynamicNode_create instantiates the sub-graph for the newly added key.
"""
from datetime import datetime, timedelta

import csp
from csp import ts


@csp.node
def alternating_key(x: ts[int]) -> csp.DynamicBasket[bool, int]:
    """Key = parity of the tick. Each tick removes the previous key and adds
    the other one, so one removal and one addition land in the same cycle."""
    with csp.state():
        s_prev = None
    if csp.ticked(x):
        key = bool(x & 1)
        if s_prev is not None and s_prev != key:
            csp.remove_dynamic_key(s_prev)
        s_prev = key
        csp.output({key: x})


@csp.graph
def cell(key: bool, v: ts[int]) -> ts[int]:
    return v + 1


@csp.node
def first_value(xs: csp.DynamicBasket[bool, int]) -> ts[int]:
    if csp.ticked(xs):
        out = None
        for _, v in xs.tickeditems():
            out = v
        return out


@csp.graph
def main():
    x = csp.count(csp.timer(timedelta(microseconds=1), True))
    out = csp.dynamic(alternating_key(x), cell, csp.snapkey(), csp.attach())
    csp.add_graph_output("o", first_value(out))


if __name__ == "__main__":
    start = datetime(2020, 1, 1)
    result = csp.run(main, starttime=start, endtime=start + timedelta(microseconds=10))
    print(result["o"])

Standalone repository with this script plus a matrix of neighbouring variants (each run in its own interpreter): https://github.com/hhenson/csp-dynamic-remove-add-segfault

same_cycle_remove_add                CRASH (segfault)   remove previous key + add new key in one cycle, csp.dynamic consumer
same_cycle_remove_add_older_key      CRASH (segfault)   the removed key was added two cycles earlier (3-key rotation)
same_cycle_remove_add_no_dynamic     ok                 identical trigger, consumed by a plain node instead of csp.dynamic
separate_cycles_remove_then_add      ok                 removals and additions happen in different cycles
deferred_remove_via_feedback         ok                 the removal is replayed through csp.feedback one engine cycle later

Expected behavior

The run completes and prints the recorded outputs. If removing one key and adding another in the same cycle is meant to be unsupported, a Python exception at csp.remove_dynamic_key/csp.output time rather than a process crash.

Error Message

signal 11 (Segmentation fault: 11), address is 0x8
[bt]: (3)  _cspimpl.so  csp::python::PyNodeWrapper_linkFrom(csp::python::PyNodeWrapper*, _object*) + 232
[bt]: (4)  _cspimpl.so  csp::python::PyNodeWrapper_linkFrom(csp::python::PyNodeWrapper*, _object*) + 232
[bt]: (10) _cspimpl.so  csp::python::PyDynamicNode_create(...)::$_0::operator()(...) + 384
[bt]: (11) _cspimpl.so  csp::DynamicNode::addDynamicInstance(csp::DialectGenericType const&) + 392
[bt]: (12) _cspimpl.so  csp::DynamicNode::executeImpl() + 144
[bt]: (13) _cspimpl.so  csp::CycleStepTable::executeCycle(csp::Profiler*, bool) + 232
[bt]: (14) _cspimpl.so  csp::RootEngine::runSim(csp::DateTime) + 184
[bt]: (15) _cspimpl.so  csp::RootEngine::run(csp::DateTime, csp::DateTime) + 176
[bt]: (16) _cspimpl.so  csp::python::PyEngine_run(csp::python::PyEngine*, _object*) + 104

The process then aborts from csp's signal handler (exit code 134). The Linux wheel reports the same fault address; its frames are unsymbolized.

Runtime Environment

  • 0.19.1 / 3.13.15 / darwin (macOS 27.0.1 arm64, wheel cp313-cp313-macosx_11_0_arm64)
  • 0.19.1 / 3.14.4 / linux (Ubuntu 26.04 x86_64, glibc 2.43, wheel cp314-cp314-manylinux_2_28_x86_64)

Additional context

Reading the 0.19.1 sources, the crash looks like an element-index mismatch between the dynamic node's bookkeeping and the trigger input basket:

Resolving the attach index from the basket's own mapping at instantiation time (the basket's change callback already reports elemId and replaceId), or applying pending removals to the mirror before processing additions, would avoid the stale index.

Workaround that works today: apply removals one engine cycle after additions, for example by replaying the removal through csp.feedback (the deferred_remove_via_feedback variant in the linked repository).

Found while porting a benchmark whose keyed collections replace a few keys every cycle through csp.dynamic.

Lingua principale
Python
Stelle
449
Fork
91
Merge medio
13h 21m
PR unite (30g)
6

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di Point72/csp

Tutte le issue di Point72/csp

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.