Current section
Files
Jump to
Current section
Files
lib/zig/templates/threaded.zig.eex
const @"Thread-<%= @name %>" = beam.Thread(nif.<%= @alias || @name %>);
pub const @"ThreadResource-<%= @name %>" = beam.Resource(*@"Thread-<%= @name %>", @This(), .{
.Callbacks = beam.ThreadedCallbacks(@"Thread-<%= @name %>")
});
// leak_check is disabled on Windows due to issues with debug allocator stack traces
const leak_check_<%= @name %> = if (builtin.os.tag == .windows) false else <%= @leak_check %>;
<% needs_make? = @params |> Map.values |> Enum.any?(&Type.needs_make?(&1.type)) %>
<% needs_size? = @params |> Enum.any?(fn {_index, param} -> Parameter.needs_size?(param) end) %>
fn @"<%= @name %>-launch"(env: beam.env, argc: c_int, args: [*c] const e.ErlNifTerm) callconv(.c) e.ErlNifTerm {
<%= if allocator = @allocator do %>
const allocator = nif.<%= allocator %>();
<% else %>
const allocator = beam.allocator;
<% end %>
// set context due to reentry
beam.context = .{
.env = env,
// TODO: make this configurable
.mode = .synchronous,
.allocator = allocator,
.io = beam.io.get(allocator),
};
<%= if needs_make? do %>
var error_info: ?beam.term = null;
<% end %>
<%= if needs_size? do %>
var payload_sizes: struct {
<%= for {index, param} <- @params, Parameter.needs_size?(param) do %>
arg<%= index %>: ?usize = null,
<% end %>
} = .{};
<% end %>
const payload_opts = .{
<%= for {index, param} <- @params do %>
.{
<%= Parameter.render_payload_options(param, index) %>
},
<% end %>
};
const cleanup_opts = .{
<%= for {index, param} <- @params, do: Parameter.render_cleanup(param, index) %>
};
const result = @"Thread-<%= @name %>".launch(@"ThreadResource-<%= @name %>", argc, args, payload_opts, cleanup_opts, leak_check_<%= @name %>, <%= if needs_make? do %>&error_info<% else %>null<% end %>) catch |err| {
// OutOfMemory during thread setup (not payload building)
return e.enif_raise_exception(env, beam.make(.{err}, .{}).v);
};
return result.v;
}
fn @"<%= @name %>-join"(env: beam.env, argc: c_int, args: [*c] const e.ErlNifTerm) callconv(.c) e.ErlNifTerm {
// argc must be 1.
_ = argc;
const thread_rsrc = beam.get(@"ThreadResource-<%= @name %>", .{.v = args[0]}, .{}) catch |err| {
return e.enif_raise_exception(env, beam.make(.{
.__struct__ = .@"Elixir.ArgumentError",
.message = beam.make("expected thread resource for join, got invalid term", .{}),
.__exception__ = true,
.__error__ = err,
}, .{}).v);
};
const thread = @"ThreadResource-<%= @name %>".unpack(thread_rsrc);
// set context due to reentry
beam.context = .{
.env = env,
.mode = .threaded,
.allocator = beam.allocator, //thread.allocator
.io = beam.io.get(beam.allocator),
};
const result = thread.join() catch |err| {
const message = switch (err) {
error.memoryleak => "memory leak detected in threaded function `<%= @name %>`",
error.threaderror => "thread error in threaded function `<%= @name %>`",
error.threadtooktoolong => "thread took too long to respond in threaded function `<%= @name %>`",
error.processnotjoined => "failed to join thread in threaded function `<%= @name %>`",
};
return beam.raise_exception(beam.make(.{
.__struct__ = .@"Elixir.RuntimeError",
.message = message,
.__exception__ = true,
}, .{}), .{}).v;
};
if (comptime @"Thread-<%= @name %>".makes_error_result()) {
const result_term = switch(result) {
.ok => |ok_result| beam.make(ok_result, .{}),
.error_return_trace => |error_result| beam.raise_exception(beam.copy(env, error_result), .{}),
};
return result_term.v;
} else {
return beam.make(result, .{}).v;
}
}