From 49eb33e764af838a654fde179bb5cebb8cf666b3 Mon Sep 17 00:00:00 2001 From: Winford Date: Mon, 12 Jun 2023 11:57:55 -0700 Subject: [PATCH] Unregister processes when they die Adds the function `globalcontext_maybe_unregister_process_id` to globalcontext.c to allow removing the appropriate entry from the registered atoms table when the associated context is destroyed. Signed-off-by: Winford --- src/libAtomVM/context.c | 4 +++ src/libAtomVM/globalcontext.c | 17 +++++++++++ src/libAtomVM/globalcontext.h | 11 +++++++ tests/erlang_tests/CMakeLists.txt | 2 ++ tests/erlang_tests/whereis_dead_process.erl | 32 +++++++++++++++++++++ tests/test.c | 1 + 6 files changed, 67 insertions(+) create mode 100644 tests/erlang_tests/whereis_dead_process.erl diff --git a/src/libAtomVM/context.c b/src/libAtomVM/context.c index 0bf8ecac1..6056217f0 100644 --- a/src/libAtomVM/context.c +++ b/src/libAtomVM/context.c @@ -110,6 +110,9 @@ void context_destroy(Context *ctx) // Another process can get an access to our mailbox until this point. synclist_remove(&ctx->global->processes_table, &ctx->processes_table_head); + // Ensure process is not registered + globalcontext_maybe_unregister_process_id(ctx->global, ctx->process_id); + // When monitor message is sent, process is no longer in the table. context_monitors_handle_terminate(ctx); @@ -135,6 +138,7 @@ void context_destroy(Context *ctx) if (ctx->platform_data) { free(ctx->platform_data); } + free(ctx); } diff --git a/src/libAtomVM/globalcontext.c b/src/libAtomVM/globalcontext.c index 144e2f7c8..1e5133a7c 100644 --- a/src/libAtomVM/globalcontext.c +++ b/src/libAtomVM/globalcontext.c @@ -316,6 +316,23 @@ bool globalcontext_unregister_process(GlobalContext *glb, int atom_index) return false; } +bool globalcontext_maybe_unregister_process_id(GlobalContext *glb, int target_process_id) +{ + struct ListHead *registered_processes_list = synclist_wrlock(&glb->registered_processes); + struct ListHead *item; + LIST_FOR_EACH (item, registered_processes_list) { + const struct RegisteredProcess *registered_process = GET_LIST_ENTRY(item, struct RegisteredProcess, registered_processes_list_head); + if (registered_process->local_process_id == target_process_id) { + list_remove(item); + free(item); + synclist_unlock(&glb->registered_processes); + return true; + } + } + synclist_unlock(&glb->registered_processes); + return false; +} + int globalcontext_get_registered_process(GlobalContext *glb, int atom_index) { struct ListHead *registered_processes_list = synclist_rdlock(&glb->registered_processes); diff --git a/src/libAtomVM/globalcontext.h b/src/libAtomVM/globalcontext.h index 5feab0c37..028e5f05c 100644 --- a/src/libAtomVM/globalcontext.h +++ b/src/libAtomVM/globalcontext.h @@ -237,6 +237,17 @@ int globalcontext_get_registered_process(GlobalContext *glb, int atom_index); */ bool globalcontext_unregister_process(GlobalContext *glb, int atom_index); +/** + * @brief Remove entry from registered atoms by process id + * + * @details Unregister a process with a certain process id. This is used when a process dies to ensure + * the process is not registered and remove it from the registered atoms table if it is. + * @param glb the global context, each registered process will be globally available for that context. + * @param process_id the process id of the entry to remove. + * @returns \c true if the process was unregistered, \c false otherwise + */ +bool globalcontext_maybe_unregister_process_id(GlobalContext *glb, int process_id); + /** * @brief equivalent to globalcontext_insert_atom_maybe_copy(glb, atom_string, 0); */ diff --git a/tests/erlang_tests/CMakeLists.txt b/tests/erlang_tests/CMakeLists.txt index fe21a7cb4..76057a8f5 100644 --- a/tests/erlang_tests/CMakeLists.txt +++ b/tests/erlang_tests/CMakeLists.txt @@ -87,6 +87,7 @@ compile_erlang(match) compile_erlang(if_test) compile_erlang(sleep) compile_erlang(hello_world) +compile_erlang(whereis_dead_process) compile_erlang(whereis_fail) compile_erlang(register_unregister) compile_erlang(try_noerror) @@ -518,6 +519,7 @@ add_custom_target(erlang_test_modules DEPENDS if_test.beam sleep.beam hello_world.beam + whereis_dead_process.beam whereis_fail.beam register_unregister.beam try_noerror.beam diff --git a/tests/erlang_tests/whereis_dead_process.erl b/tests/erlang_tests/whereis_dead_process.erl new file mode 100644 index 000000000..7c62a3e96 --- /dev/null +++ b/tests/erlang_tests/whereis_dead_process.erl @@ -0,0 +1,32 @@ +% +% This file is part of AtomVM. +% +% Copyright 2023 Winford (Uncle Grumpy) +% +% 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(whereis_dead_process). + +-export([start/0]). + +start() -> + spawn(fun() -> register(foo, self()) end), + result_to_int(whereis(foo)). + +result_to_int(undefined) -> + 0; +result_to_int(_) -> + 1. diff --git a/tests/test.c b/tests/test.c index 2c5123d65..1b2abfd17 100644 --- a/tests/test.c +++ b/tests/test.c @@ -112,6 +112,7 @@ struct Test tests[] = { TEST_CASE_EXPECTED(match, 5), TEST_CASE_EXPECTED(if_test, 5), TEST_CASE(sleep), + TEST_CASE(whereis_dead_process), TEST_CASE_EXPECTED(whereis_fail, 2), TEST_CASE_EXPECTED(try_noerror, 1), TEST_CASE_EXPECTED(catch_badmatch, 1),