-
Notifications
You must be signed in to change notification settings - Fork 124
Implemented erlang:bump_reductions nif. #1813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -131,7 +131,8 @@ | |
dist_ctrl_get_data/1, | ||
dist_ctrl_put_data/2, | ||
unique_integer/0, | ||
unique_integer/1 | ||
unique_integer/1, | ||
bump_reductions/1 | ||
]). | ||
|
||
-export_type([ | ||
|
@@ -272,7 +273,8 @@ send_after(Time, Dest, Msg) -> | |
(Pid :: pid(), message_queue_len) -> {message_queue_len, non_neg_integer()}; | ||
(Pid :: pid(), memory) -> {memory, non_neg_integer()}; | ||
(Pid :: pid(), links) -> {links, [pid()]}; | ||
(Pid :: pid(), monitored_by) -> {monitored_by, [pid() | resource() | port()]}. | ||
(Pid :: pid(), monitored_by) -> {monitored_by, [pid() | resource() | port()]}; | ||
(Pid :: pid(), reductions) -> {reductions, [pos_integer()]}. | ||
process_info(_Pid, _Key) -> | ||
erlang:nif_error(undefined). | ||
|
||
|
@@ -1529,3 +1531,15 @@ unique_integer(_Options) -> | |
-spec nif_error(Reason :: any()) -> no_return(). | ||
nif_error(_Reason) -> | ||
erlang:nif_error(undefined). | ||
|
||
%%----------------------------------------------------------------------------- | ||
%% @param Reductions an integer representing a value of which the reduction counter | ||
%% will be incremented by. | ||
%% @returns true | ||
%% @doc Increments the reduction counter for the calling process, a context switch is | ||
%% forced when the counter reaches the maximum number of reductions for a process. | ||
%% @end | ||
%%----------------------------------------------------------------------------- | ||
-spec bump_reductions(pos_integer()) -> true. | ||
bump_reductions(Reductions) when is_integer(Reductions), Reductions > 1 -> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Erlang/OTP seems to allow There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes - I will look into it. |
||
erlang:nif_error(undefined). |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,7 @@ X(PORT_COUNT_ATOM, "\xA", "port_count") | |
X(ATOM_COUNT_ATOM, "\xA", "atom_count") | ||
X(SYSTEM_ARCHITECTURE_ATOM, "\x13", "system_architecture") | ||
X(WORDSIZE_ATOM, "\x8", "wordsize") | ||
X(REDUCTIONS_ATOM, "\xA", "reductions") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Usually we add new atoms at the end, but I'm not sure we really have a convention here, adding in the middle reduces merge conflicts. However, the jit compiler depends on some values and it may break it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think if it reduces merge conflicts I would leave it as it is. |
||
|
||
X(DECIMALS_ATOM, "\x8", "decimals") | ||
X(SCIENTIFIC_ATOM, "\xA", "scientific") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -202,6 +202,7 @@ static term nif_unicode_characters_to_list(Context *ctx, int argc, term argv[]); | |
static term nif_unicode_characters_to_binary(Context *ctx, int argc, term argv[]); | ||
static term nif_erlang_lists_subtract(Context *ctx, int argc, term argv[]); | ||
static term nif_zlib_compress_1(Context *ctx, int argc, term argv[]); | ||
static term nif_erlang_bump_reductions_1(Context *ctx, int argc, term argv[]); | ||
|
||
#define DECLARE_MATH_NIF_FUN(moniker) \ | ||
static term nif_math_##moniker(Context *ctx, int argc, term argv[]); | ||
|
@@ -875,6 +876,11 @@ static const struct Nif zlib_compress_nif = | |
}; | ||
|
||
|
||
static const struct Nif erlang_bump_reductions_nif = { | ||
.base.type = NIFFunctionType, | ||
.nif_ptr = nif_erlang_bump_reductions_1 | ||
}; | ||
|
||
#define DEFINE_MATH_NIF(moniker) \ | ||
static const struct Nif math_##moniker##_nif = \ | ||
{ \ | ||
|
@@ -5920,6 +5926,15 @@ static term nif_zlib_compress_1(Context *ctx, int argc, term argv[]) | |
} | ||
#endif | ||
|
||
static term nif_erlang_bump_reductions_1(Context *ctx, int argc, term argv[]) | ||
{ | ||
UNUSED(argc); | ||
VALIDATE_VALUE(argv[0], term_is_integer); | ||
int64_t reductions_to_bump = term_to_int(argv[0]) - 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why we do -1 here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think its because bump_reductions is creating 1 reduction by itself just by being called. |
||
ctx->reductions += reductions_to_bump; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should ensure we don't overflow, whatever the width of this field is. |
||
return TRUE_ATOM; | ||
} | ||
|
||
// | ||
// MAINTENANCE NOTE: Exception handling for fp operations using math | ||
// error handling is designed to be thread-safe, as errors are specified | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1767,7 +1767,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
Module *prev_mod; | ||
term *x_regs; | ||
const uint8_t *pc; | ||
int remaining_reductions; | ||
uint64_t max_reductions; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On 32 bits platforms it's going to be a problem. Why this change? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, will be reverted to int. |
||
|
||
Context *ctx = scheduler_run(glb); | ||
|
||
|
@@ -1780,7 +1780,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
code = mod->code->code; | ||
x_regs = ctx->x; | ||
pc = (ctx->saved_ip); | ||
remaining_reductions = DEFAULT_REDUCTIONS_AMOUNT; | ||
max_reductions = ctx->reductions + DEFAULT_REDUCTIONS_AMOUNT; | ||
|
||
#pragma GCC diagnostic push | ||
#pragma GCC diagnostic ignored "-Wpedantic" | ||
|
@@ -1864,8 +1864,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
#ifdef IMPL_EXECUTE_LOOP | ||
ctx->cp = module_address(mod->module_index, pc - code); | ||
|
||
remaining_reductions--; | ||
if (LIKELY(remaining_reductions)) { | ||
++ctx->reductions; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is going to slow us down as opposed to keeping the value in a local variable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Local variable from opcodesswitch is unobtainable in nif. Also every context should have its own reductions counter if we want the reductions to work exactly like the OTP version. |
||
if (LIKELY(ctx->reductions < max_reductions)) { | ||
TRACE_CALL(ctx, mod, "call", label, arity); | ||
JUMP_TO_ADDRESS(mod->labels[label]); | ||
} else { | ||
|
@@ -1895,8 +1895,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
|
||
DEBUG_DUMP_STACK(ctx); | ||
|
||
remaining_reductions--; | ||
if (LIKELY(remaining_reductions)) { | ||
++ctx->reductions; | ||
if (LIKELY(ctx->reductions < max_reductions)) { | ||
TRACE_CALL(ctx, mod, "call_last", label, arity); | ||
JUMP_TO_ADDRESS(mod->labels[label]); | ||
} else { | ||
|
@@ -1917,8 +1917,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
USED_BY_TRACE(label); | ||
|
||
#ifdef IMPL_EXECUTE_LOOP | ||
remaining_reductions--; | ||
if (LIKELY(remaining_reductions)) { | ||
++ctx->reductions; | ||
if (LIKELY(ctx->reductions < max_reductions)) { | ||
TRACE_CALL(ctx, mod, "call_only", label, arity); | ||
JUMP_TO_ADDRESS(mod->labels[label]); | ||
} else { | ||
|
@@ -1933,8 +1933,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
// save pc in case of error | ||
const uint8_t *orig_pc = pc - 1; | ||
|
||
remaining_reductions--; | ||
if (UNLIKELY(!remaining_reductions)) { | ||
++ctx->reductions; | ||
if (UNLIKELY(ctx->reductions >= max_reductions)) { | ||
SCHEDULE_NEXT(mod, orig_pc); | ||
} | ||
#endif | ||
|
@@ -2041,8 +2041,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
// save pc in case of error | ||
const uint8_t *orig_pc = pc - 1; | ||
|
||
remaining_reductions--; | ||
if (UNLIKELY(!remaining_reductions)) { | ||
++ctx->reductions; | ||
if (UNLIKELY(ctx->reductions >= max_reductions)) { | ||
SCHEDULE_NEXT(mod, orig_pc); | ||
} | ||
#endif | ||
|
@@ -3240,8 +3240,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
USED_BY_TRACE(label); | ||
|
||
#ifdef IMPL_EXECUTE_LOOP | ||
remaining_reductions--; | ||
if (LIKELY(remaining_reductions)) { | ||
++ctx->reductions; | ||
if (LIKELY(ctx->reductions < max_reductions)) { | ||
JUMP_TO_ADDRESS(mod->labels[label]); | ||
} else { | ||
SCHEDULE_NEXT(mod, mod->labels[label]); | ||
|
@@ -3477,8 +3477,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
|
||
case OP_CALL_FUN: { | ||
#ifdef IMPL_EXECUTE_LOOP | ||
remaining_reductions--; | ||
if (UNLIKELY(!remaining_reductions)) { | ||
++ctx->reductions; | ||
if (UNLIKELY(ctx->reductions >= max_reductions)) { | ||
SCHEDULE_NEXT(mod, pc - 1); | ||
} | ||
#endif | ||
|
@@ -3533,8 +3533,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
|
||
case OP_CALL_EXT_ONLY: { | ||
#ifdef IMPL_EXECUTE_LOOP | ||
remaining_reductions--; | ||
if (UNLIKELY(!remaining_reductions)) { | ||
++ctx->reductions; | ||
if (UNLIKELY(ctx->reductions >= max_reductions)) { | ||
SCHEDULE_NEXT(mod, pc - 1); | ||
} | ||
#endif | ||
|
@@ -5233,8 +5233,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
// save pc in case of error | ||
const uint8_t *orig_pc = pc - 1; | ||
|
||
remaining_reductions--; | ||
if (UNLIKELY(!remaining_reductions)) { | ||
++ctx->reductions; | ||
if (UNLIKELY(ctx->reductions >= max_reductions)) { | ||
SCHEDULE_NEXT(mod, orig_pc); | ||
} | ||
#endif | ||
|
@@ -5286,8 +5286,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
|
||
case OP_APPLY_LAST: { | ||
#ifdef IMPL_EXECUTE_LOOP | ||
remaining_reductions--; | ||
if (UNLIKELY(!remaining_reductions)) { | ||
++ctx->reductions; | ||
if (UNLIKELY(ctx->reductions >= max_reductions)) { | ||
SCHEDULE_NEXT(mod, pc - 1); | ||
} | ||
#endif | ||
|
@@ -6793,8 +6793,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) | |
|
||
case OP_CALL_FUN2: { | ||
#ifdef IMPL_EXECUTE_LOOP | ||
remaining_reductions--; | ||
if (UNLIKELY(!remaining_reductions)) { | ||
++ctx->reductions; | ||
if (UNLIKELY(ctx->reductions >= max_reductions)) { | ||
SCHEDULE_NEXT(mod, pc - 1); | ||
} | ||
#endif | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
% | ||
% This file is part of AtomVM. | ||
% | ||
% Copyright 2025 Franciszek Kubis <[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_bump_reductions). | ||
|
||
-export([start/0, process_test/0]). | ||
|
||
start() -> | ||
{reductions, 1} = erlang:process_info(self(), reductions), | ||
erlang:bump_reductions(500), | ||
{reductions, 502} = erlang:process_info(self(), reductions), | ||
Pid = erlang:spawn_opt(fun() -> process_test() end, [link]), | ||
Pid ! {ready, self()}, | ||
receive | ||
{r1, {reductions, Reductions}} -> | ||
2 = Reductions | ||
end, | ||
receive | ||
{r2, {reductions, Reductions2}} -> | ||
1029 = Reductions2 | ||
end, | ||
erlang:bump_reductions(2000), | ||
{reductions, 2505} = erlang:process_info(self(), reductions), | ||
0. | ||
|
||
process_test() -> | ||
receive | ||
{ready, Pid} -> | ||
Pid ! {r1, erlang:process_info(self(), reductions)}, | ||
erlang:bump_reductions(1025), | ||
Pid ! {r2, erlang:process_info(self(), reductions)} | ||
end. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure about the spec? Reading your code it seems the function returns
{reductions, non_neg_integer()}
, following Erlang/OTP, instead of{reductions, [pos_integer()]}
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I guess there should be non_neg_integer() just like in the OTP.