Loading "Hello world" into Atom throws EBADF #302

Open
edsko opened this Issue Feb 3, 2015 · 22 comments

Comments

Projects
None yet
4 participants
@edsko

edsko commented Feb 3, 2015

I'm trying to experiment with ghcjs and seeing if it would be possible to use Haskell to write plugins for Atom, an editor based on node.js. The first problem is that this means that the generated code somehow needs to be available as a node.js module. I tried writing a small wrapper around the generated code, but since eval is not available and the generated code does not work when loaded using node's vm module, that doesn't seem possible. However, it's easy enough to change the last line of all.js to

exports.main = function() { h$main(h$mainZCMainzimain); }

At this point this works when calling node.js from the command line:

hsHello = require('./hs/Hello.js');
hsHello.main();

which dutifully prints "Hello world". However, when I try do the same within atom (started in dev mode) this throws an exception:

[23753:0203/193534:INFO:CONSOLE(7506)] "Uncaught setErrno not yet implemented: Error: EBADF, write", source: .../hs/Hello.js (7506)

(when starting atom normally this crashes the editor completely); this exception is thrown from h$setErrno. (I realize that this may not be a ghcjs problem per se -- but perhaps the generated code is making some assumptions that are not always true? I have no idea.)

@edsko

This comment has been minimized.

Show comment
Hide comment
@edsko

edsko Feb 4, 2015

Sorry, more complete stack trace:

  • The exception is thrown by h$setErrno
  • which is called in the definition of h$base_writeFile in the if(h$isNode) conditional.
  • which is called from within write from node.js's fs module (in particular, from within strWrapper)

If I add an additional line to h$setErrno to do

      if(es.indexOf('EBADF') !== -1) return 9;

then calling main again crashes the whole editor outright, developer mode or not.

Not sure if that helps any :)

edsko commented Feb 4, 2015

Sorry, more complete stack trace:

  • The exception is thrown by h$setErrno
  • which is called in the definition of h$base_writeFile in the if(h$isNode) conditional.
  • which is called from within write from node.js's fs module (in particular, from within strWrapper)

If I add an additional line to h$setErrno to do

      if(es.indexOf('EBADF') !== -1) return 9;

then calling main again crashes the whole editor outright, developer mode or not.

Not sure if that helps any :)

@luite

This comment has been minimized.

Show comment
Hide comment
@luite

luite Feb 5, 2015

Member

hmm, the editor might be breaking the node.js check. Does the plugin node.js specific things for the base library? otherwise you could try compiling with -DGHCJS_BROWSER, which strips the node.js parts.

Otherwise I'll probably need to investigate a bit more, to see what this environment looks like. Dynamically loading code is a bit tricky, the template haskell runner uses a trick to load GHCJS code in the global context, but it depends on eval being available:

https://github.com/ghcjs/ghcjs/blob/master/lib/etc/thrunner.js#L139

I couldn't get this to work with the vm module, but i think the situation for Atom should be easier, since you probably don't need to load multiple scripts into the same context.

Member

luite commented Feb 5, 2015

hmm, the editor might be breaking the node.js check. Does the plugin node.js specific things for the base library? otherwise you could try compiling with -DGHCJS_BROWSER, which strips the node.js parts.

Otherwise I'll probably need to investigate a bit more, to see what this environment looks like. Dynamically loading code is a bit tricky, the template haskell runner uses a trick to load GHCJS code in the global context, but it depends on eval being available:

https://github.com/ghcjs/ghcjs/blob/master/lib/etc/thrunner.js#L139

I couldn't get this to work with the vm module, but i think the situation for Atom should be easier, since you probably don't need to load multiple scripts into the same context.

@edsko

This comment has been minimized.

Show comment
Hide comment
@edsko

edsko Feb 6, 2015

Ok, I just tried this. When I compile the code with -DGHCJS_BROWSER and again make the same modification to all.js, it now gets a little bit further; I see

[62837:0206/190729:INFO:CONSOLE(23427)] "Hello world
", source: /Users/dev/wt/util/atom/jstest/hs/Hello.js (23427)

