[Git][ghc/ghc][wip/js-th] make thrunner stdin asynchronous, fixing macOS/Windows
Luite Stegeman (@luite)
gitlab at gitlab.haskell.org
Mon Jan 23 05:58:56 UTC 2023
Luite Stegeman pushed to branch wip/js-th at Glasgow Haskell Compiler / GHC
Commits:
75b6a584 by Luite Stegeman at 2023-01-23T14:55:04+09:00
make thrunner stdin asynchronous, fixing macOS/Windows
warning: buffering very inefficient
- - - - -
2 changed files:
- libraries/base/jsbits/base.js
- thrunner.js
Changes:
=====================================
libraries/base/jsbits/base.js
=====================================
@@ -677,8 +677,12 @@ if(h$isNode()) {
c(0);
}
- process.stdin.on('readable', h$base_process_stdin);
- process.stdin.on('end', function() { h$base_stdin_eof = true; h$base_process_stdin(); });
+ // if we are in a Template Haskell context (global.h$TH is set) then we
+ // should keep away from stdin, because it's used for communication with GHC
+ if(typeof global.h$TH !== 'object') {
+ process.stdin.on('readable', h$base_process_stdin);
+ process.stdin.on('end', function() { h$base_stdin_eof = true; h$base_process_stdin(); });
+ }
h$base_isattyStdin = function() { return process.stdin.isTTY; };
h$base_isattyStdout = function() { return process.stdout.isTTY; };
=====================================
thrunner.js
=====================================
@@ -11,8 +11,8 @@
n: response to request n
*/
-var h$THfs = require('node:fs');
-var h$THvm = require('node:vm');
+var h$THfs = require('fs');
+var h$THvm = require('vm');
if(typeof __dirname == 'undefined') {
// TODO work out the best shim to use here
@@ -67,10 +67,9 @@ function h$lookupClosure(v) {
globalThis.h$loadJS = h$loadJS;
globalThis.h$lookupClosure = h$lookupClosure;
-function h$initTH() {
+async function h$initTH() {
console.log("Welcome to GHC's JS interpreter");
- process.stdin.setEncoding('binary');
process.stderr.setEncoding('binary');
process.stdin.on('end', () => {
@@ -81,28 +80,21 @@ function h$initTH() {
h$interp_loop();
}
-function h$interp_loop() {
- const fd = h$THfs.openSync('/dev/stdin', 'rs');
-
- const cmd_buf = Buffer.alloc(1);
- const int32_buf = Buffer.alloc(4);
-
- const getInt32 = () => {
- h$THfs.readSync(fd, int32_buf, 0, 4);
- return (new Int32Array(int32_buf))[0];
- }
+async function h$interp_loop() {
+ async function getInt32() {
+ let b = await readStdin(4);
+ return new Int32Array(b)[0];
+ }
- const getBuffer = (size) => {
- const b = Buffer.alloc(size);
- h$THfs.readSync(fd, b, 0, size);
- return b;
- }
+ async function getByte() {
+ let b = await readStdin(1);
+ return b[0];
+ }
console.log("Entering interpreter loop...");
- while(1) {
- h$THfs.readSync(fd, cmd_buf, 0, 1);
- const cmd_id = cmd_buf[0];
+ while(true) {
+ const cmd_id = await getByte();
// interpret command
switch (cmd_id) {
@@ -114,8 +106,8 @@ function h$interp_loop() {
// load JS file
case 1:
- const payload_size = getInt32();
- const payload = getBuffer(payload_size);
+ const payload_size = await getInt32();
+ const payload = await readStdin(payload_size);
const p = payload.toString('utf8');
h$loadJS(p);
break;
@@ -132,11 +124,55 @@ function h$interp_loop() {
}
}
+/////////////////////////////////////////////////////////////////////
+// stdin buffering helpers
+//
+// usage: await readStdin(n);
+// this returns a buffer with n bytes from stdin
+//
+// perhaps we can use some existing infrastructure instead of
+// managing our own buffers
+/////////////////////////////////////////////////////////////////////
+let stdinBufs = [], stdinWaiters = [];
+process.stdin.on('data', (d) => {
+ stdinBufs.push(d);
+ let waiters = stdinWaiters;
+ stdinWaiters = [];
+ for(const r of waiters) r();
+});
+
+
+// helper function, reads n bytes from stdin if available in the buffer,
+// returns null otherwise
+//
+// warning: this is very inefficient for larger buffers
+function getStdinData(n) {
+ if(stdinBufs.length == 0) return null;
+ let b = stdinBufs.length == 1 ? stdinBufs[0] : Buffer.concat(stdinBufs);
+ if(b.length >= n) {
+ let r = b.slice(0, n);
+ stdinBufs = [b.slice(n)];
+ return r;
+ } else {
+ if(stdinBufs.length !== 1) stdinBufs = [b];
+ return null;
+ }
+}
+
+async function readStdin(n) {
+ while(true) {
+ let d = getStdinData(n);
+ if(d) {
+ return d;
+ } else {
+ await new Promise((resolve) => { stdinWaiters.push(resolve); });
+ }
+ }
+}
+/////////////////////////////////////////////////////////////////////
+
function h$runServer() {
console.log("Run server");
- // don't allow Haskell to read from stdin
- h$base_stdin_fd.read = function(fd, fdo, buf, buf_offset, n, c) { c(0); }
- // run server
h$main(h$ghciZCGHCiziServerzidefaultServer);
}
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/75b6a584057d9f162339bd2f40f6535ec8613a0f
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/75b6a584057d9f162339bd2f40f6535ec8613a0f
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20230123/3c1731a5/attachment-0001.html>
More information about the ghc-commits
mailing list