Sven Suska <sven715rt / suska.org> writes:

> Sven Suska schrieb:
>
> If it were just a normal hash, we would have to lock the object manually:
>

Start locking from this point, before you do a check.

>  if users.has_key(new_nickname)
>    raise "Nickname already exists"
>  end
>  # hash could be modified here, if not locked!!!
>  # if another thread would have stored a user with the same nickname
>  # just now, then this data would be overwritten in the next
>  statement.

If you start locking at this point, then in between the check and the
insertion another check and insertion could have occurred and the
following insertion effectively undoes that insertion.

>  users[new_nickname] = user_data


> If "users" were in this "partially_frozen" state, then we could write:
>
>  begin
>    users[new_nickname] = user_data
>  rescue CantChangeExistingDataError
>    raise "Nickname already exists"
>  end

For Hash in official ruby implementation, since #= is implemented in C
without any yielding, #= is effectively an atomic operation. But since
you want to throw an exception if there is already existing entry, you'd
still need a lock to do the check-and-insert atomically.

In another thread, Ryan Davis brought up purely functional data
structure. However, that won't lift the necessity of locking in your
example case because the users list would have to be a global, shared
resource and you are trying to do something akin to the P() operation
of sempahore. Unless Hash already have an atomic check-before-set
operation, you can't get around it the need of having a lock. [And no,
Hash has no such thing yet. Set#add? is similar to what you're looking
for but it's not atomic].

YS.