-
Notifications
You must be signed in to change notification settings - Fork 29
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
Make SendToChildrenInParallel asynchronous #682
Comments
With the proposed solution we are open to a zombie goroutine in case the caller is not fully reading the channel. I see two solutions:
Solution 2 seems easier: ret := make(chan error, len(p.Children))
wait := sync.WaitGroup{}
wait.Add(len(p.Children))
for _, c := range p.Children() {
go func(tn *onet.TreeNode) {
defer wait.Done()
ret <- p.SendTo(tn, msg)
}(c)
}
go func() {
wait.Wait()
close(ret)
}()
return ret Then the caller can do: ret := SendToChildrenInBackground(...)
for err, more := range ret {
if more {
// do something with err
}
} |
Right, good idea, that do avoid having non-terminating goroutine. I would avoid sending func SendToChildrenInBackground(msg interface{}) <-chan error {
ret := make(chan error, len(p.Children()))
var wait sync.WaitGroup
wait.Add(len(p.Children()))
for _, c := range p.Children() {
go func(tn *onet.TreeNode) {
defer wait.Done()
if err := p.SendTo(tn, msg); err != nil {
ret <- err
}
}(c)
}
go func() {
wait.Wait()
close(ret)
}()
return ret
} This way, the caller can simply for err := range SendToChildrenInBackground(...) {
// some error happened
} |
The nice thing about having a potential |
Alright, let's add the next helper then, to easily choose between the two behaviors func FilterNilError(recv <-chan error) <-chan error {
ret := make(chan error) // should we use cap(recv) here?
go func() {
for err := range recv {
if err != nil {
ret <- err
}
}
close(ret)
}()
return ret
} |
That makes the main function quite small: func (n *TreeNodeInstance) SendToChildrenInBackground(
msg interface{}) <-chan error {
ret := make(chan error, len(n.Children()))
for _, c := range n.Children() {
go func(tn *TreeNode) {
ret <- n.SendTo(tn, msg)
}(c)
}
return ret
} Or we make a |
No, it needs the
Having a helper allows us to reuse this pattern without linking it to And to come back to the need to have |
I thought of using it in a
If I remember correctly, closing the channel would always choose the first If that is true, then I prefer not to have the |
No, there are absolutely no priority in select statement. Use I seems you're trying to be too smart there. The function should have a simple behavior: return result of sends on a channel and close it when its done. period. Let the caller implement its own utility function if needed. |
I like being smart... Anyway - if you both think the channel should be closed, I'll add some additional logic in the 2 places (out of 6) where this is needed. And for the nil use-case, it could be used in the propagate-protocol. But I'm not sure it's a good thing to do there. Now I just need an easy way to write the test for this and some time... |
The current
SendToChildrenInParallel
is synchronous: it waits for each children to receive the message OR to return an error. Unfortunately some connections only return an error after a timeout. For the blscosi protocol, this timeout is too long for the protocol to work correctly.So
SendToChildrenInParallel
should return immediately and still allow to be notified:@tharvik proposed the following code:
As this is a breaking change, one could add the following to
SendToChildrenInParallel
:Naming propositions (add yours):
SendToChildrenInBackground
Once this is merged, the calls to
SendToChildrenInParallel
should be replaced with this one. Also the hacky ones in thecothority/blscosi/protocol/sub_protocol.go
andcothority/messaging/propagate.go
.The text was updated successfully, but these errors were encountered: