Skip to content

Commit

Permalink
gets threaded erroring working
Browse files Browse the repository at this point in the history
  • Loading branch information
ityonemo committed May 23, 2024
1 parent 5240207 commit 4624416
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 75 deletions.
2 changes: 1 addition & 1 deletion lib/zig/_module.ex
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ defmodule Zig.Module do
if Enum.any?(module.nifs, &match?(%Error{}, &1.return.type)) do
quote do
require Zig.Manifest
Zig.Manifest.resolver(unquote(module.manifest), unquote(module.module_code_path), :defp)
Zig.Manifest.resolver(unquote(module.manifest), unquote(module.zig_code_path), :defp)
end
end

Expand Down
11 changes: 6 additions & 5 deletions lib/zig/_nif.ex
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,18 @@ defmodule Zig.Nif do
based on nif options for this function keyword at (opts :: nifs :: function_name)
"""
def new(name, file, opts!) do
opts! = adjust(opts!)
opts! = adjust(opts!)

%__MODULE__{
name: name,
file: file,
export: Keyword.get(opts!, :export, true),
concurrency: Map.get(@concurrency_modules, opts![:concurrency], Synchronous)
}
}
end

def adjust(opts) do
Enum.map(opts, fn
Enum.map(opts, fn
concurrency when concurrency in @concurrency_opts -> {:concurrency, concurrency}
{atom, _} = kv when is_atom(atom) -> atom

Check warning on line 109 in lib/zig/_nif.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 25.0 / Elixir 1.13.4

variable "kv" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 109 in lib/zig/_nif.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.14.5

variable "kv" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 109 in lib/zig/_nif.ex

View workflow job for this annotation

GitHub Actions / Linux OTP 26.0 / Elixir 1.15.2

variable "kv" is unused (if the variable is not meant to be used, prefix it with an underscore)
end)
Expand Down Expand Up @@ -221,7 +222,7 @@ defmodule Zig.Nif do

def maybe_catch(_), do: nil

def resources(nif),do: nif.concurrency.resources(nif)
def resources(nif), do: nif.concurrency.resources(nif)

# COMMON TOOLS
# generates AST for parameters.
Expand All @@ -241,7 +242,7 @@ defmodule Zig.Nif do

def binding_error(name, arity) do
"nif for function #{name}/#{arity} not bound"
end
end

# Access behaviour guards
@impl true
Expand Down
4 changes: 2 additions & 2 deletions lib/zig/error_prong.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ defmodule Zig.ErrorProng do
]
end

def error_return_prong(:elixir, ignored) do
def return_error_prong(:elixir, ignored) do
stacktrace_prep =
case ignored do
[] ->
Expand Down Expand Up @@ -111,7 +111,7 @@ defmodule Zig.ErrorProng do
end
end

def error_return_prong(:erlang, _) do
def return_error_prong(:erlang, _) do
["error:{error, Type, _ExtraStacktrace}:Stacktrace -> erlang:raise(error, Type, Stacktrace)"]
end
end
11 changes: 6 additions & 5 deletions lib/zig/nif/_basic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ defmodule Zig.Nif.Basic do
To understand wrapping logic, see `Zig.Nif.Marshaller`
"""

alias Zig.ErrorProng
alias Zig.Nif
alias Zig.Nif.DirtyCpu
alias Zig.Nif.DirtyIo
Expand Down Expand Up @@ -112,15 +113,15 @@ defmodule Zig.Nif.Basic do
end
]

argument_error_prong =
argument_error_prong = [ErrorProng.argument_error_prong(:elixir, nif.file, nif.line)]

error_return_prong =
List.wrap(
if true do
Zig.ErrorProng.argument_error_prong(:elixir, nif.file, nif.line)
if match?(%Zig.Type.Error{}, nif.signature.return) do
ErrorProng.return_error_prong(:elixir, [marshal_name])
end
)

error_return_prong = []

error_prongs =
case {argument_error_prong, error_return_prong} do
{[], []} -> []
Expand Down
2 changes: 1 addition & 1 deletion lib/zig/nif/synchronous.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule Zig.Nif.Synchronous do
@impl true
def table_entries(nif) do
nif
|> Nif.arities
|> Nif.arities()
|> Enum.map(&{Basic.entrypoint(nif), &1, nif.name, :synchronous})
end

Expand Down
62 changes: 44 additions & 18 deletions lib/zig/nif/threaded.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,62 @@ defmodule Zig.Nif.Threaded do
import Zig.QuoteErl

@impl true
#def render_elixir(%{raw: raw} = nif) when not is_nil(raw) do
# def render_elixir(%{raw: raw} = nif) when not is_nil(raw) do
# entrypoints = Enum.map(nif.params, fn arity ->
# unused_params = Nif.elixir_parameters(arity, false)
#
#
# quote do
# unquote(Nif.style(nif))(unquote(nif.name)(unquote_splicing(unused_params))) do
# :erlang.nif_error(unquote(error_text(nif, arity)))
# end
# end
# end)
#end
# end

def render_elixir(%{signature: %{arity: arity}} = nif) do
used_params_ast = Nif.elixir_parameters(arity, true)
unused_params_ast = Nif.elixir_parameters(arity, false)

# TODO: marshal parameters (that need to be marshalled) here.

quote context: Elixir do
unquote(Nif.style(nif))(unquote(nif.name)(unquote_splicing(used_params_ast))) do
ref = unquote(entrypoint(nif, :launch))(unquote_splicing(used_params_ast))

receive do
{:done, ^ref} ->
unquote(entrypoint(nif, :join))(ref)

{:error, ^ref} ->
raise unquote("thread for function #{nif.name} failed during launch")
function_code = [
do:
quote do
ref = unquote(entrypoint(nif, :launch))(unquote_splicing(used_params_ast))

receive do
{:done, ^ref} ->
unquote(entrypoint(nif, :join))(ref)

{:error, ^ref} ->
raise unquote("thread for function #{nif.name} failed during launch")
end
end
]

argument_error_prong = ErrorProng.argument_error_prong(:elixir, nif.file, nif.line)

error_return_prong =
List.wrap(
if match?(%Zig.Type.Error{}, nif.signature.return) do
ErrorProng.return_error_prong(:elixir, [entrypoint(nif, :join)])
end
)

error_prongs =
case {argument_error_prong, error_return_prong} do
{[], []} -> []
_ -> [catch: argument_error_prong ++ error_return_prong]
end

function_block = function_code ++ error_prongs

quote context: Elixir do
unquote(Nif.style(nif))(
unquote(nif.name)(unquote_splicing(used_params_ast)),
unquote(function_block)
)

defp unquote(entrypoint(nif, :launch))(unquote_splicing(unused_params_ast)) do
:erlang.nif_error(unquote(Nif.binding_error(nif.name, arity)))
end
Expand Down Expand Up @@ -100,9 +125,10 @@ defmodule Zig.Nif.Threaded do
launch = entrypoint(nif, :launch)
join = entrypoint(nif, :join)

launch_entries = nif
|> Nif.arities()
|> Enum.flat_map(&[{launch, &1, :"@\"#{launch}\"", :synchronous}])
launch_entries =
nif
|> Nif.arities()
|> Enum.flat_map(&[{launch, &1, :"@\"#{launch}\"", :synchronous}])

[{join, 1, :"@\"#{join}\"", :synchronous} | launch_entries]
end
Expand All @@ -114,10 +140,10 @@ defmodule Zig.Nif.Threaded do
@impl true
def resources(nif), do: [{:root, :"@\"ThreadResource-#{nif.name}\""}]

#defp error_prongs(nif) do
# defp error_prongs(nif) do
# nif.signature.params
# |> Enum.map(&Type.error_prongs(&1, :argument))
# |> List.insert_at(0, Type.error_prongs(nif.signature.return, :return))
# |> List.flatten()
#end
# end
end
14 changes: 9 additions & 5 deletions lib/zig/resources.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ defmodule Zig.Resources do
EEx.function_from_file(:def, :render, resources, [:assigns])

defp beam_type(resource) do
inner_name = case Atom.to_string(resource) do
"@" <> rest ->
String.trim(rest, "\"")
normal -> normal
end
inner_name =
case Atom.to_string(resource) do
"@" <> rest ->
String.trim(rest, "\"")

normal ->
normal
end

~s(@"beam-type-#{inner_name}")
end

Expand Down
4 changes: 2 additions & 2 deletions lib/zig/sema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ defmodule Zig.Sema do
nif_opts = Keyword.get(specified_fns, function.name, module.default_nif_opts)

function.name
|> Nif.new(module.module_code_path, nif_opts |> dbg)
|> Nif.new(module.module_code_path, nif_opts)
|> apply_from_sema(function, nif_opts)
|> set_file_line(module.manifest_module, module.parsed)
end)
Expand All @@ -153,7 +153,7 @@ defmodule Zig.Sema do
Enum.map(selected_fns, fn {name, nif_opts} ->
if function = Enum.find(functions, &(&1.name == name)) do
name
|> Nif.new(module.module_code_path, nif_opts |> dbg)
|> Nif.new(module.module_code_path, nif_opts)
|> apply_from_sema(function, nif_opts)
|> set_file_line(module.manifest_module, module.parsed)
else
Expand Down
72 changes: 36 additions & 36 deletions test/integration/concurrency/threaded_automatic_erroring_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@ defmodule ZiglerTest.Concurrency.ThreadedAutomaticErroringTest do
use ZiglerTest.IntegrationCase, async: true

@moduletag [threaded: true, erroring: true]
test "restore"

# use Zig, otp_app: :zigler, nifs: [threaded: [:threaded]]
#
# ~Z"""
# const beam = @import("beam");
#
# const ThreadError = error{BadNumber};
#
# pub fn threaded(number: i32) !i32 {
# if (number == 42) { return error.BadNumber; }
# return number + 1;
# }
# """
#
# test "threaded function can succeed" do
# assert 48 = threaded(47)
# end
#
# # note the line numbers might not be correct, hence the skip.
# @tag :skip
# test "threaded function can error" do
# error =
# try do
# threaded(42)
# rescue
# e in ErlangError ->
# %{payload: e.original, stacktrace: __STACKTRACE__}
# end
#
# assert %{payload: :BadNumber, stacktrace: [head | _]} = error
#
# expected_file = Path.absname(__ENV__.file)
#
# assert {__MODULE__, :threaded, [:...], [file: ^expected_file, line: 13]} = head
# end

use Zig, otp_app: :zigler, nifs: [threaded: [:threaded]]

~Z"""
const beam = @import("beam");
const ThreadError = error{BadNumber};
pub fn threaded(number: i32) !i32 {
if (number == 42) { return error.BadNumber; }
return number + 1;
}
"""

test "threaded function can succeed" do
assert 48 = threaded(47)
end

# note the line numbers might not be correct, hence the skip.
test "threaded function can error" do
error =
try do
threaded(42)
rescue
e in ErlangError ->
%{payload: e.original, stacktrace: __STACKTRACE__}
end

assert %{payload: :BadNumber, stacktrace: [head | _] = stacktrace} = error

expected_file = Path.absname(__ENV__.file)

assert {__MODULE__, :threaded, [:...], [file: ^expected_file, line: 14]} = head

refute Enum.any?(stacktrace, fn {_, function, _, _} -> function == :"threaded-join" end)
end
end

0 comments on commit 4624416

Please sign in to comment.