on the console, but after that the whole thing crashes. Crashes crashes -- no stack trace, nothing. I've tried to debug this a bit further but I've so far been unable to get any more information on what's causing the crash. I've posted a message on the Atom message boards asking how to get more info; if that turns up anything I will let you know.

edsko commented Feb 6, 2015

Ok, I just tried this. When I compile the code with -DGHCJS_BROWSER and again make the same modification to all.js, it now gets a little bit further; I see

[62837:0206/190729:INFO:CONSOLE(23427)] "Hello world
", source: /Users/dev/wt/util/atom/jstest/hs/Hello.js (23427)

on the console, but after that the whole thing crashes. Crashes crashes -- no stack trace, nothing. I've tried to debug this a bit further but I've so far been unable to get any more information on what's causing the crash. I've posted a message on the Atom message boards asking how to get more info; if that turns up anything I will let you know.

@edsko

This comment has been minimized.

Show comment
Hide comment
@edsko

edsko Feb 6, 2015

I just happened to come across the Deployment page (which is not linked from anywhere?) and I figured I'd give that a go. So I wrapped all.js in

(function(global) {
// contents of all.js, minus the last line
global.runMainAction = function() {
  h$main(h$mainZCMainzimain); 
}
})(exports);

and tried again, and although I'm getting the Hello world output again, the editor still crashes immediately after :(

edsko commented Feb 6, 2015

I just happened to come across the Deployment page (which is not linked from anywhere?) and I figured I'd give that a go. So I wrapped all.js in

(function(global) {
// contents of all.js, minus the last line
global.runMainAction = function() {
  h$main(h$mainZCMainzimain); 
}
})(exports);

and tried again, and although I'm getting the Hello world output again, the editor still crashes immediately after :(

@luite

This comment has been minimized.

Show comment
Hide comment
@luite

luite Feb 6, 2015

Member

I've just downloaded the editor, hopefully later today on the bus I'll have some time to check what's going on.

Member

luite commented Feb 6, 2015

I've just downloaded the editor, hopefully later today on the bus I'll have some time to check what's going on.

@edsko

This comment has been minimized.

Show comment
Hide comment
@edsko

edsko Feb 7, 2015

Great! I don't know how far you'll get though without any stack trace or any other info about what's going wrong.. But if you wanted to try, it's easy: start atom using atom -d -f on the command line (that leaves in the foreground and enables dev mode), open the dev console (View, Developer, Toggle Developer Tools), and then just try to execute the generated Haskell code from the dev console:

hsHello = require('/Users/dev/wt/util/atom/jstest/hs/Hello.js');
hsHello.runMainAction();

You will see (or at least, I see, on OSX) Hello being printed to the terminal (though not, somewhat oddly, to the chromium dev console), and then the editor will crash immediately after.

edsko commented Feb 7, 2015

Great! I don't know how far you'll get though without any stack trace or any other info about what's going wrong.. But if you wanted to try, it's easy: start atom using atom -d -f on the command line (that leaves in the foreground and enables dev mode), open the dev console (View, Developer, Toggle Developer Tools), and then just try to execute the generated Haskell code from the dev console:

hsHello = require('/Users/dev/wt/util/atom/jstest/hs/Hello.js');
hsHello.runMainAction();

You will see (or at least, I see, on OSX) Hello being printed to the terminal (though not, somewhat oddly, to the chromium dev console), and then the editor will crash immediately after.

@edsko

This comment has been minimized.

Show comment
Hide comment
@edsko

edsko Feb 9, 2015

Although Atom reports this as "The editor has crashed", hooking up lldb to the various atom processes actually shows a normal termination of two of those processes at the time that the editor reports that it has crashed. Might it be that the JavaScript code generated by ghcjs actually requests the process to terminate? Is that even possible?

edsko commented Feb 9, 2015

Although Atom reports this as "The editor has crashed", hooking up lldb to the various atom processes actually shows a normal termination of two of those processes at the time that the editor reports that it has crashed. Might it be that the JavaScript code generated by ghcjs actually requests the process to terminate? Is that even possible?

@luite

This comment has been minimized.

Show comment
Hide comment
@luite

luite Feb 9, 2015

Member

Yes that's quite possible, when the RTS detects node.js it exits the
process when the main thread finishes. Try running the main action with
h$run rather than h$main to prevent this
On 9 Feb 2015 22:46, "Edsko de Vries" notifications@github.com wrote:

Although Atom reports this as "The editor has crashed", hooking up lldb
to the various atom processes actually shows a normal termination
https://discuss.atom.io/t/how-to-debug-atom-crash-on-osx/14756 of two
of those processes at the time that the editor reports that it has crashed.
Might it be that the JavaScript code generated by ghcjs actually
requests the process to terminate? Is that even possible?


Reply to this email directly or view it on GitHub
#302 (comment).

Member

luite commented Feb 9, 2015

Yes that's quite possible, when the RTS detects node.js it exits the
process when the main thread finishes. Try running the main action with
h$run rather than h$main to prevent this
On 9 Feb 2015 22:46, "Edsko de Vries" notifications@github.com wrote:

Although Atom reports this as "The editor has crashed", hooking up lldb
to the various atom processes actually shows a normal termination
https://discuss.atom.io/t/how-to-debug-atom-crash-on-osx/14756 of two
of those processes at the time that the editor reports that it has crashed.
Might it be that the JavaScript code generated by ghcjs actually
requests the process to terminate? Is that even possible?


Reply to this email directly or view it on GitHub
#302 (comment).

@edsko

This comment has been minimized.

Show comment
Hide comment
@edsko

edsko Feb 9, 2015

Hurray! That worked! No more editor crashes! Mind that I did compile the code with -DGHCJS_BROWSER so that's a little strange? But progress at last!

edsko commented Feb 9, 2015

Hurray! That worked! No more editor crashes! Mind that I did compile the code with -DGHCJS_BROWSER so that's a little strange? But progress at last!

@luite

This comment has been minimized.

Show comment
Hide comment
@luite

luite Feb 9, 2015

Member

I suspect the main thread cleanup code was added before the GHCJS_BROWSER
macro. I'll review the code and add the macro tomorrow if necessary, after
some sleep (still on New Zealand time)
On 10 Feb 2015 01:47, "Edsko de Vries" notifications@github.com wrote:

Hurray! That worked! No more editor crashes! Mind that I did compile the
code with -DGHCJS_BROWSER so that's a little strange? But progress at
last!


Reply to this email directly or view it on GitHub
#302 (comment).

Member

luite commented Feb 9, 2015

I suspect the main thread cleanup code was added before the GHCJS_BROWSER
macro. I'll review the code and add the macro tomorrow if necessary, after
some sleep (still on New Zealand time)
On 10 Feb 2015 01:47, "Edsko de Vries" notifications@github.com wrote:

Hurray! That worked! No more editor crashes! Mind that I did compile the
code with -DGHCJS_BROWSER so that's a little strange? But progress at
last!


Reply to this email directly or view it on GitHub
#302 (comment).

@edsko

This comment has been minimized.

Show comment
Hide comment
@edsko

edsko Feb 9, 2015

Sure, that's an orthogonal issue anyway, and not one that I'm blocked on for now. I shall continue playing with this now, and if I get something working, I'll try to write up a blog post about atom+ghcjs.

edsko commented Feb 9, 2015

Sure, that's an orthogonal issue anyway, and not one that I'm blocked on for now. I shall continue playing with this now, and if I get something working, I'll try to write up a blog post about atom+ghcjs.

@luite

This comment has been minimized.

Show comment
Hide comment
@luite

luite Feb 9, 2015

Member

Ok, if text processing performance turns out to be a problem, I almost have
a ghcjs-base library update ready which replicates the Data.Text API for
JSString, with much better performance.
On 10 Feb 2015 01:55, "Edsko de Vries" notifications@github.com wrote:

