Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not good enough syntax for loops (explicit lock needed) #752

Closed
emil14 opened this issue Nov 7, 2024 · 4 comments
Closed

Not good enough syntax for loops (explicit lock needed) #752

emil14 opened this issue Nov 7, 2024 · 4 comments
Labels

Comments

@emil14
Copy link
Collaborator

emil14 commented Nov 7, 2024

In this issue we are talking about looped network topologies rather than HOCs like map/filter/reduce

Usecase

In "99 bottles" example we have to use explicit lock instead of deferred connection because that's the only way to to implement manual loop:

next2Lines node should compute when we send initial message 99 and then each time it sends result which is not -1 - in this case result must be send back to next2Lines (this is where to loop is created).

Problem

We can't send [99, next2Lines] -> switch because 99 works as infinite sender and thus we'll create conflict between 2 streams, instead of sending 99 just once.

Because of this we need to defer sending 99 until :start is sent, which happens exactly once.

:start -> { 99 -> ??? }
[???, next2Lines] -> switch

Despite having a feature (deferred connections) exactly for that, we can't rely on it here and have to use explicit locks:

def Main(start) (stop) {
    Next2Lines, Lock<int> // explicit lock for fan-in to next2Lines
    ---
    :start -> lock:sig
    99 -> lock:data
    [lock:data, next2Lines] -> switch {
        -1 -> :stop
        else -> next2Lines
    }
}

Might be related to #717

Meta

This is not the perfect use case because it was caused by #754 concurrency issues with stream processing, so better way to deal with it is firstly to solve that problem and then observe if we need something else. Maybe all our problems could be solved with streams

@emil14 emil14 added the Major label Nov 7, 2024
@emil14
Copy link
Collaborator Author

emil14 commented Nov 7, 2024

Add port to next2Lines

def Main(start) (stop) {
    Next2Lines
    ---
    :start -> { 99 -> next2Lines:start }
    next2Lines -> switch {
        -1 -> :stop
        else -> next2Lines:next
    }
}

def next2Lines(start int, next int) (n int) {
    Dec<int>, PrintFirstLine, PrintSecondLine
    ---
    // printFirstLine and printSecondLine won't work in parallel
    // because they are in the loop at the level of Main
    [:start, :next] -> printFirstLine -> dec -> printSecondLine -> :n
}

I don't like this solution, it's clearly a hack. We basically moved complexity from one place to another and probably made this even less obvious.

@emil14
Copy link
Collaborator Author

emil14 commented Nov 7, 2024

Introduce extra component

def Main(start) (stop) {
    Send99, Next2Lines
    ---
    :start -> send99
    [send99, next2Lines] -> switch {
        -1 -> :stop
        else -> next2Lines:next
    }
}

def Send99(sig any) (n int) {
    :sig -> { 99 -> :n }
}

def Next2Lines(start, next int) (n int) {
    Dec<int>, PrintFirstLine, PrintSecondLine
    ---
    // printFirstLine and printSecondLine won't work in parallel
    // because they are in the loop at the level of Main
    [:start, :next] -> printFirstLine -> dec -> printSecondLine -> :n
}

I would still consider this a hack, but maybe less than modifying Next2Lines. Perhaps somewhat at pair with original solution with explicit lock

@emil14 emil14 changed the title Not good enough syntax for lock + fan-in (explicit lock needed) Not good enough syntax for loops (explicit lock needed) Nov 7, 2024
@emil14
Copy link
Collaborator Author

emil14 commented Nov 7, 2024

Pass component

Same as #752 (comment) but generic

def Pass<T>(data T) (res T) {
    :data -> :res
}

Could be used like this

def Main(start) (stop) {
    Pass, Next2Lines
    ---
    :start -> { 99 -> pass }
    [pass, next2Lines] -> switch {
        -1 -> :stop
        else -> next2Lines:next
    }
}

It's the same as Send99 except it's not ADHOC, i.e. we don't have to create such dummy per each use-case, so a bit less boilerplate. I wouldn't consider this perfect solution, but I probably like it more than send99

@emil14
Copy link
Collaborator Author

emil14 commented Nov 9, 2024

While HOC

At the moment solved with While (see #723)

This HOC is basically what implements loop with lock inside, so you don't have to

def Main(start) (stop) {
	While<int>{Next2Lines}
	---
	:start -> { 99 -> while:from }
	0 -> while:to
	while:res -> :stop
	while:err -> panic
}

@emil14 emil14 closed this as not planned Won't fix, can't repro, duplicate, stale Feb 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant