async-pool icon indicating copy to clipboard operation
async-pool copied to clipboard

makeDependent on already started tasks

Open redneb opened this issue 11 years ago • 3 comments

I find makeDependent a little bit confusing: what happens if it is called in order to indicate that a particular task has dependencies when that task has already been started? For example

main :: IO ()
main = do
    p <- createPool
    withTaskGroupIn p 2 $ \g -> do
        t1 <- async g task1
        t2 <- async g task2
        threadDelay 1000 -- wait 1ms for task2 to start
        atomically $
            makeDependent p (taskHandle t2) (taskHandle t1)
        wait t1
        wait t2

for some task1, task2 :: IO ().

redneb avatar Sep 10 '14 15:09 redneb

Hi @redneb,

There are several scenarios when makeDependent could be called in such a case:

  1. The parent task had not yet begun, but the child task had begun. In this case, the call to makeDependent achieves nothing. This is why you are encouraged to use asyncAfter, or asyncSTM and makeDependent in the same STM transaction.
  2. Neither task had begun. This establishes the dependency as normal.
  3. The parent had begun, but the child had not yet begun. This establishes the dependency as normal.
  4. Both tasks had already begun. This is just like #1 above.
  5. One of the tasks already finished. This is rather like #1 as well.
  6. Both tasks had finished. This makes the call a no-op.

John

jwiegley avatar Sep 11 '14 11:09 jwiegley

Right. So I guess the point of this ticket was that (a) makeDependent should be removed from the public api in favor of asyncSTM/asyncAfter (and possibly other additions if necessary), or (b) the documentation should be updated to indicate that sometimes makeDependent is a no-op and its use should be discouraged. In general, I feel that makeDependent in not particularly elegant and it does not fit well with the rest of the module which is nicely designed.

redneb avatar Sep 11 '14 12:09 redneb

@redneb I see your point. The problem with asyncAfter is that it dwells in IO, and so establishes only a 1-to-1 dependency without race. I included asyncSTM and makeDependent so that you could express arbitrary n-to-m dependencies within the scope of a single transaction, eliminating races. It should be clear from the types that only STM can give you reasonable results, but I will document this fact about makeDependent.

The other possibility is to add asyncAfterSTM, and drop makeDependent, and have both asyncAfter functions accept a list of Async handles.

The problem there is that I want to leave open the flexibility that one part of a user's application is responsible for submitting tasks, and another part (also in the STM transaction) is responsible for associating them. But I could certainly be placed in an "advanced" section of the documentation, and marked out with caveats.

jwiegley avatar Sep 12 '14 01:09 jwiegley