diff options
-rw-r--r-- | erts/emulator/beam/erl_db_util.c | 41 | ||||
-rw-r--r-- | lib/stdlib/doc/src/ets.xml | 36 | ||||
-rw-r--r-- | lib/stdlib/test/ets_SUITE.erl | 20 |
3 files changed, 74 insertions, 23 deletions
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 91ac53ad0b..eaed15b14b 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1125,12 +1125,11 @@ error: * Returns true if 'b' is guaranteed to always construct * the same term as 'a' has matched. */ -static int db_match_eq_body(Eterm a, Eterm b) +static int db_match_eq_body(Eterm a, Eterm b, int const_mode) { DECLARE_ESTACK(s); Uint arity; Eterm *ap, *bp; - int const_mode = 0; const Eterm CONST_MODE_OFF = THE_NON_VALUE; while (1) { @@ -1224,6 +1223,7 @@ int db_match_keeps_key(int keypos, Eterm match, Eterm guard, Eterm body) Eterm single_body_subterm; Eterm single_body_subterm_key; Eterm* single_body_subterm_key_tpl; + int const_mode; if (!is_list(body)) { return 0; @@ -1244,42 +1244,53 @@ int db_match_keeps_key(int keypos, Eterm match, Eterm guard, Eterm body) return 0; } - single_body_term_tpl = tuple_val(single_body_term); - if (arityval(*single_body_term_tpl) != 1) { - // not the 1-element tuple we're expecting - return 0; - } - match_key = db_getkey(keypos, match); if (!is_value(match_key)) { // can't get key out of match return 0; } - single_body_subterm = single_body_term_tpl[1]; + single_body_term_tpl = tuple_val(single_body_term); + if (single_body_term_tpl[0] == make_arityval(2) && + single_body_term_tpl[1] == am_const) { + /* {const, {"ets-tuple constant"}} */ + single_body_subterm = single_body_term_tpl[2]; + const_mode = 1; + } + else if (*single_body_term_tpl == make_arityval(1)) { + /* {{"ets-tuple construction"}} */ + single_body_subterm = single_body_term_tpl[1]; + const_mode = 0; + } + else { + /* not a tuple construction */ + return 0; + } + single_body_subterm_key = db_getkey(keypos, single_body_subterm); if (!is_value(single_body_subterm_key)) { // can't get key out of single body subterm return 0; } - if (db_match_eq_body(match_key, single_body_subterm_key)) { + if (db_match_eq_body(match_key, single_body_subterm_key, const_mode)) { /* tuple with same key is returned */ return 1; } - if (!is_tuple(single_body_subterm_key)) { - /* can't possibly be an element instruction */ + if (const_mode) { + /* constant key did not match */ return 0; } - single_body_subterm_key_tpl = tuple_val(single_body_subterm_key); - if (arityval(*single_body_subterm_key_tpl) != 3) { + if (!is_tuple(single_body_subterm_key)) { /* can't possibly be an element instruction */ return 0; } - if (single_body_subterm_key_tpl[1] == am_element && + single_body_subterm_key_tpl = tuple_val(single_body_subterm_key); + if (single_body_subterm_key_tpl[0] == make_arityval(3) && + single_body_subterm_key_tpl[1] == am_element && single_body_subterm_key_tpl[3] == am_DollarUnderscore && single_body_subterm_key_tpl[2] == make_small(keypos)) { diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index 0d1e5c6e3a..342be80f98 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -1495,17 +1495,37 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> <fsummary>Match and replace objects atomically in an ETS table</fsummary> <desc> <p>Matches the objects in the table <c><anno>Tab</anno></c> using a - <seealso marker="#match_spec">match specification</seealso>. If - an object is matched, the existing object is replaced with - the match specification result, which <em>must</em> retain - the original key or the operation will fail with <c>badarg</c>.</p> + <seealso marker="#match_spec">match specification</seealso>. For each + matched object, the existing object is replaced with + the match specification result.</p> + <p>The match-and-replace operation for each individual object is guaranteed to be + <seealso marker="#concurrency">atomic and isolated</seealso>. The + <c>select_replace</c> table iteration as a whole, like all other select functions, + does not give such guarantees.</p> + <p>The match specifiction must be guaranteed to <em>retain the key</em> + of any matched object. If not, <c>select_replace</c> will fail with <c>badarg</c> + without updating any objects.</p> <p>For the moment, due to performance and semantic constraints, tables of type <c>bag</c> are not yet supported.</p> <p>The function returns the total number of replaced objects.</p> - <note> - <p>The match/replacement operation atomicity scope is limited - to each individual object.</p> - </note> + <p><em>Example</em></p> + <p>For all 2-tuples with a list in second position, add atom <c>'marker'</c> first in the list:</p> + <pre> +1> <input>T = ets:new(x,[]), ets:insert(T, {key, [1, 2, 3]}).</input> +true +2> <input>MS = ets:fun2ms(fun({K, L}) when is_list(L) -> {K, [marker | L]} end).</input> +[{{'$1','$2'},[{is_list,'$2'}],[{{'$1',[marker|'$2']}}]}] +3> <input>ets:select_replace(T, MS).</input> +1 +4> <input>ets:tab2list(T).</input> +[{key,[marker,1,2,3]}] + </pre> + <p>A generic single object compare-and-swap operation:</p> + <pre> +[Old] = ets:lookup(T, Key), +New = update_object(Old), +Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])), + </pre> </desc> </func> diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 4c2f6cfe9e..05451a83fb 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -1349,6 +1349,26 @@ t_select_replace(Config) when is_list(Config) -> NeqPairs(X,Y)) || X <- Terms, Y <- Terms], + %% Wrap entire tuple with 'const' + [[begin + Old = {Key, 1, 2}, + ets:insert(T2, Old), + 1 = ets:select_replace(T2, [{Old, [], [{const, New}]}]), + [New] = ets:lookup(T2, Key), + ets:delete(T2, Key) + end || New <- [{Key, 1, 2}, {Key, 3, 4}, {Key, 1}, {Key, 1, 2, 3}, {Key}] + ] + || Key <- [{1, tuple}, {nested, {tuple, {a,b}}} | Terms]], + + %% 'const' wrap does not work with maps or variables in keys + [[begin + Old = {Key, 1, 2}, + {'EXIT',{badarg,_}} = (catch ets:select_replace(T2, [{Old, [], [{const, New}]}])) + end || New <- [{Key, 1, 2}, {Key, 3, 4}, {Key, 1}, {Key, 1, 2, 3}, {Key}] + ] + || Key <- [#{a => 1}, {nested, #{a => 1}}, '$1']], + + ets:delete(T2), verify_etsmem(EtsMem). |