[erlang-questions] Unexpected try/catch behaviour
Richard O'Keefe
ok@REDACTED
Wed Feb 24 01:25:33 CET 2010
On Feb 24, 2010, at 1:05 PM, Per Melin wrote:
> How about when you need to catch a specific event?
>
> try ets:slot(Table, SlotNumber) of
> '$end_of_table' ->
> ok;
> Items ->
> do_stuff(Items)
> catch
> error:badarg ->
> % We get badarg when the slot number was higher than the total
> % number of slots. This can happen if someone deleted items
> from
> % the table while we were traversing it. No worries.
> ok
> end.
That can be written just as well as
case try ets:slot(Table, Slot_Number)
catch error:badarg -> '$end_of_table'
end
of '$end_of_table' -> ok
; Items -> do_stuff(Items)
end
in which not only will badargs inside do_stuff not be caught,
but the call to do_stuff is visibly *outside* the try, so it
is _obvious_ that they won't be caught.
The problem here is the definition of ets:slot/2:
"Returns all objects in the I:th slot of the table Tab.
A table can be traversed by repeatedly calling the
function, starting with the first slot I = 0 and
ending when '$end_of_table' is returned. The
function will fail with reason badarg if the I
argument is out of range."
A better definition would be to say that
The function will fail with reason badarg if I is
not a non-negative integer. Sufficiently large
values of I result in '$end_of_table' returns.
With that interface, the whole example would just be
case ets:slot(Table, Slot_Number)
of '$end_of_table' -> ok
; Items -> do_stuff(Items)
end
In erl_db_tree.c, for example, I _think_
if (is_not_small(slot_term) ||
((slot = signed_val(slot_term)) < 0) ||
(slot > tb->common.nitems))
return DB_ERROR_BADPARAM;
if (slot == tb->common.nitems) {
*ret = am_EOT;
return DB_ERROR_NONE;
}
should change to
if (is_not_small(slot_term) ||
((slot = signed_val(slot_term)) < 0))
return DB_ERROR_BADPARAM;
if (slot >= tb_common.nitems) {
*ret = am_EOT;
return DB_ERROR_NONE;
}
In erl_db_hash.c, I _think_
if (is_not_small(slot_term) ||
((slot = signed_val(slot_term)) < 0) ||
(slot > tb->nactive))
return DB_ERROR_BADPARAM;
if (slot == tb->nactive) {
*ret = am_EOT;
return DB_ERROR_NONE;
}
should change to
if (is_not_small(slot_term) ||
((slot = signed_val(slot_term)) < 0))
return DB_ERROR_BADPARAM;
if (slot >= tb->nactive) {
*ret = am_EOT;
return DB_ERROR_NONE;
}
The reason for this change is the one you give, that someone
else might have been changing the table. If that someone else
adds stuff to the table so that it grows, nothing bad happens,
but if they delete, and the tree or hash table shrinks, you
are really out of luck.
More information about the erlang-questions
mailing list