Skip to content

Documentation should explicitly clarify thread cancelation order #187

Description

@guibou

Reading the description of #27 I realized that race (and actually, concurrently) argument order is not arbitrary.

I was "naively" thinking that in the context of an async exception received by the race thread, it would be propagated at the same time to both arguments of race. However, it is first propagated to the "right" one, then to the "left" one (once the "right" thread had terminated). The example with withAsync in the documentation is clear about that, but implicit.

This can lead to delay in the shutdown of the "left" thread AND even "infinite" lock if the "right" thread is stuck in an uninterruptible task and actually waits for the left thread "signal" to stop.

For example, the following piece of code:

stopJob <- newIORef False

let
   actionA = do
        someInterruptibleOperation `finally` writeIORef stopJob True

   actionB = do
      -- It is not interruptible (e.g. FFI call), but will cooperatively stop when stopJob value is turned to True
      someNotInterruptibleAction stopJob

race actionA actionB

In this context, when the main thread will receive an async exception, it will be first sent to actionB, which will never stop and hence actionA will never get the message and will hence never stop actionB. Using race actionB actionA instead solves this part of the problem.

Note that race actionB actionA is not safe either because actionA can be interrupted immediately before the exception handler is set and as a result actionB will never be see the stopJob value to True. You have to ensure that the exception handler in actionA is setup before actionB reachs the ininterruptible state.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions