Skip to content

Don't fork processes in the constructor of Evaluator #444

@travisfw

Description

@travisfw

A constructor should be as free of side effects as possible. If I/O has to be done before other operations, I would make this initialization explicit with, for example, a void init() method. I would make the constructor protected and provide a static factory method that also does the initialization and I/O, throwing an IOException or HaskellIOException. A HaskellIOException which subclasses IOException instead of just wrapping the IOException in a HaskellException would make methods that perform I/O more obvious, and I/O exceptions could be handled in the outside context intelligently, whether HaskellIOException or other.

Forking processes is an even heavier operation than I/O, and it should always be transparent to the calling context that it is happening. You can accidentally create zombies and other hazards on the host machine when you fork new system processes. Definitely not something to do in a constructor.

I just cloned viskell and first did mvn package and the tests requiring haskell executables failed at the constructor to Evaluator.

Test set: nl.utwente.viskell.ghcj.GhciEvaluatorTest
-------------------------------------------------------------------------------
Tests run: 3, Failures: 0, Errors: 3, Skipped: 0, Time elapsed: 0.016 sec <<< FAILURE!
typeErrorTest(nl.utwente.viskell.ghcj.GhciEvaluatorTest)  Time elapsed: 0.01 sec  <<< ERROR!
nl.utwente.viskell.ghcj.HaskellException: java.io.IOException: Cannot run program "stack": error=2, No such file or directory
        at nl.utwente.viskell.ghcj.Evaluator.<init>(Evaluator.java:60)
        at nl.utwente.viskell.ghcj.GhciEvaluator.<init>(GhciEvaluator.java:13)
        at nl.utwente.viskell.ghcj.GhciEvaluatorTest.startGhci(GhciEvaluatorTest.java:16)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
        at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
        at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
        at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
        at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: java.io.IOException: Cannot run program "stack": error=2, No such file or directory
        at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
        at nl.utwente.viskell.ghcj.Evaluator.<init>(Evaluator.java:55)
        ... 31 more
Caused by: java.io.IOException: error=2, No such file or directory
        at java.lang.UNIXProcess.forkAndExec(Native Method)
        at java.lang.UNIXProcess.<init>(UNIXProcess.java:247)
        at java.lang.ProcessImpl.start(ProcessImpl.java:134)
        at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
        ... 32 more

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions