Asynchronous exceptions and laziness bugs in Data.Unique.newUnique

Hello, I just browsed over the source of Data.Unique.newUnique[1]: newUnique :: IO Unique newUnique = do val <- takeMVar uniqSource let next = val+1 putMVar uniqSource next return (Unique next) I think asynchronous exceptions should be blocked. Currently if an asynchronous exception is thrown after taking the MVar, putMVar is not executed anymore, leaving the MVar in the empty state. A subsequent call to newUnique will then dead-lock when it tries to take the MVar. Another problem is the laziness of 'next'. When you put 'next' in the MVar you actually put the thunk 'val+1' not an Integer in WHNF. When you create say a million uniques the last unique will be a very large thunk like 0+1+1...+1. Forcing that thunk (when you print the hash of the unique for example) will probably overflow your stack. The attached patch adds the necessary block and strictly evaluates the next unique value. regards, Bas [1] http://haskell.org/ghc/docs/latest/html/libraries/base-4.2.0.0/src/Data-Uniq...

On 17/03/2010 15:49, Bas van Dijk wrote:
Hello,
I just browsed over the source of Data.Unique.newUnique[1]:
newUnique :: IO Unique newUnique = do val<- takeMVar uniqSource let next = val+1 putMVar uniqSource next return (Unique next)
I think asynchronous exceptions should be blocked. Currently if an asynchronous exception is thrown after taking the MVar, putMVar is not executed anymore, leaving the MVar in the empty state. A subsequent call to newUnique will then dead-lock when it tries to take the MVar.
Another problem is the laziness of 'next'. When you put 'next' in the MVar you actually put the thunk 'val+1' not an Integer in WHNF. When you create say a million uniques the last unique will be a very large thunk like 0+1+1...+1. Forcing that thunk (when you print the hash of the unique for example) will probably overflow your stack.
The attached patch adds the necessary block and strictly evaluates the next unique value.
Thanks, I'm actually going to change this to use STM as it does a better job than MVars in this case. Cheers, Simon

On Thu, Mar 18, 2010 at 11:45 AM, Simon Marlow
Thanks, I'm actually going to change this to use STM as it does a better job than MVars in this case.
Hi Simon, I see you actually write 'val+1' to the MVar: newUnique :: IO Unique newUnique = atomically $ do val <- readTVar uniqSource let next = val+1 writeTVar uniqSource $! val + 1 return (Unique next) Why don't you write 'next' so that when you force the returned Unique it doesn't need to be computed a second time? (The attached patch fixes it) regards, Bas

On 19/03/2010 13:16, Bas van Dijk wrote:
On Thu, Mar 18, 2010 at 11:45 AM, Simon Marlow
wrote: Thanks, I'm actually going to change this to use STM as it does a better job than MVars in this case.
Hi Simon, I see you actually write 'val+1' to the MVar:
newUnique :: IO Unique newUnique = atomically $ do val<- readTVar uniqSource let next = val+1 writeTVar uniqSource $! val + 1 return (Unique next)
Why don't you write 'next' so that when you force the returned Unique it doesn't need to be computed a second time?
(The attached patch fixes it)
Yes, silly mistake. I'll apply your patch, thanks. Cheers, Simon
participants (2)
-
Bas van Dijk
-
Simon Marlow