Skip to content

Race condition when using AsyncTimeout #459

@rgov

Description

@rgov

Consider the following example:

import asyncio

from transitions.extensions.asyncio import AsyncMachine, AsyncTimeout
from transitions.extensions.states import add_state_features


@add_state_features(AsyncTimeout)
class CustomMachine(AsyncMachine):
    pass


class Model:
    async def on_enter_completed(self):
        print('entered completed state')
    
    async def on_enter_waiting(self):
        print('entered waiting state')


async def main():
    model = Model()

    states = [
        'start', 
        { 'name': 'waiting', 'timeout': 0.5, 'on_timeout': 'to_waiting' },
        'completed',
    ]
    machine = CustomMachine(model, states, initial='start')
    machine.add_transition('wait', 'start', 'waiting')
    machine.add_transition('complete', 'waiting', 'completed')

    await model.wait()
    await asyncio.sleep(1.0)
    await model.complete()


loop = asyncio.get_event_loop()
loop.create_task(main())
loop.run_forever()
loop.close()

The intended behavior is for it to loop on the waiting state a few times, then transition to the completed state and stop. However, the output looks like this:

entered waiting state
entered waiting state
entered completed state
entered waiting state
entered waiting state
entered waiting state
entered waiting state
[...repeats forever...]

It seems that the timer that transitions back to the waiting state does not get properly canceled when we transition to the completed state.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions