nevalang / neva

🌊 Dataflow programming language with static types and implicit parallelism. Compiles to machine code and Go
https://nevalang.org
MIT License
124 stars 7 forks source link

Connector bug #522

Closed emil14 closed 8 months ago

emil14 commented 8 months ago

Weird behaviour in connector causes freeze in 99 bottles example.

PENDING: looper/virtual_blocker_8/in:data[0] [looper/virtual_blocker_8/in:data[0]] Take one down and pass it around, no more bottles of beer on the wall.
PENDING: looper/virtual_blocker_6/in:data[0] [looper/virtual_blocker_6/in:data[0]] $0 bottles of beer on the wall, $0 bottles of beer.
PENDING: virtual_blocker_13/in:data[0] [virtual_blocker_13/in:data[0]] No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.
PENDING: looper/virtual_blocker_7/in:data[0] [looper/virtual_blocker_7/in:data[0]] 0
PENDING: virtual_blocker_11/in:data[0] [virtual_blocker_11/in:data[0]] 99
PENDING: virtual_blocker_12/in:data[0] [virtual_blocker_12/in:data[0]] 0
PENDING: virtual_blocker_11/in:sig[0] [virtual_blocker_11/in:sig[0] virtual_blocker_12/in:sig[0]] <empty>
PENDING: virtual_blocker_12/in:sig[0] [virtual_blocker_12/in:sig[0]] <empty>
PENDING: virtual_blocker_15/in:data[0] [virtual_blocker_15/in:data[0]] 0
PENDING: virtual_blocker_12/in:data[0] [virtual_blocker_12/in:data[0]] 0
PENDING: eq/in:b[0] [eq/in:b[0]] 0
PENDING: eq/in:a[0] [eq/in:a[0]] 99
PENDING: virtual_blocker_11/in:data[0] [virtual_blocker_11/in:data[0]] 99
PENDING: virtual_selector_1/in:msg[0] [virtual_selector_1/in:msg[0]] map[a:99 b:0]
PENDING: looper/in:old[0] [looper/in:old[0]] 99
PENDING: looper/virtual_blocker_6/in:sig[0] [looper/virtual_blocker_6/in:sig[0] looper/fprinter1/in:args[0]] 99
PENDING: looper/fprinter1/in:args[0] [looper/fprinter1/in:args[0]] 99
PENDING: looper/virtual_blocker_10/in:data[0] [looper/virtual_blocker_10/in:data[0]] Take one down and pass it around, $0 bottles of beer on the wall.
PENDING: looper/virtual_blocker_6/in:data[0] [looper/virtual_blocker_6/in:data[0]] $0 bottles of beer on the wall, $0 bottles of beer.
PENDING: looper/fprinter1/in:tpl[0] [looper/fprinter1/in:tpl[0]] $0 bottles of beer on the wall, $0 bottles of beer.
99 bottles of beer on the wall, 99 bottles of beer.
PENDING: looper/decrementor/in:data[0] [looper/decrementor/in:data[0]] 99
PENDING: looper/virtual_blocker_7/in:sig[0] [looper/virtual_blocker_7/in:sig[0] looper/eq/in:a[0]] 98
PENDING: looper/eq/in:a[0] [looper/eq/in:a[0]] 98
PENDING: looper/virtual_blocker_7/in:data[0] [looper/virtual_blocker_7/in:data[0]] 0
PENDING: looper/eq/in:b[0] [looper/eq/in:b[0]] 0
PENDING: looper/virtual_blocker_10/in:sig[0] [looper/virtual_blocker_10/in:sig[0] looper/virtual_selector_0/in:msg[0]] map[a:98 b:0]
PENDING: looper/virtual_selector_0/in:msg[0] [looper/virtual_selector_0/in:msg[0]] map[a:98 b:0]
PENDING: looper/fprinter2/in:args[0] [looper/fprinter2/in:args[0]] 98
PENDING: looper/virtual_blocker_10/in:data[0] [looper/virtual_blocker_10/in:data[0]] Take one down and pass it around, $0 bottles of beer on the wall.
PENDING: looper/fprinter2/in:tpl[0] [looper/fprinter2/in:tpl[0]] Take one down and pass it around, $0 bottles of beer on the wall.
Take one down and pass it around, 98 bottles of beer on the wall.
PENDING: looper/out:new[0] [looper/out:new[0]] 98
PENDING: virtual_blocker_15/in:sig[0] [virtual_blocker_15/in:sig[0] eq/in:a[0]] 98
PENDING: eq/in:a[0] [eq/in:a[0]] 98
PENDING: virtual_blocker_15/in:data[0] [virtual_blocker_15/in:data[0]] 0
PENDING: eq/in:b[0] [eq/in:b[0]] 0
PENDING: virtual_selector_1/in:msg[0] [virtual_selector_1/in:msg[0]] map[a:98 b:0]
PENDING: looper/in:old[0] [looper/in:old[0]] 98
PENDING: looper/fprinter1/in:args[0] [looper/fprinter1/in:args[0] looper/fprinter1/in:args[0]] 98

