riker icon indicating copy to clipboard operation
riker copied to clipboard

Using Riker with Tokio

Open mankinskin opened this issue 4 years ago • 7 comments

Hi, I am having some trouble spawning a future on riker's executor. I want to spawn a stream polling future that should forward messages to an actor. Basically this is the only interesting part:

fn pre_start(&mut self, ctx: &Context<Self::Msg>) {
    let myself = ctx.myself();
    self.handle = Some(ctx.run(async move {
        while let Some(res) = crate::telegram::telegram().api.stream().next().await {
            match res {
                Ok(update) => myself.tell(update, None),
                Err(e) => error!("{}", e),
            }
        }
    }).expect("Failed to spawn telegram stream!"));
}

then I create the actor in my main function. annotated with tokio::main:

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut telegram_actor = crate::actor_sys_mut().await.actor_of::<TelegramActor>("telegram-actor").unwrap();
}

Where actor_sys() lazily creates the ActorSystem:

lazy_static! {
    static ref ACTOR_SYS: Arc<RwLock<ActorSystem>> = Arc::new(RwLock::new(ActorSystem::new().unwrap()));
}
pub async fn actor_sys() -> RwLockReadGuard<'static, ActorSystem> {
    ACTOR_SYS.read().await
}
pub async fn actor_sys_mut() -> RwLockWriteGuard<'static, ActorSystem> {
    ACTOR_SYS.write().await
}

However when I run this with the ctx.run call, I get this error:

thread 'pool-thread-#3' panicked at 'there is no timer running, must be called from the context of Tokio runtime', /home/linusb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.24/src/time/driver/handle.rs:25:14

Am I doing something wrong? How can I solve this? Is there a better approach?

mankinskin avatar Dec 15 '20 18:12 mankinskin

So the problem seems to be that I have a tokio executor running, but riker uses the executor from futures. I'm thinking about configuring riker to use the tokio executor now.

mankinskin avatar Dec 16 '20 08:12 mankinskin

So this is my current state.

The ActorSystem starts the executor, which is currently hardcoded to futures::executor::ThreadPool, which is a handle to a ThreadPool, i.e. it can be cloned.

I have tried to make this field generic using an ExecutorHandle trait, but I realized that ActorSystem is passed all over the crate as a handle to the ActorSystem, so that meant adding generic parameters everywhere, which requires a lot of maintenance.

Then I tried to use cargo features to select the executor used using compiler flags. This got me pretty far, but there are some issues making the tests pass. I also had to edit riker-testkit. which seems to implement some mock actors for testing. I got it to compile, but for some reason the tests won't run through, they are just blocking and not being executed. Now I am looking into it on my forks tokio_executor branches: https://github.com/mankinskin/riker/tree/tokio_executor https://github.com/mankinskin/riker-testkit/tree/tokio_executor

mankinskin avatar Dec 17 '20 09:12 mankinskin

I have come to believe that it would be better to migrate the whole crate to use async/await and a tokio executor, which also intersects with the discussion in #87 . I would probably remove the feature in my fork and focus on migrating the entire crate to tokio using as much async/await as possible. But I will have to get more familiar with the individual components of this crate first. Any comments or suggestions would be much appreciated.

mankinskin avatar Dec 17 '20 13:12 mankinskin

The problem was not with riker or tokio, but rather telegram-bot.. It might be that there is an incompatibility between tokio 0.2 and 0.3

mankinskin avatar Dec 18 '20 13:12 mankinskin

Actually, the problem was still there, even when using tokio 0.2 (in my crate). I thought the issue was that I was using telegram-bot (tokio 0.2) and with tokio 0.3, but that was not it. Even when using 0.2 everywhere, the futures executed by riker conflict with the tokio runtime.

The tokio_executor branch solves this though, by replacing the ThreadPool executor with a tokio::executor::Handle.

The code is a little bit ugly because of all the feature guards, and probably not ready to be merged in this repository. It works for my usecase right now, as I only really want to use tokio, but I would be happy to discuss how to turn this into a PR. Probably we can make the feature guards less extensive by encapsulating some parts of the code more.

mankinskin avatar Dec 18 '20 16:12 mankinskin

@mankinskin Greetings again! I don't know it problem of Tokio or Rust, but using riker from your branch gives me about 74-76% higher RAM usage than stock lib.

BratSinot avatar Aug 03 '21 13:08 BratSinot

@BratSinot this might well be due to tokio: https://www.reddit.com/r/rust/comments/i3fync/why_async_version_of_simple_program_use_to_50x/

I have not benchmarked this branch, but I don't think anything except tokio could allocate this much RAM. In the reddit answer they say it is not really used memory, but just virtual memory, i.e. reserved memory ready to use but not actually used.

mankinskin avatar Aug 03 '21 18:08 mankinskin