dpc / mioco.pre-0.9

Scalable, coroutine-based, asynchronous IO handling library for Rust programming language. (aka MIO COroutines).
Mozilla Public License 2.0
457 stars 30 forks source link

Timers not triggering or triggering multiple times #125

Closed lhallam closed 8 years ago

lhallam commented 8 years ago

When using timers with select in a loop, they're registered every time any event is triggered - event_loop.set_timeout is called multiple times, leading to more triggers, and more set_timeout calls.

A small example:

#[macro_use] extern crate mioco;

fn main(){
    mioco::start(move ||{
        let mut timer_a = mioco::timer::Timer::new();
        let mut timer_b = mioco::timer::Timer::new();
        timer_a.set_timeout(1000);
        timer_b.set_timeout(500);
        loop{
            select!(
                timer_a:r => {
                    println!("tick..");
                    timer_a.set_timeout(1000);
                },
                timer_b:r => {
                    println!("tock..");
                    timer_b.set_timeout(500);
                },
                );
        }
    }).unwrap();
}

when select! is first called, both timers are registered, mio will timeout in 1000ms with token 0, and 500ms with token 1.

timer_b triggers first, it's timeout is set to 500ms and select! is called again. Both timers are registered again, so mio now has two timeouts set for the first timer. Both times it's triggered, another two timeouts are set. This continues exponentially, eventually crashing when mio's queue is full.

edit: My current workaround is adding a RefCell to TimerCore to track if it's been registered (https://gist.github.com/xalri/968ddc5e63df000098067fc211aed733)

lhallam commented 8 years ago

A related issue which I couldn't figure out, if you try to read from the timer on one of the extra triggers, it panics:

tock..
tick.. SteadyTime(SteadyTime { tv_sec: 21492, tv_nsec: 543597443 })
thread '<main>' panicked at 'assertion failed: coroutine.last_event.id().as_usize() ==
    self.shared().0.borrow().common.id.unwrap().as_usize()', /home/lewis/.multirust/toolchains/nightly/cargo/registry/src/github.com-88ac128001ac3a9a/mioco-0.4.1/src/evented.rs:58
note: Run with `RUST_BACKTRACE=1` for a backtrace.
thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value: Any', ../src/libcore/result.rs:785
error: Process didn't exit successfully: `target/debug/mioco_timer` (exit code: 101)
dpc commented 8 years ago

Thanks for report! I'll look into that next weekend.

dpc commented 8 years ago

The first issue should be fixed in git master branch. That was a very nice find. Thanks again for reporting it!

dpc commented 8 years ago

Please note that select! syntax changed from io:r to r:io.

dpc commented 8 years ago

I believe the other issue should be gone now, too. Please reopen if the problem is still there.

lhallam commented 8 years ago

Thanks, everything is working now ^^

Something tangentially related I've been wondering about:

I've been using select_add and select_wait rather than the macro to enable/disable specific events at run time. Everywhere I'm using this it's inside a loop, so I'm re-registering usually the same events every time - which, with channels at least, involves expenive atomic operations (for some context, it's part of a game server, I'm selecting over a receiver and a 10ms timer, callgrind shows the deregister_all call in selectwait is about 50% of my CPU usage ><). Would there be a way of manually (re)registering/deregistering my Receivers and Timers, then disabling the deregister_all call in select_wait?

I'll probably make a fork and play with it myself, but I just wanted to get your input beofre I try something which might not be possible :3

lhallam commented 8 years ago

everything is working now ^^

Or perhaps not >_<

As of 24e190e168d11070760a35098029208803b11210 I've had a new timer issue which I can't seem to reproduce: I'm selecting between a Receiver and a Timer in a loop, the receiver's connected (through a few layers of abstraction) to a UDP socket, both are triggered about every 15ms. The problem is that when the Receiver starts triggering, the timer stops. The same code works as intended before 24e190e168d11070760a35098029208803b11210.

Here's the code I'm having issues with: https://gitlab.com/xalri/altserv/blob/master/altserv/src/game.rs#L38

I'll keep trying to reproduce it, and post an issue if I find anything.

Anyway, thanks's for writing and maintaining mioco, I started my project with just mio, switching to this made many things much easier ^_^

edit: My attempt at reproducing the issue: https://gist.github.com/xalri/91e9cadef8fac7e55160b3f1880eb200 It works as expected (except the timer only triggeres every 200ms? #126 ), no sign of the issue I mentioned above..

dpc commented 8 years ago

Will take a look when I have some time. I'm very interested in any weird behaviors and issues so please report everything that might be wrong.

lhallam commented 8 years ago

Increasing the resolution of the timer fixes the issue. Knowing this I finally managed to reproduce it: https://gist.github.com/xalri/baf7b9c7368562f6bdd38f97cd87b2ec

Start the client, then start the server. While the client is receiving messages from the server the timer never triggers. If you set timer_tick_ms to 5 in the client it works as expected, showing ~20 messages being received for every tick of the timer.

dpc commented 8 years ago

OK, so I guess since you opened more precise #126 , we can close this one.