WIP: System.Environment: thread safe withArgs
This is a tentative fix for #18261
withArgs args action
is not thread safe and its behavior with multiples threads is not reproducible, among other things:
- call of
getArgs
inaction
is not guarantee to returnargs
if anotherwithArgs
in another thread was executed. - call of
getArgs
after thewithArgs
block is not guaranteed to see the initially restored environment. - It can crash the program because of access of a
free
d memory area. Or worse, maybe this could be exploited and lead to a security issue.
This merge request attempts at fixing the crash, and also document the thread unsafe behavior.
In order to fix the possible crash, the program arguments stored in "C side" are only read once and never wrote. In Haskell side, it is stored in an IORef [String]
.
Discussion points
- It use
unsafePerformIO
, is that acceptable? - It will stores a bunch of
String
for the complete lifetime of a program. This may have a (kinda negligible, but existing) impact on the GC. Should we look for a more compact representation (ByteString?
) or some kind of lazy initialization (e.g. we can storeNothing
in theIORef
if the arguments are unchanged from what's stored in "C side")?
Tests
I've "tested" with the following program:
import Control.Monad
import System.Environment
import Control.Concurrent (threadDelay, forkIO)
prog = forkIO $ forever $ do
withArgs ["hello", "goodbye"] $ do
print "chicken"
main = do
replicateM 10 prog
threadDelay 10_000_000
Without this MR, the program crashs (when run with more than 1 thread), with, it does not crash anymore