Sure, that's an orthogonal issue anyway, and not one that I'm blocked on
for now. I shall continue playing with this now, and if I get something
working, I'll try to write up a blog post about atom+ghcjs.


Reply to this email directly or view it on GitHub
#302 (comment).

Member

luite commented Feb 9, 2015

Ok, if text processing performance turns out to be a problem, I almost have
a ghcjs-base library update ready which replicates the Data.Text API for
JSString, with much better performance.
On 10 Feb 2015 01:55, "Edsko de Vries" notifications@github.com wrote:

Sure, that's an orthogonal issue anyway, and not one that I'm blocked on
for now. I shall continue playing with this now, and if I get something
working, I'll try to write up a blog post about atom+ghcjs.


Reply to this email directly or view it on GitHub
#302 (comment).

@edsko

This comment has been minimized.

Show comment
Hide comment
@edsko

edsko Feb 9, 2015

Out of curiosity I just tried loading the Haskell code compiled without -DGHCJS_BROWSER and that still throws the EBADF exception (although it no longer crashes the editor now that the process.exit call is gone).

edsko commented Feb 9, 2015

Out of curiosity I just tried loading the Haskell code compiled without -DGHCJS_BROWSER and that still throws the EBADF exception (although it no longer crashes the editor now that the process.exit call is gone).

@luite

This comment has been minimized.

Show comment
Hide comment
@luite

luite Feb 9, 2015

Member

I suspect there's some assumption that stdin or stdout is available on
node.js baked in some where
On 10 Feb 2015 02:05, "Edsko de Vries" notifications@github.com wrote:

Out of curiosity I just tried loading the Haskell code compiled without
-DGHCJS_BROWSER and that still throws the EBADF exception (although it no
longer crashes the editor now that the process.exit call is gone).


Reply to this email directly or view it on GitHub
#302 (comment).

Member

luite commented Feb 9, 2015

I suspect there's some assumption that stdin or stdout is available on
node.js baked in some where
On 10 Feb 2015 02:05, "Edsko de Vries" notifications@github.com wrote:

Out of curiosity I just tried loading the Haskell code compiled without
-DGHCJS_BROWSER and that still throws the EBADF exception (although it no
longer crashes the editor now that the process.exit call is gone).


Reply to this email directly or view it on GitHub
#302 (comment).

@luite

This comment has been minimized.

Show comment
Hide comment
@luite

luite Feb 9, 2015

Member

I've changed the exit handling code. There were two places where the process could be shut down, one is h$doneMain when the main thread is finished, the other h$shutdownHaskellAndExit called by the base library, that's now both handled in a single place. I'm still debugging some other scheduler related issues, I'll push when i get things to validate again (although the issues aren't likely to be related to this change).

Member

luite commented Feb 9, 2015

I've changed the exit handling code. There were two places where the process could be shut down, one is h$doneMain when the main thread is finished, the other h$shutdownHaskellAndExit called by the base library, that's now both handled in a single place. I'm still debugging some other scheduler related issues, I'll push when i get things to validate again (although the issues aren't likely to be related to this change).

@edsko

This comment has been minimized.

Show comment
Hide comment
@edsko

edsko Feb 9, 2015

Excellent! Meanwhile, I have very basic integration into atom working; following the JQuery example, set up a Haskell thread that listens on a Chan for requests, executes them in order, and calls associated callbacks.

> haskell = require('/Users/dev/wt/util/atom/jstest/hs/Hello.js');
Object {ArrayBuffer: function, Int8Array: function, Uint8Array: function, Uint8ClampedArray: function, Int16Array: function…}
> haskell.main()
> haskell.request(5, function(k) { console.log("Factorial of 5 is", k);} )
Factorial of 5 is 120

Pretty cool :) I am planning to play with this a lot more and actually implement a package for Atom, and then write it up as a blogpost. ghcjs is awesome work @luite :)

The only annoying thing right now is that I have to start a do-nothing thread that just waits forever and after forever passes writes to the Chan, or else the main thread is killed with a "blocked indefinitely on MVar" exception; but I think I saw something similar somewhere in the other examples? Anyway, not a major deal, just feels that there ought to be a nicer way to do this.

