views:

45

answers:

2

Question

Is it safe for multiple threads to fetch and store simple, individual values in a shared hash without lock()ing the hash?

Can you prove it or cite strong authority?

Background

My belief was that at worst unlocked hash manipulations could lead to segfaults.

However, I've very recently seen code that coordinates worker threads' activity in such a manner, which the author believes to be safe. The code in question performs only simple fetches and stores:

  • The shared hash is a plain, shared hash (not a user-defined, tie()d construct)
  • The values are simple scalars, not references and not themselves shared
  • Each key/value pair is uniquely stored and modified by one and only one thread
  • All key/value pairs may be fetched by any thread
  • No thread iterates through the shared hash (no each(), no keys() nor values() looping)

Code Excerpt

my %status : shared;

for my $id (1 .. $n) {
  threads->create(\&thread_routine);
}

sub thread_routine {
  my $me = threads->tid();

  $status{ $me } = 'Getting ready';
  ... do something ...
  $status{ $me } = 'Thinking';
  ... do something else ...

  $status{ $me } = 'Looking around';
  for my $tid (threads->list) {
    next if $tid == $me;
    if ($status{ $tid } eq "Thinking") { ... react ... }
    ...
  }

  $status{ $me } = 'All done';
}
+1  A: 

It's my understanding that whatever locking is necessary to prevent corruption to the internal structure of the hash is handled automatically. The lock function is to allow threads to coordinate so that the higher-level consistency of the hash can be maintained. Since in this case each key is modified by only one thread, no explicit locking is necessary.

I can't quote an authority, except that I can't find anything in the threads::shared documentation that suggests you need to be careful to lock a variable before modifying it. If doing that could cause Perl to crash, you'd think it would be worth a mention.

cjm
+1  A: 

IMHO it'll work OK.

threads module docs just mention not to use threads in the END blocks, and do not mention mutexes at all anywhere else besides that. If mutexes were necessary they would for sure be included in the threads module - but they aren't, are they?

my %hash : shared;

map {
        async { map { $hash{$_}++; } (1 .. 30); }
} (1 .. 300);

map {$_->join} (threads->list);

print Dumper \%hash;

Each time I run that a get value 300 in each key. So far I couldn't find a better way to prove it's safe.

hlynur
Mutexes are included, in as much as all `shared` variables carry what Java programmers might call an "intrinsic" or "monitor" lock.
pilcrow
Which means it'll work. I've also found that there is **lock** method exported by threads::shared module, which allows to manually create locks.
hlynur