cross-platform / dspatch

The Refreshingly Simple Cross-Platform C++ Dataflow / Patching / Pipelining / Graph Processing / Stream Processing / Reactive Programming Framework
https://flowbasedprogramming.com/
BSD 2-Clause "Simplified" License
216 stars 44 forks source link

what "a feedback component" mean? #23

Closed vacing closed 4 years ago

vacing commented 4 years ago
        // 1. set tickStatus -> TickStarted
        p->tickStatuses[bufferNo] = internal::Component::TickStatus::TickStarted;

        // 2. tick incoming components
        for ( auto& wire : p->inputWires )
        {
            if ( mode == TickMode::Series )
            {
                wire.fromComponent->Tick( mode, bufferNo );
            }
            else if ( mode == TickMode::Parallel )
            {
                if ( !wire.fromComponent->Tick( mode, bufferNo ) )
                {
                    p->feedbackWires[bufferNo].emplace( &wire );
                }
            }
        }

        // 3. set tickStatus -> Ticking
        p->tickStatuses[bufferNo] = internal::Component::TickStatus::Ticking;

According to the code above, I think a component in stage 2 are took as a feed back component by a behind component in another thread. But in this circumstance, this component may turn to Ticking latter, why we needn't wait such a component but wait for component whose state is Ticking?

                    // wait for non-feedback incoming components to finish ticking
                    auto wireIndex = p->feedbackWires[bufferNo].find( &wire );
                    if ( wireIndex == p->feedbackWires[bufferNo].end() )
                    {
                        wire.fromComponent->p->componentThreads[bufferNo]->Sync();
                    }
                    else
                    {
                        p->feedbackWires[bufferNo].erase( wireIndex );
                    }
MarcusTomlinson commented 4 years ago

Firstly, you understand that in a pull system, you can't just keep going backwards through a circuit ticking components without detecting feedbacks right. If you did, you'd find yourself stuck in infinite loops. So during circuit traversals, a flag is used per component, per buffer for "TickStarted" and "Ticking" to let other components know whether they're in a loop together, or have already started ticking respectively.

As I mentioned earlier, think of buffers as circuit threads. The line:

p->tickStatuses[bufferNo] = internal::Component::TickStatus::Ticking;

sets the "Ticking" state for a particular thread only (thread #bufferNo). Other threads entering this method will each be concerned with their own bufferNo, and thus, operate on their own flags. The same is true for p->feedbackWires.

MarcusTomlinson commented 4 years ago

Regarding the actual wait logic here: We specifically wait for non-feedback incoming components to finish ticking and not the feedback ones. If we did, we'd be waiting forever, as that component itself is waiting on the one before it to finish, and so on around the loop.

vacing commented 4 years ago

sorry, did you mean your circuit support wire circle( A -> B -> A) ? not DAG?

MarcusTomlinson commented 4 years ago

Yes, DSPatch circuits support loops like A -> B -> A.

vacing commented 4 years ago

Yes, DSPatch circuits support loops like A -> B -> A.

Amazing !!! I didn't realize one can really supports circle, because most framework I learned only support DAG, now it makes sense to me.

vacing commented 4 years ago

So during circuit traversals, a flag is used per component, per buffer for "TickStarted" and "Ticking" to let other components know whether they're in a loop together, or have already started ticking respectively.

Will it happen when components working in Parrelel Mode(use one buffer), A->B. A and B have different Tick thread, at the time A is in stage 2, B tries to Activate A, and find A's Buffer is TickStarted.

A --> B
thread A: stage1 --> stage2 -----------------------------------------------> stage 3
thread B: stage1--------------------> stage2(try to tick A) ---------------->
vacing commented 4 years ago

oh I know, that won't happen, because only the lambda tick is executed in component thread, not the Component's Tick method, so the big Tick only driven by circuit thread, and only concurrent in buffer level.

vacing commented 4 years ago

Great thanks to your quick and kind and patient reply, I will try to submit some easy use API patch later, hope they can be helpful.

Best regards vacing