As we can see from this log from connector's distribute function, we send first msg 99 to looper/virtual_blocker_20/in:sig[0], then to looper/fprinter1/in:args[0]

PENDING looper/virtual_blocker_20/in:sig[0] [0x1400003cc00 0x1400003d5c0] [looper/virtual_blocker_20/in:sig[0] looper/fprinter1/in:args[0]] 99
PENDING looper/fprinter1/in:args[0] [0x1400003d5c0] [looper/fprinter1/in:args[0]] 99

But when second msg 98 arrives we send it to looper/fprinter1/in:args[0] only. Where is the other receiver looper/virtual_blocker_20/in:sig[0]? Why it's replaced with another looper/fprinter1/in:args[0]?

PENDING: looper/fprinter1/in:args[0] [looper/fprinter1/in:args[0] looper/fprinter1/in:args[0]] 98

Probably this causes endless loop, we try to send to first receiver, it's busy (because it needs signal first), we go to next one and... that's actually the same receiver! Which is... well... still busy.

emil14 commented 8 months ago

Fixed in https://github.com/nevalang/neva/pull/508

emil14 commented 8 months ago

Actually it wasn't

// warning: it's not clear whether it's safe to move on before all receivers processed
emil14 commented 8 months ago

I think this might be the reason why this example sometimes terminates before we print 2 - because 2 reaches receiver before 1, cuz we have 2 goroutines concurrent to each-other.

component Main(start) (stop) {
    nodes {
        Mod
        Range
        Match<int>
        Unwrap<int>
        Printer<any>
        Blocker<int>
    }
    net {
        :start -> (
            1   -> range:from,
            3   -> range:to
        )
        range:data    -> unwrap:data

        unwrap:some   -> [mod:data, blocker:data]
        15            -> mod:case[0]
        3             -> mod:case[1]
        5             -> mod:case[2]

        mod:then[0] -> printer:data
        mod:then[1] -> printer:data
        mod:then[2] -> printer:data
        mod:else    -> printer:data

        printer:sig -> blocker:sig

        blocker:data  -> match:data
        2             -> match:case[0]
        match:then[0] -> :stop
    }
}

virtual_emitter_5/out:msg[0] -> mod/in:case[2] 5
virtual_emitter_2/out:msg[0] -> virtual_blocker_2/in:data[0] 3
in:start[0] -> [ virtual_blocker_1/in:sig[0], virtual_blocker_2/in:sig[0] ] <empty>
virtual_emitter_6/out:msg[0] -> match/in:case[0] 2
virtual_emitter_1/out:msg[0] -> virtual_blocker_1/in:data[0] 1
virtual_emitter_3/out:msg[0] -> mod/in:case[0] 15
virtual_emitter_1/out:msg[0] -> virtual_blocker_1/in:data[0] 1
virtual_emitter_4/out:msg[0] -> mod/in:case[1] 3
virtual_blocker_1/out:data[0] -> range/in:from[0] 1
virtual_emitter_2/out:msg[0] -> virtual_blocker_2/in:data[0] 3
virtual_blocker_2/out:data[0] -> range/in:to[0] 3

range/out:data[0] -> unwrap/in:data[0] 1
range/out:data[0] -> unwrap/in:data[0] 2
range/out:data[0] -> unwrap/in:data[0] <nil>

unwrap/out:some[0] -> [ mod/in:data[0], blocker/in:data[0] ] 1
unwrap/out:some[0] -> [ mod/in:data[0], blocker/in:data[0] ] 2

virtual_emitter_3/out:msg[0] -> mod/in:case[0] 15
unwrap/out:none[0] -> __destructor__/in:msg[0] map[]
virtual_emitter_4/out:msg[0] -> mod/in:case[1] 3
virtual_emitter_5/out:msg[0] -> mod/in:case[2] 5

mod/out:else[0] -> printer/in:data[0] 1
1
printer/out:sig[0] -> blocker/in:sig[0] 1

virtual_emitter_3/out:msg[0] -> mod/in:case[0] 15
blocker/out:data[0] -> match/in:data[0] 2
virtual_emitter_6/out:msg[0] -> match/in:case[0] 2
virtual_emitter_4/out:msg[0] -> mod/in:case[1] 3

match/out:then[0] -> out:stop[0] 2
emil14 commented 8 months ago

Fixed in https://github.com/nevalang/neva/pull/525

But the new (old) issue is https://github.com/nevalang/neva/issues/529