summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2018-08-21 16:12:42 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2018-08-22 13:44:40 +0100
commit221855c6fe22abc5c70587482bae3e8c4809eb39 (patch)
tree2c14d0094408784a1742f70ad407655e50189453
parenta27d7e7d3a3e790d7325f69e4ac40a2a3922b5d2 (diff)
igt/shell: Flesh out the os.fork() interface
-rw-r--r--shell/igt-shell.cc1
-rw-r--r--shell/include/os.h1
-rw-r--r--shell/lib/os-fork.cc275
-rw-r--r--shell/lib/os.cc1
-rw-r--r--shell/meson.build1
-rw-r--r--shell/samples/os.fork.js30
6 files changed, 309 insertions, 0 deletions
diff --git a/shell/igt-shell.cc b/shell/igt-shell.cc
index 0e542e4e..3bcc6fb1 100644
--- a/shell/igt-shell.cc
+++ b/shell/igt-shell.cc
@@ -68,6 +68,7 @@ static bool ExecuteString(Isolate* iso, Local<String> source,
Local<Value> name, bool print_result,
bool report_exceptions)
{
+ MicrotasksScope tasks(iso, MicrotasksScope::kRunMicrotasks);
HandleScope handle_scope(iso);
TryCatch try_catch(iso);
diff --git a/shell/include/os.h b/shell/include/os.h
index 6e1f01de..18c7ec44 100644
--- a/shell/include/os.h
+++ b/shell/include/os.h
@@ -4,6 +4,7 @@
#include "v8.h"
extern void os_file_ctor(v8::Isolate *iso, v8::Handle<v8::ObjectTemplate> os);
+extern void os_fork_ctor(v8::Isolate *iso, v8::Handle<v8::ObjectTemplate> os);
extern void os_throw_errno(v8::Isolate *iso, int err);
diff --git a/shell/lib/os-fork.cc b/shell/lib/os-fork.cc
new file mode 100644
index 00000000..992da5ce
--- /dev/null
+++ b/shell/lib/os-fork.cc
@@ -0,0 +1,275 @@
+#include <errno.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "igt-shell.h"
+#include "os.h"
+#include "v8-helpers.h"
+
+using namespace v8;
+
+struct job {
+ Persistent<Object> weakref;
+ unsigned int count;
+ int result;
+ pid_t pids[];
+};
+
+static int __job_wait(Isolate *iso, struct job *j, unsigned int flags)
+{
+ unsigned int alive = 0;
+ int result = j->result;
+
+ for (unsigned int i = 0; i < j->count; i++) {
+ int status, ret;
+
+ while ((ret = waitpid(j->pids[i], &status, flags)) < 0) {
+ int err = errno;
+
+ if (err == SIGINT)
+ continue;
+
+ for (unsigned int x = 0; x < j->count; x++)
+ kill(j->pids[x], SIGTERM);
+
+ if (iso)
+ os_throw_errno(iso, err);
+ }
+
+ if (ret) {
+ if (WIFSIGNALED(status) && !WIFSIGNALED(result))
+ result = status; /* take exception to signals, esp. SIGBUS/SIGSEGV */
+ if (status && !result)
+ result = status;
+ } else {
+ j->pids[alive++] = j->pids[i];
+ }
+ }
+
+ j->count = alive;
+ j->result = result;
+ return result;
+}
+
+/* XXX lazy gc is lazy */
+static void job_dtor(const WeakCallbackInfo<job>& weak)
+{
+ struct job *j = weak.GetParameter();
+
+ j->weakref.Reset();
+
+ if (j->count) {
+ auto iso = weak.GetIsolate();
+
+ if (__job_wait(iso, j, 0))
+ iso->ThrowException(Exception::Error(AsString(iso, "unchecked job error").ToLocalChecked()));
+ }
+
+ free(j);
+}
+
+static void job_cancel(struct job *j, unsigned int count)
+{
+ for (unsigned int i = 0; i < count; i++)
+ kill(j->pids[i], SIGTERM);
+
+ for (unsigned int i = 0; i < count; i++)
+ waitpid(j->pids[i], NULL, 0);
+
+ free(j);
+}
+
+static void job_wait(const FunctionCallbackInfo<Value>& args)
+{
+ struct job *j =
+ (struct job *)args.This()->GetAlignedPointerFromInternalField(0);
+
+ args.GetReturnValue().Set(__job_wait(args.GetIsolate(), j, 0));
+}
+
+static void job_kill(const FunctionCallbackInfo<Value>& args)
+{
+ auto ctx = args.GetIsolate()->GetCurrentContext();
+ int sig = args[0]->Int32Value(ctx).FromMaybe(SIGINT);
+
+ struct job *j =
+ (struct job *)args.This()->GetAlignedPointerFromInternalField(0);
+
+ for (unsigned int i = 0; i < j->count; i++)
+ kill(j->pids[i], sig);
+}
+
+static struct job *job_create(unsigned int count)
+{
+ struct job *j = (struct job *)malloc(sizeof(*j) + count * sizeof(pid_t));
+ if (!j)
+ return NULL;
+
+ memset(static_cast<void*>(j), 0, sizeof(*j));
+ j->count = count;
+ return j;
+}
+
+static void job_result(Local<String> prop,
+ const PropertyCallbackInfo<Value>& info)
+{
+ struct job *j = (struct job *)info.This()->GetAlignedPointerFromInternalField(0);
+ auto iso = info.GetIsolate();
+
+ __job_wait(iso, j, WNOHANG);
+ if (j->count == 0)
+ info.GetReturnValue().Set(Uint32::New(iso, j->result));
+}
+
+static void attach_job(Isolate *iso, ReturnValue<Value> rv, struct job *j)
+{
+ HandleScope scope(iso);
+
+ Local<ObjectTemplate> tmpl = ObjectTemplate::New(iso);
+ tmpl->SetInternalFieldCount(1);
+ tmpl->Set(AsString(iso, "wait").ToLocalChecked(),
+ FunctionTemplate::New(iso, job_wait));
+ tmpl->Set(AsString(iso, "kill").ToLocalChecked(),
+ FunctionTemplate::New(iso, job_kill));
+ tmpl->SetAccessor(AsString(iso, "result").ToLocalChecked(), job_result);
+
+ auto ctx = iso->GetCurrentContext();
+ Local<Object> obj = tmpl->NewInstance(ctx).ToLocalChecked();
+ obj->SetAlignedPointerInInternalField(0, j);
+
+ j->weakref.Reset(iso, obj);
+ j->weakref.SetWeak(j, job_dtor, WeakCallbackType::kParameter);
+
+ rv.Set(obj);
+}
+
+static void fork_caller(const FunctionCallbackInfo<Value>& args)
+{
+ auto iso = args.GetIsolate();
+
+ struct job *j = job_create(1);
+ if (!j) {
+ os_throw_errno(iso, ENOMEM);
+ return;
+ }
+
+ j->pids[0] = fork();
+ if (j->pids[0] < 0) {
+ os_throw_errno(iso, errno);
+ return;
+ }
+
+ if (!j->pids[0]) /* child */
+ return;
+
+ attach_job(iso, args.GetReturnValue(), j);
+}
+
+__attribute__((noreturn))
+static void do_child(Isolate *iso, Local<Value> v_fn, Local<Value> arg)
+{
+ HandleScope scope(iso);
+ TryCatch try_catch(iso);
+
+ Local<Context> ctx = iso->GetCurrentContext();
+ Local<Function> fn = Local<Function>::Cast(v_fn->ToObject());
+ Local<Value> result = fn->Call(ctx, fn, 1, &arg).ToLocalChecked();
+
+ if (try_catch.HasCaught())
+ abort();
+
+ exit(result->Int32Value(ctx).FromMaybe(0));
+}
+
+static void fork_once(const FunctionCallbackInfo<Value>& args)
+{
+ auto iso = args.GetIsolate();
+
+ struct job *j = job_create(1);
+ if (!j) {
+ os_throw_errno(iso, ENOMEM);
+ return;
+ }
+
+ j->pids[0] = fork();
+ if (j->pids[0] < 0) {
+ os_throw_errno(iso, errno);
+ return;
+ }
+
+ if (!j->pids[0])
+ do_child(iso, args[1], args[0]);
+
+ attach_job(iso, args.GetReturnValue(), j);
+}
+
+static void fork_array(const FunctionCallbackInfo<Value>& args)
+{
+ auto iso = args.GetIsolate();
+
+ Local<Array> a = Local<Array>::Cast(args[0]->ToObject());
+
+ struct job *j = job_create(a->Length());
+ if (!j) {
+ os_throw_errno(iso, ENOMEM);
+ return;
+ }
+
+ for (unsigned int i = 0; i < a->Length(); i++) {
+ j->pids[i] = fork();
+ if (j->pids[i] < 0) {
+ job_cancel(j, i);
+ os_throw_errno(iso, errno);
+ return;
+ }
+
+ if (!j->pids[i]) {
+ Local<Value> v = a->Get(iso->GetCurrentContext(), i).ToLocalChecked();
+ do_child(iso, args[1], v);
+ }
+ }
+
+ attach_job(iso, args.GetReturnValue(), j);
+}
+
+static void os_fork(const FunctionCallbackInfo<Value>& args)
+{
+ auto iso = args.GetIsolate();
+
+ if (args.Length() == 0) {
+ fork_caller(args);
+ return;
+ }
+
+ if (args.Length() != 2) {
+ iso->ThrowException(Exception::SyntaxError(AsString(iso, "bad args").ToLocalChecked()));
+ return;
+ }
+
+ if (!args[1]->IsFunction()) {
+ iso->ThrowException(Exception::TypeError(AsString(iso, "bad function arg").ToLocalChecked()));
+ return;
+ }
+
+ if (args[0]->IsArray())
+ fork_array(args);
+ else
+ fork_once(args);
+}
+
+static void os_exit(const FunctionCallbackInfo<Value>& args)
+{
+ auto ctx = args.GetIsolate()->GetCurrentContext();
+ exit(args[0]->Int32Value(ctx).FromMaybe(0));
+}
+
+void os_fork_ctor(Isolate *iso, Handle<ObjectTemplate> os)
+{
+ HandleScope handle_scope(iso);
+
+ os->Set(AsString(iso, "fork").ToLocalChecked(),
+ FunctionTemplate::New(iso, os_fork));
+ os->Set(AsString(iso, "exit").ToLocalChecked(),
+ FunctionTemplate::New(iso, os_exit));
+}
diff --git a/shell/lib/os.cc b/shell/lib/os.cc
index 64fe64eb..c23670fd 100644
--- a/shell/lib/os.cc
+++ b/shell/lib/os.cc
@@ -93,6 +93,7 @@ static void os_builtin_ctor(Isolate *iso, Handle<ObjectTemplate> igt)
tmpl->SetAccessor(AsString(iso, "now").ToLocalChecked(), os_now);
os_file_ctor(iso, tmpl);
+ os_fork_ctor(iso, tmpl);
igt->Set(iso, "os", tmpl);
}
diff --git a/shell/meson.build b/shell/meson.build
index a361172b..8b76ffac 100644
--- a/shell/meson.build
+++ b/shell/meson.build
@@ -4,6 +4,7 @@ executable('igt', [
'lib/igt-toy.cc',
'lib/os.cc',
'lib/os-file.cc',
+ 'lib/os-fork.cc',
'lib/v8-helpers.cc',
],
objects: 'v8/libv8_monolith.a',
diff --git a/shell/samples/os.fork.js b/shell/samples/os.fork.js
new file mode 100644
index 00000000..debdebcc
--- /dev/null
+++ b/shell/samples/os.fork.js
@@ -0,0 +1,30 @@
+igt.os.fork('hello', print).wait();
+
+igt.os.fork([ {delay: 1, msg:'goodbye' }, {delay:0, msg:'world'} ],
+ function(x) { igt.os.sleep(x.delay); print(x.msg) }).wait();
+
+var job = igt.os.fork('', function(x) { igt.os.exit(1); igt.os.sleep(999); });
+print('Exitcode = ' + job.wait());
+
+job = igt.os.fork();
+if (job) {
+ print("Parent shall wait for its child!");
+ job.wait();
+} else {
+ print("The child wants to play!");
+ for (var x = 0; x < 10; x++) {
+ print("Play " + x);
+ igt.os.sleep(0.1);
+ }
+ print("Tired now!");
+ igt.os.exit(0);
+}
+print("Only parents allowed after children sleep, exitcode:", job.result);
+
+job = igt.os.fork();
+if (job) {
+ print("No Mr Bond, I expect you to", job.wait());
+} else {
+ badwolf();
+}
+print("Onwards, all by myself.");