Multiple `async` sources for a `sync` `Rocket.jl` actor

Hm, interestingly but intsource events aren’t dropped for me (I run your example in Jupyter notebook). But the output is still not the same as you would expect without merged().

got char a, waiting ...
leaving char
got char b, waiting ...
leaving char
got char c, waiting ...
leaving char
got Int 1, waiting ...
leaving Int
got Int 2, waiting ...
leaving Int
got Int 3, waiting ...
leaving Int
done
done

I’m pretty much sure that is has to do something with the fact that sync() actor assumes only one completion event (though I think we can change it in the future). So it probably drops the second source or maybe printing is failing. merged() operator deals with that automatically by merging two completion events into one.

It is also an interesting example of my previous message that asynchronous behaviour really may depend on a hardware, operating system and environment where you run your code (in my case its MacOS, Jupyter notebook, Julia 1.6.1).

You may also look into factory feature. For example:

struct MyActorFactory <: Rocket.AbstractActorFactory end

# Factory needs to have one single method `create_actor` which will be executed during each subscription
# Here L gives you the exact type of the stream, so you can drop Union{Char, Int} in your actor and use the exact L
# e. g. struct MyActor{L} <: Rocket.Actor{L} end
# but in this example I didn't change you original implementation
Rocket.create_actor(::Type{L}, factory::MyActorFactory) where { L } = MyActor()
factory = Rocket.sync(MyActorFactory(); withlock = true)

Rocket.subscribe!(charsource |> Rocket.async(), factory)
Rocket.subscribe!(intsource |> Rocket.async(), factory)

wait(factory)

In this example sync factory will create two actors for each source independently and will wait for both of them. The downside here is that withlock will also be independent for both actors and you will have unsynchronised events again. With factories you also don’t have a direct reference to actors (you may save it though in create_actor method if you want). With you original example the better approach is to use merged() of course, but factories might be also useful in many cases. For example builtin logger() actor is actually the factory that creates a LoggerActor every time it subscribes to some source.

I already fixed sync() to have withlock = true by default to be consistent with the documentation. I will probably wait a little bit more for other bugfixes for a new minor release.