diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index a32186da8a40..5709df04b2eb 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3825,6 +3825,24 @@ BIF_RETTYPE processes_0(BIF_ALIST_0) return erts_ptab_list(BIF_P, &erts_proc); } +/**********************************************************************/ +/* + * The erts_internal:processes_next/1 BIF. + */ + +BIF_RETTYPE erts_internal_processes_next_1(BIF_ALIST_1) +{ + Eterm res; + if (is_not_small(BIF_ARG_1)) { + BIF_ERROR(BIF_P, BADARG); + } + res = erts_ptab_processes_next(BIF_P, &erts_proc, unsigned_val(BIF_ARG_1)); + if (is_non_value(res)) { + BIF_ERROR(BIF_P, BADARG); + } + BIF_RET(res); +} + /**********************************************************************/ /* * The erlang:ports/0 BIF. diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index c7aa2bff1c78..45c59fe9acb0 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -807,3 +807,4 @@ bif erts_trace_cleaner:send_trace_clean_signal/1 # bif erts_internal:system_monitor/1 bif erts_internal:system_monitor/3 +bif erts_internal:processes_next/1 diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c index 37408860918c..2272f6ecfb48 100644 --- a/erts/emulator/beam/erl_ptab.c +++ b/erts/emulator/beam/erl_ptab.c @@ -1465,6 +1465,61 @@ ptab_pix2el(ErtsPTab *ptab, int ix) return ptab_el; } +#define ERTS_PTAB_REDS_MULTIPLIER 25 + +Eterm +erts_ptab_processes_next(Process *c_p, ErtsPTab *ptab, Uint first) +{ + Uint i; + int scanned; + Sint limit; + Uint need; + Eterm res; + Eterm* hp; + Eterm *hp_end; + + int max_pids = MAX(ERTS_BIF_REDS_LEFT(c_p), 1); + int num_pids = 0; + int n = max_pids * ERTS_PTAB_REDS_MULTIPLIER; + + need = n * 2; + hp = HAlloc(c_p, need); /* we need two heap words for each id */ + hp_end = hp + need; + res = make_list(hp); + + limit = MIN(ptab->r.o.max, first+n); + if (first == limit) { + return am_none; + } else if (first > limit) { + return THE_NON_VALUE; + } + + for (i = first; i < limit && num_pids < max_pids; i++) { + ErtsPTabElementCommon *el = ptab_pix2el(ptab, i); + if (el) { + hp[0] = el->id; + hp[1] = make_list(hp+2); + hp += 2; + num_pids++; + } + } + + if (num_pids == 0) { + res = NIL; + } else { + hp[-1] = NIL; + } + + scanned = (i - first) / ERTS_PTAB_REDS_MULTIPLIER + 1; + + res = TUPLE2(hp, make_small(i), res); + HRelease(c_p, hp_end, hp); + + BUMP_REDS(c_p, scanned); + + return res; +} + Eterm erts_debug_ptab_list(Process *c_p, ErtsPTab *ptab) { diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h index 8753ae9365e2..a53475152e2a 100644 --- a/erts/emulator/beam/erl_ptab.h +++ b/erts/emulator/beam/erl_ptab.h @@ -474,6 +474,9 @@ ERTS_GLB_INLINE int erts_lc_ptab_is_rwlocked(ErtsPTab *ptab) BIF_RETTYPE erts_ptab_list(struct process *c_p, ErtsPTab *ptab); +BIF_RETTYPE erts_ptab_processes_next(struct process *c_p, ErtsPTab *ptab, + Uint first); + #endif #if defined(ERTS_PTAB_WANT_DEBUG_FUNCS__) && !defined(ERTS_PTAB_DEBUG_FUNCS__) diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index e632a5ac98b1..d72cc86eb0b3 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -1108,6 +1108,10 @@ error_info(_Config) -> {process_display, [ExternalPid, whatever]}, {process_display, [DeadProcess, backtrace]}, + {processes_next, [{a, []}]}, + {processes_next, [{-1, []}]}, + {processes_next, [a]}, + {process_flag, [trap_exit, some_value]}, {process_flag, [bad_flag, some_value]}, diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 9680eb5836fd..705dbdf30678 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -103,7 +103,8 @@ demonitor_aliasmonitor/1, down_aliasmonitor/1, monitor_tag/1, - no_pid_wrap/1]). + no_pid_wrap/1, + processes_iter/1]). -export([prio_server/2, prio_client/2, init/1, handle_event/2]). @@ -163,7 +164,8 @@ groups() -> processes_small_tab, processes_this_tab, processes_last_call_trap, processes_apply_trap, processes_gc_trap, processes_term_proc_list, - processes_send_infant]}, + processes_send_infant, + processes_iter]}, {process_info_bif, [], [t_process_info, process_info_messages, process_info_other, process_info_other_msg, @@ -2513,7 +2515,14 @@ processes_bif_test() -> processes() end, + IterProcesses = + fun () -> + erts_debug:set_internal_state(reds_left, WantReds), + iter_all_processes() + end, + ok = do_processes_bif_test(WantReds, WillTrap, Processes), + ok = do_processes_bif_test(WantReds, false, IterProcesses()), case WillTrap of false -> @@ -2550,7 +2559,19 @@ processes_bif_test() -> undefined -> ok; Comment -> {comment, Comment} end. - + +iter_all_processes() -> + Iter = erlang:processes_first(), + iter_all_processes(Iter). + +iter_all_processes(Iter0) -> + case erlang:processes_next(Iter0) of + {Pid, Iter} -> + [Pid|iter_all_processes(Iter)]; + none -> + none + end. + do_processes_bif_test(WantReds, DieTest, Processes) -> Tester = self(), SpawnProcesses = fun (Prio) -> @@ -4199,6 +4220,19 @@ processes_term_proc_list(Config) when is_list(Config) -> ok. +processes_iter(Config) when is_list(Config) -> + ProcessLimit = erlang:system_info(process_limit), + {'EXIT',{badarg,_}} = catch erts_internal:processes_next(ProcessLimit + 1), + {'EXIT',{badarg,_}} = catch erts_internal:processes_next(-1), + {'EXIT',{badarg,_}} = catch erts_internal:processes_next(1 bsl 32), + {'EXIT',{badarg,_}} = catch erts_internal:processes_next(1 bsl 64), + {'EXIT',{badarg,_}} = catch erts_internal:processes_next(abc), + + none = erts_internal:processes_next(ProcessLimit), + + ok. + + %% OTP-18322: Send msg to spawning process pid returned from processes/0 processes_send_infant(_Config) -> case erlang:system_info(schedulers_online) of diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index aed9bed68383..8548c3b7a7f0 100644 Binary files a/erts/preloaded/ebin/erlang.beam and b/erts/preloaded/ebin/erlang.beam differ diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 928de46229df..d27c9c9a20ce 100644 Binary files a/erts/preloaded/ebin/erts_internal.beam and b/erts/preloaded/ebin/erts_internal.beam differ diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 3c950c0a2f53..4fdef2a44769 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -265,6 +265,7 @@ A timeout value that can be passed to a -export_type([message_queue_data/0]). -export_type([monitor_option/0]). -export_type([stacktrace/0]). +-export_type([processes_iter_ref/0]). -type stacktrace_extrainfo() :: {line, pos_integer()} | @@ -465,6 +466,7 @@ A list of binaries. This datatype is useful to use together with -export([time_offset/0, time_offset/1, timestamp/0]). -export([process_display/2]). -export([process_flag/3, process_info/1, processes/0, purge_module/1]). +-export([processes_first/0, processes_next/1]). -export([put/2, raise/3, read_timer/1, read_timer/2, ref_to_list/1, register/2]). -export([send_after/3, send_after/4, start_timer/3, start_timer/4]). -export([registered/0, resume_process/1, round/1, self/0]). @@ -5219,6 +5221,64 @@ Example: processes() -> erlang:nif_error(undefined). +%% The process iterator is a 2-tuple, consisting of an index to the process +%% table and a list of process identifiers that existed when the last scan of +%% the process table took place. The index is the starting place for the next +%% scan of the process table. +-opaque processes_iter_ref() :: {integer(), [pid()]}. + +%% processes_first/0 +-doc """ +Returns a processes iterator that can be used in +[`processes_next/1`](`processes_next/1`). +""". +-doc #{ group => processes }. +-spec processes_first() -> processes_iter_ref(). +processes_first() -> + {0, []}. + +%% processes_next/1 +-doc """ +Returns a 2-tuple, consisting of one process identifier and a new processes +iterator. If the process iterator has run out of processes in the process table, +`none` will be returned. + +Example: + +```erlang +> I0 = erlang:processes_first(), ok. +ok +> {Pid1, I1} = erlang:processes_next(I0), Pid1. +<0.0.0>, +> {Pid2, I2} = erlang:processes_next(I1), Pid2. +<0.1.0> +``` + +> #### Note {: .info } +> +> This BIF has less consistency guarantee than [`processes/0`](`processes/0`). +> Process identifiers returned from consecutive calls of this BIF may not be a +> consistent snapshot of all elements existing in the table during any of the +> calls. +""". +-doc #{ group => processes }. +-spec processes_next(Iter) -> {Pid, NewIter} | 'none' when + Iter :: processes_iter_ref(), + NewIter :: processes_iter_ref(), + Pid :: pid(). +processes_next({IterRef, [Pid|Pids]}) -> + {Pid, {IterRef, Pids}}; +processes_next({IterRef0, []}=Arg) -> + try erts_internal:processes_next(IterRef0) of + none -> none; + {IterRef, [Pid|Pids]} -> {Pid, {IterRef, Pids}}; + {IterRef, []} -> processes_next({IterRef, []}) + catch error:badarg -> + badarg_with_info([Arg]) + end; +processes_next(Arg) -> + badarg_with_info([Arg]). + %% purge_module/1 -doc """ Removes old code for `Module`. Before this BIF is used, `check_process_code/2` diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index e0aaf5ee6af8..07a3c4b5ccd4 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -129,6 +129,8 @@ -export([system_monitor/1, system_monitor/3]). +-export([processes_next/1]). + %% %% Await result of send to port %% @@ -1166,3 +1168,7 @@ system_monitor(_Session) -> Return :: undefined | ok | {pid(), Options}. system_monitor(_Session, _MonitorPid, _Options) -> erlang:nif_error(undefined). + +-spec processes_next(integer()) -> {integer(), [pid()]} | 'none'. +processes_next(_IterRef) -> + erlang:nif_error(undefined). diff --git a/lib/kernel/src/erl_erts_errors.erl b/lib/kernel/src/erl_erts_errors.erl index 469907dac3df..33ac547d9179 100644 --- a/lib/kernel/src/erl_erts_errors.erl +++ b/lib/kernel/src/erl_erts_errors.erl @@ -733,6 +733,8 @@ format_erlang_error(process_display, [Pid,_], Cause) -> _ -> [must_be_local_pid(Pid, dead_process)] end; +format_erlang_error(processes_next, [_], _Cause) -> + [~"invalid processes iterator"]; format_erlang_error(process_flag, [_,_], Cause) -> case Cause of badopt ->