Haskell ghci能否在非安全性能IO块内重新排序IO操作
是否可以对Haskell ghci能否在非安全性能IO块内重新排序IO操作,haskell,ghci,Haskell,Ghci,是否可以对unsafePerformIO内的IO块调用中的IO操作重新排序? 我有有效的IO功能 assembleInsts :: ... -> IO S.ByteString assembleInsts ... = do tmpInputFile <- generateUniqueTmpFile writeFile tmpInputFile str (ec,out,err) <- readProcessWithExitCode asm_exe [tmp
unsafePerformIO
内的IO块调用中的IO操作重新排序?
我有有效的IO功能
assembleInsts :: ... -> IO S.ByteString
assembleInsts ... = do
tmpInputFile <- generateUniqueTmpFile
writeFile tmpInputFile str
(ec,out,err) <- readProcessWithExitCode asm_exe [tmpInputFile] ""
-- asm generates binary output in tmpOutputFile
removeFile tmpInputFile
let tmpOutputFile = replaceExtension tmpIsaFile "bits" -- assembler creates this
bs <- S.readFile tmpOutputFile -- fails due to tmpOutputFile not existing
removeFile tmpOutputFile
return bs
此外,我在模块顶部添加了以下注释
按照文档的(System.IO.Unsafe
)说明
{-# OPTIONS -fno-cse #-}
module Gen.IsaAsm where
(我也尝试添加了-fnofull lazness
,正如一篇参考文章所说的那样
我咨询过,但编译器拒绝了。我不这么认为
不过,这种情况在这里也适用。)
在ghci
中运行时,会报告以下错误
*** Exception: C:\Users\trbauer\AppData\Local\Temp\tempfile_13516_0.dat: openBinaryFile: does not exist (No such file or directory)
但是如果我删除removefiletmpoutputfile
,它就会神奇地工作。
因此,removeFile
似乎在进程终止之前执行。
这可能吗?bytestring非常严格,我甚至尝试在某一点上使用以下命令强制输出:
S.length bs `seq` return ()
在删除文件之前
import qualified Data.ByteString.Lazy as S
...
let tmpDatFile = tmpOutputFile
是否有方法转储中间代码以了解发生了什么?
(也许我可以用Process Monitor或其他什么东西来追踪这一点。)
不幸的是,我想在此操作中进行清理(删除文件)
我认为exe版本可能会工作,但在ghci下它会失败(解释)。
我正在使用上一个Haskell平台中的GHC 7.6.3
我知道
unsafePerformIO
是一个非常大的问题,并且有其他相关风险,但它确实会限制我的软件更改的复杂性。这可能不适用,因为它是基于您问题中未指定的假设。特别是,这个答案基于以下两个假设<未指定的code>S是数据.ByteString.Lazy
和tmpDatFile
,未定义的是tmpoutfile
import qualified Data.ByteString.Lazy as S
...
let tmpDatFile = tmpOutputFile
可能原因
如果这些假设成立,removeFile
将过早运行,即使不使用unsafePerformIO
。下面的代码
import System.Directory
import qualified Data.ByteString.Lazy as S
assembleInsts = do
-- prepare a file, like asm might have generated
let tmpOutputFile = "dataFile.txt"
writeFile tmpOutputFile "a bit of text"
-- read the prepared file
let tmpDatFile = tmpOutputFile
bs <- S.readFile tmpOutputFile
removeFile tmpDatFile
return bs
main = do
bs <- assembleInsts
print bs
而是产生正确的输出
“一点文字”
解释
Data.ByteSting.Lazy
的readFile
文档声明它将
通过testring将整个文件惰性地读入中。手柄将保持打开状态,直到遇到EOF
在内部,readfile
通过调用unsafeInterleaveIO
来实现这一点unsafeInterleaveIO
延迟IO代码的执行,直到对其返回的术语进行计算
hGetContentsN :: Int -> Handle -> IO ByteString
hGetContentsN k h = lazyRead -- TODO close on exceptions
where
lazyRead = unsafeInterleaveIO loop
loop = do
c <- S.hGetSome h k -- only blocks if there is no data available
if S.null c
then do hClose h >> return Empty
else do cs <- lazyRead
return (Chunk c cs)
hGetContentsN::Int->Handle->IO ByteString
hGetContentsN k h=lazyRead——异常时关闭TODO
哪里
lazyRead=未缓冲的交错循环
循环=do
c>返回空
否则,即使您使用的是unsafePerformIO
,也不应重新排序IO操作。如果您想确定这一点,可以使用-ddump siml
标志查看GHC生成的中间核心语言,甚至其中一种语言显示汇编之前的所有编译中间步骤
我知道这回答了您的问题,而不是您实际需要的,但您至少可以排除GHC bug。不过,GHC中似乎不太可能存在影响此功能的bug。完全是我的错。。。。对不起大家。GHC不会在上述条件下对IO块中的IO操作重新排序。汇编程序未能汇编输出并创建假定的文件。我只是忘记检查汇编器的退出代码或输出流。我假设输入在语法上是正确的,因为它是生成的,汇编程序拒绝了它,只是无法创建文件。它给出了一个有效的错误代码和错误诊断,所以这对我来说真的很糟糕。我可能第一次使用了readProcess
,这会在非零退出时引发异常,但最终一定会改变这一点。我认为汇编程序有一个错误,在某些情况下,它没有正确地指示失败的退出代码,我不得不从readProcessWithExitCode
更改
我仍然不确定当我省略removeFile
时,错误为什么消失了
import qualified Data.ByteString.Lazy as S
...
let tmpDatFile = tmpOutputFile
我想删除这个问题,但我希望上面的建议也能帮助其他人调试类似的(更有效的)问题。我已经被Cirdec提到的懒惰IO事情烧坏了,chi提到的-ddump siml
标志也很好地知道。我的第一个猜测是readProcessWithExitCode
和S.readFile
正在做懒惰I/O。。。尽管我猜测,S
代表Data.ByteString.Strict
。。。所以我不确定。值得一看吗?“readProcessWithExitCode
创建一个外部进程,严格读取其标准输出和标准错误,等待进程终止,然后返回进程的ExitCode
、标准输出和标准错误。”是S
数据.ByteString.Lazy
还是数据.ByteString.Strict
?如果S
是Data.ByteString.Lazy
,那么tmpDatFile
是什么,它与tmpOutputFile
有何不同?抱歉,我更正了歧义S
是一个严格的字节字符串tmpDatFile
确实应该是tmpOutputFile
。它是由通过readProcessWithExitCode
启动的外部进程创建的。标准输出流被忽略且不需要。对于建议的解决方案,S
实际上是一个严格的字节字符串。即使如此,在删除文件之前,我还是尝试通过在S.length
中输入seq
来强制输出。这并没有改变结果。