Dirkjan Bussink <
d.bu...@gmail.com> wrote:
> No, there isn't something similar. The overhead of this would actually
> be non trivial in Rubinius, since we have actual concurrent threads,
> so an implementation similar to MRI will suffer from all kinds of race
> conditions in Rubinius. There is no guarantee whatsoever, that when
> reading the locked variable, it will still be there then when actually
> reading / writing something. So the only solution is to actually lock
> the entire string for every modifying operation.
>
> This would also be necessary for every operation that happens when the
> string is not "locked", so it would mean a general performance impact
> for all string operations. This is necessary so no locking happens
> while another thread is modifying something, a problem MRI does not
> have because of the GIL. We don't want to add synchronization across
> every string operation, so there is nothing that works like this and
> probably never will be.
Good to know, I hate unnecessary synchronization :)
Just curious, will using shared strings improperly raise a Ruby
exception, or will it just segfault the VM? I wouldn't even mind the
latter.
> How is the string shared for this problem? If it's some output string
> you share, the only way to do it is to lock it. In Rubinius you can
> actually use every object as a lock, much like in Java:
AFAIK, MRI only uses rb_str_locktmp/rb_str_unlocktmp to detect bugs
where strings are accidentally shared.
MRI tests a per-string bit and raises immediately if the string is
already locked by another thread. There's no waiting at all. Since
this bit is guarded by the GVL, no additional memory barrier is needed
in MRI.
If Rubinius were to implement it; it'd still need the per-string bit
flag and use atomic test/set operations. No waiting, but there'll
need to be memory barriers at least.
> Eric Wong wrote:
> > If not, I will probably hash the VALUE to a fixed set of mutexes (maybe
> > #locks will be scaled to processor count) and use pthread_mutex_trylock
> > to emulate that behavior...
On second thought, that approach would not work as-is, it would generate
false positives without a per-string bit.
Maybe something like this:
# will raise if lock bit is already set
Rubinius.synchronize(string) { string.set_lock_bit! }
io.read(8192, string)
# will raise if lock bit was not set
Rubinius.synchronize(string) { string.clear_lock_bit! }