makeDependent on already started tasks
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 ().
Hi @redneb,
There are several scenarios when makeDependent could be called in such a case:
- The parent task had not yet begun, but the child task had begun. In this case, the call to
makeDependentachieves nothing. This is why you are encouraged to useasyncAfter, orasyncSTMandmakeDependentin the same STM transaction. - Neither task had begun. This establishes the dependency as normal.
- The parent had begun, but the child had not yet begun. This establishes the dependency as normal.
- Both tasks had already begun. This is just like #1 above.
- One of the tasks already finished. This is rather like #1 as well.
- Both tasks had finished. This makes the call a no-op.
John
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 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.