Skip to content

Commit

Permalink
Add support for node() bif
Browse files Browse the repository at this point in the history
Also add support for undocumented `erlang:setnode/2` nif.

Signed-off-by: Paul Guyot <[email protected]>
  • Loading branch information
pguyot committed Nov 24, 2024
1 parent 6823697 commit 34d2bec
Show file tree
Hide file tree
Showing 15 changed files with 150 additions and 2 deletions.
8 changes: 7 additions & 1 deletion libs/estdlib/src/erlang.erl
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@
term_to_binary/1,
timestamp/0,
universaltime/0,
localtime/0
localtime/0,
setnode/2
]).

-export_type([
Expand Down Expand Up @@ -1254,3 +1255,8 @@ universaltime() ->
-spec localtime() -> calendar:datetime().
localtime() ->
erlang:nif_error(undefined).

%% @hidden
-spec setnode(atom(), pos_integer()) -> true.
setnode(_NodeName, _Creation) ->
erlang:nif_error(undefined).
5 changes: 5 additions & 0 deletions src/libAtomVM/bif.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ term bif_erlang_self_0(Context *ctx)
return term_from_local_process_id(ctx->process_id);
}

term bif_erlang_node_0(Context *ctx)
{
return ctx->global->node_name;
}

term bif_erlang_byte_size_1(Context *ctx, uint32_t fail_label, int live, term arg1)
{
UNUSED(live);
Expand Down
1 change: 1 addition & 0 deletions src/libAtomVM/bif.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ extern "C" {
const struct ExportedFunction *bif_registry_get_handler(AtomString module, AtomString function, int arity);

term bif_erlang_self_0(Context *ctx);
term bif_erlang_node_0(Context *ctx);
term bif_erlang_byte_size_1(Context *ctx, uint32_t fail_label, int live, term arg1);
term bif_erlang_bit_size_1(Context *ctx, uint32_t fail_label, int live, term arg1);
term bif_erlang_length_1(Context *ctx, uint32_t fail_label, int live, term arg1);
Expand Down
1 change: 1 addition & 0 deletions src/libAtomVM/bifs.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct BifNameAndPtr
};
%%
erlang:self/0, {.bif.base.type = BIFFunctionType, .bif.bif0_ptr = bif_erlang_self_0}
erlang:node/0, {.bif.base.type = BIFFunctionType, .bif.bif0_ptr = bif_erlang_node_0}
erlang:length/1, {.gcbif.base.type = GCBIFFunctionType, .gcbif.gcbif1_ptr = bif_erlang_length_1}
erlang:byte_size/1, {.gcbif.base.type = GCBIFFunctionType, .gcbif.gcbif1_ptr = bif_erlang_byte_size_1}
erlang:bit_size/1, {.gcbif.base.type = GCBIFFunctionType, .gcbif.gcbif1_ptr = bif_erlang_bit_size_1}
Expand Down
14 changes: 14 additions & 0 deletions src/libAtomVM/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#define BYTES_PER_TERM (TERM_BITS / 8)

static struct ResourceMonitor *context_monitors_handle_terminate(Context *ctx);
static void context_distribution_handle_terminate(Context *ctx);
static void destroy_extended_registers(Context *ctx, unsigned int live);

Context *context_new(GlobalContext *glb)
Expand Down Expand Up @@ -132,6 +133,11 @@ void context_destroy(Context *ctx)
// Ensure process is not registered
globalcontext_maybe_unregister_process_id(ctx->global, ctx->process_id);

// Handle distribution termination
if (UNLIKELY(ctx->flags & Distribution)) {
context_distribution_handle_terminate(ctx);
}

// When monitor message is sent, process is no longer in the table
// and is no longer registered either.
struct ResourceMonitor *resource_monitor = context_monitors_handle_terminate(ctx);
Expand Down Expand Up @@ -448,6 +454,14 @@ static struct ResourceMonitor *context_monitors_handle_terminate(Context *ctx)
return result;
}

static void context_distribution_handle_terminate(Context *ctx)
{
// For now, the only process with Distribution flag set is net_kernel.
GlobalContext *glb = ctx->global;
glb->node_name = NONODE_AT_NOHOST_ATOM;
glb->creation = 0;
}

int context_link(Context *ctx, term link_pid)
{
struct ListHead *item;
Expand Down
1 change: 1 addition & 0 deletions src/libAtomVM/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ enum ContextFlags
Ready = 8,
Killed = 16,
Trap = 32,
Distribution = 64,
};

enum HeapGrowthStrategy
Expand Down
6 changes: 6 additions & 0 deletions src/libAtomVM/defaultatoms.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ static const char *const unicode_atom = "\x7" "unicode";

static const char *const global_atom = "\x6" "global";

static const char *const nonode_at_nohost_atom = "\xD" "nonode@nohost";
static const char *const net_kernel_atom = "\xA" "net_kernel";

void defaultatoms_init(GlobalContext *glb)
{
int ok = 1;
Expand Down Expand Up @@ -308,6 +311,9 @@ void defaultatoms_init(GlobalContext *glb)

ok &= globalcontext_insert_atom(glb, global_atom) == GLOBAL_ATOM_INDEX;

ok &= globalcontext_insert_atom(glb, nonode_at_nohost_atom) == NONODE_AT_NOHOST_ATOM_INDEX;
ok &= globalcontext_insert_atom(glb, net_kernel_atom) == NET_KERNEL_ATOM_INDEX;

if (!ok) {
AVM_ABORT();
}
Expand Down
8 changes: 7 additions & 1 deletion src/libAtomVM/defaultatoms.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@ extern "C" {

#define GLOBAL_ATOM_INDEX 111

#define PLATFORM_ATOMS_BASE_INDEX 112
#define NONODE_AT_NOHOST_ATOM_INDEX 112
#define NET_KERNEL_ATOM_INDEX 113

#define PLATFORM_ATOMS_BASE_INDEX 114

#define FALSE_ATOM TERM_FROM_ATOM_INDEX(FALSE_ATOM_INDEX)
#define TRUE_ATOM TERM_FROM_ATOM_INDEX(TRUE_ATOM_INDEX)
Expand Down Expand Up @@ -317,6 +320,9 @@ extern "C" {

#define GLOBAL_ATOM TERM_FROM_ATOM_INDEX(GLOBAL_ATOM_INDEX)

#define NONODE_AT_NOHOST_ATOM TERM_FROM_ATOM_INDEX(NONODE_AT_NOHOST_ATOM_INDEX)
#define NET_KERNEL_ATOM TERM_FROM_ATOM_INDEX(NET_KERNEL_ATOM_INDEX)

void defaultatoms_init(GlobalContext *glb);

void platform_defaultatoms_init(GlobalContext *glb);
Expand Down
3 changes: 3 additions & 0 deletions src/libAtomVM/globalcontext.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ GlobalContext *globalcontext_new()
smp_spinlock_init(&glb->ref_ticks_spinlock);
#endif

glb->node_name = NONODE_AT_NOHOST_ATOM;
glb->creation = 0;

#if HAVE_OPEN && HAVE_CLOSE
ErlNifEnv env;
erl_nif_env_partial_init_from_globalcontext(&env, glb);
Expand Down
3 changes: 3 additions & 0 deletions src/libAtomVM/globalcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ struct GlobalContext
SpinLock env_spinlock;
#endif

term ATOMIC node_name;
uint32_t ATOMIC creation;

#if HAVE_OPEN && HAVE_CLOSE
ErlNifResourceType *posix_fd_resource_type;
#endif
Expand Down
44 changes: 44 additions & 0 deletions src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
#include "synclist.h"
#include "sys.h"
#include "term.h"
#include "term_typedef.h"
#include "unicode.h"
#include "utils.h"

Expand Down Expand Up @@ -164,6 +165,7 @@ static term nif_erlang_function_exported(Context *ctx, int argc, term argv[]);
static term nif_erlang_garbage_collect(Context *ctx, int argc, term argv[]);
static term nif_erlang_group_leader(Context *ctx, int argc, term argv[]);
static term nif_erlang_get_module_info(Context *ctx, int argc, term argv[]);
static term nif_erlang_setnode(Context *ctx, int argc, term argv[]);
static term nif_erlang_memory(Context *ctx, int argc, term argv[]);
static term nif_erlang_monitor(Context *ctx, int argc, term argv[]);
static term nif_erlang_demonitor(Context *ctx, int argc, term argv[]);
Expand Down Expand Up @@ -653,6 +655,12 @@ static const struct Nif get_module_info_nif =
.nif_ptr = nif_erlang_get_module_info
};

static const struct Nif setnode_nif =
{
.base.type = NIFFunctionType,
.nif_ptr = nif_erlang_setnode
};

static const struct Nif raise_nif =
{
.base.type = NIFFunctionType,
Expand Down Expand Up @@ -3912,6 +3920,42 @@ static term nif_erlang_get_module_info(Context *ctx, int argc, term argv[])
return result;
}

static term nif_erlang_setnode(Context *ctx, int argc, term argv[])
{
UNUSED(argc);

VALIDATE_VALUE(argv[0], term_is_atom);
VALIDATE_VALUE(argv[1], term_is_integer);

avm_int64_t creation = term_maybe_unbox_int64(argv[1]);
if (UNLIKELY(creation < 0 || creation > ((avm_int64_t) 1) << 32)) {
RAISE_ERROR(BADARG_ATOM);
}

int netkernel_pid = globalcontext_get_registered_process(ctx->global, NET_KERNEL_ATOM_INDEX);
if (UNLIKELY(netkernel_pid == 0)) {
RAISE_ERROR(BADARG_ATOM);
}

Context *net_kernel = globalcontext_get_process_lock(ctx->global, netkernel_pid);
if (IS_NULL_PTR(net_kernel)) {
RAISE_ERROR(BADARG_ATOM);
}

// Take advantage of the lock on net_kernel process
if (UNLIKELY(ctx->global->node_name != NONODE_AT_NOHOST_ATOM)) {
globalcontext_get_process_unlock(ctx->global, net_kernel);
RAISE_ERROR(BADARG_ATOM);
}
ctx->global->node_name = argv[0];
ctx->global->creation = (uint32_t) creation;

context_update_flags(net_kernel, ~NoFlags, Distribution);
globalcontext_get_process_unlock(ctx->global, net_kernel);

return TRUE_ATOM;
}

struct RefcBinaryAVMPack
{
struct AVMPackData base;
Expand Down
1 change: 1 addition & 0 deletions src/libAtomVM/nifs.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ erlang:group_leader/0, &group_leader_nif
erlang:group_leader/2, &group_leader_nif
erlang:get_module_info/1, &get_module_info_nif
erlang:get_module_info/2, &get_module_info_nif
erlang:setnode/2, &setnode_nif
erts_debug:flat_size/1, &flat_size_nif
ets:new/2, &ets_new_nif
ets:insert/2, &ets_insert_nif
Expand Down
2 changes: 2 additions & 0 deletions tests/erlang_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@ compile_erlang(maps_nifs)
compile_erlang(test_raw_raise)

compile_erlang(test_ets)
compile_erlang(test_node)

add_custom_target(erlang_test_modules DEPENDS
code_load_files
Expand Down Expand Up @@ -992,4 +993,5 @@ add_custom_target(erlang_test_modules DEPENDS
test_raw_raise.beam

test_ets.beam
test_node.beam
)
54 changes: 54 additions & 0 deletions tests/erlang_tests/test_node.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
%
% This file is part of AtomVM.
%
% Copyright 2024 Paul Guyot <[email protected]>
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
%

-module(test_node).

-export([start/0]).

start() ->
ok = test_node_no_distribution(),
ok = test_node_distribution(),
0.

test_node_no_distribution() ->
nonode@nohost = node(),
nonode@nohost = erlang:node(),
ok.

test_node_distribution() ->
{NetKernelPid, MonitorRef} = spawn_opt(
fun() ->
receive
quit -> ok
end
end,
[monitor]
),
register(net_kernel, NetKernelPid),
true = erlang:setnode(test@test_node, 42),
test@test_node = node(),
NetKernelPid ! quit,
ok =
receive
{'DOWN', MonitorRef, process, NetKernelPid, normal} -> ok
after 1000 -> timeout
end,
nonode@nohost = node(),
ok.
1 change: 1 addition & 0 deletions tests/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ struct Test tests[] = {
TEST_CASE(test_raw_raise),

TEST_CASE(test_ets),
TEST_CASE(test_node),

// TEST CRASHES HERE: TEST_CASE(memlimit),

Expand Down

0 comments on commit 34d2bec

Please sign in to comment.