Skip to main content

lib/quickbeam/worker_api.ex

defmodule QuickBEAM.WorkerAPI do
  @moduledoc false

  @worker_bootstrap """
  globalThis.self = globalThis;
  self.postMessage = function(data) {
    Beam.call("__worker_post", data);
  };
  Object.defineProperty(self, "onmessage", {
    set(handler) {
      Beam.onMessage(msg => handler({ data: msg }));
    },
    configurable: true,
  });
  """

  @spec spawn_worker([String.t()], pid()) :: integer()
  def spawn_worker([script], parent_pid) do
    worker_id = System.unique_integer([:positive])

    {:ok, child} =
      QuickBEAM.start(
        handlers: %{
          "__worker_post" => fn [data] ->
            send(parent_pid, {:worker_msg, worker_id, data})
            nil
          end
        }
      )

    send(parent_pid, {:worker_register, worker_id, child})

    QuickBEAM.eval(child, @worker_bootstrap)

    Task.start(fn ->
      try do
        case QuickBEAM.eval(child, script) do
          {:ok, _} -> :ok
          {:error, err} -> send(parent_pid, {:worker_error, worker_id, err})
        end
      catch
        :exit, {:normal, _} -> :ok
        :exit, :normal -> :ok
      end
    end)

    worker_id
  end

  @spec post_to_child(list(), pid()) :: nil
  def post_to_child([worker_id, data], parent_pid) do
    send(parent_pid, {:worker_post_to_child, worker_id, data})
    nil
  end

  @spec terminate_worker([integer()], pid()) :: nil
  def terminate_worker([worker_id], parent_pid) do
    send(parent_pid, {:worker_terminate, worker_id})
    nil
  end
end