summaryrefslogtreecommitdiff
path: root/runtime/js/40_signals.js
blob: 2de58e74cd25fa10e6acabf7d85800597dfa5baa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
"use strict";

((window) => {
  const core = window.Deno.core;
  const { build } = window.__bootstrap.build;
  const { errors } = window.__bootstrap.errors;
  const {
    Error,
    Promise,
    PromisePrototypeThen,
    PromiseResolve,
    SymbolAsyncIterator,
  } = window.__bootstrap.primordials;

  function bindSignal(signo) {
    return core.opSync("op_signal_bind", signo);
  }

  function pollSignal(rid) {
    return core.opAsync("op_signal_poll", rid);
  }

  function unbindSignal(rid) {
    core.opSync("op_signal_unbind", rid);
  }

  function signal(signo) {
    if (build.os === "windows") {
      throw new Error("Signal API is not implemented for Windows");
    }
    return new SignalStream(signo);
  }

  class SignalStream {
    #disposed = false;
    #pollingPromise = PromiseResolve(false);
    #rid = 0;

    constructor(signo) {
      this.#rid = bindSignal(signo);
      this.#loop();
    }

    #pollSignal = async () => {
      let done;
      try {
        done = await pollSignal(this.#rid);
      } catch (error) {
        if (error instanceof errors.BadResource) {
          return true;
        }
        throw error;
      }
      return done;
    };

    #loop = async () => {
      do {
        this.#pollingPromise = this.#pollSignal();
      } while (!(await this.#pollingPromise) && !this.#disposed);
    };

    then(
      f,
      g,
    ) {
      const p = PromisePrototypeThen(this.#pollingPromise, (done) => {
        if (done) {
          // If pollingPromise returns true, then
          // this signal stream is finished and the promise API
          // should never be resolved.
          return new Promise(() => {});
        }
        return;
      });
      return PromisePrototypeThen(p, f, g);
    }

    async next() {
      return { done: await this.#pollingPromise, value: undefined };
    }

    [SymbolAsyncIterator]() {
      return this;
    }

    dispose() {
      if (this.#disposed) {
        throw new Error("The stream has already been disposed.");
      }
      this.#disposed = true;
      unbindSignal(this.#rid);
    }
  }

  window.__bootstrap.signals = {
    signal,
    SignalStream,
  };
})(this);