edsko commented Feb 9, 2015

Excellent! Meanwhile, I have very basic integration into atom working; following the JQuery example, set up a Haskell thread that listens on a Chan for requests, executes them in order, and calls associated callbacks.

> haskell = require('/Users/dev/wt/util/atom/jstest/hs/Hello.js');
Object {ArrayBuffer: function, Int8Array: function, Uint8Array: function, Uint8ClampedArray: function, Int16Array: function…}
> haskell.main()
> haskell.request(5, function(k) { console.log("Factorial of 5 is", k);} )
Factorial of 5 is 120

Pretty cool :) I am planning to play with this a lot more and actually implement a package for Atom, and then write it up as a blogpost. ghcjs is awesome work @luite :)

The only annoying thing right now is that I have to start a do-nothing thread that just waits forever and after forever passes writes to the Chan, or else the main thread is killed with a "blocked indefinitely on MVar" exception; but I think I saw something similar somewhere in the other examples? Anyway, not a major deal, just feels that there ought to be a nicer way to do this.

@edsko

This comment has been minimized.

Show comment
Hide comment
@edsko

edsko Feb 15, 2015

Ok, blog post Writing Atom plugins in Haskell using ghcjs published. Comments/feedback/corrections welcome!

edsko commented Feb 15, 2015

Ok, blog post Writing Atom plugins in Haskell using ghcjs published. Comments/feedback/corrections welcome!

@luite

This comment has been minimized.

Show comment
Hide comment
@luite

luite Feb 15, 2015

Member

awesome, I'll read it later today

Member

luite commented Feb 15, 2015

awesome, I'll read it later today

@edsko

This comment has been minimized.

Show comment
Hide comment
@edsko

edsko Feb 17, 2015

Incidentally, requiring -DGHJCS_BROWSER is a bit unfortunate, as it means for example that we have no access to the file system in Haskell. Not a huge deal for me right, but worth fixing at some point nonetheless.

edsko commented Feb 17, 2015

Incidentally, requiring -DGHJCS_BROWSER is a bit unfortunate, as it means for example that we have no access to the file system in Haskell. Not a huge deal for me right, but worth fixing at some point nonetheless.

@luite

This comment has been minimized.

Show comment
Hide comment
@luite

luite Feb 17, 2015

Member

Ah good point, I'll look into this. It might just take some twiddling with the assumptions around the standard streams.

Member

luite commented Feb 17, 2015

Ah good point, I'll look into this. It might just take some twiddling with the assumptions around the standard streams.

@sylwiabr

This comment has been minimized.

Show comment
Hide comment
@sylwiabr

sylwiabr Oct 25, 2016

Hey, what is the status of this issue? Are there any plans to solve it?

Hey, what is the status of this issue? Are there any plans to solve it?

@wereHamster

This comment has been minimized.

Show comment
Hide comment
@wereHamster

wereHamster Jan 4, 2017

I just tried to use a GHCJS-compiled application inside Electron. I too had to set GHCJS_BROWSER, otherwise the code would not work. The issue is that Electron exports process (and other nodejs-specific globals) even if the JavaScript code is running on the 'browser' side.

So in Electron you have both window and document but also process etc. I think we should treat it as a separate platform (next to browser, node, JVM, jsshell etc). Instead of the automagic detection of the platform (in shims/src/platform.js), I'd much rather have a compiler option which allows me to explicitly set the platform (similar to how C-compilers have a --target= option).

I just tried to use a GHCJS-compiled application inside Electron. I too had to set GHCJS_BROWSER, otherwise the code would not work. The issue is that Electron exports process (and other nodejs-specific globals) even if the JavaScript code is running on the 'browser' side.

So in Electron you have both window and document but also process etc. I think we should treat it as a separate platform (next to browser, node, JVM, jsshell etc). Instead of the automagic detection of the platform (in shims/src/platform.js), I'd much rather have a compiler option which allows me to explicitly set the platform (similar to how C-compilers have a --target= option).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment