How to prevent race condition in Moose with Lazy attributes and Coro

368 views Asked by At

The project we are working on makes use of the Coro for asynchronous processing and unfortunately it is too large to move away from Coro in the near future. We are running into a race condition where an object with a lazy attribute calls the builder for that lazy attribute inside the builder the thread cedes for some reason and then a different coro thread attempts to access the same attribute triggering the attribute to be built again.

Normally I would protect the check and then set code with a semaphore however the check and set behaviour of Moose is inside of moose rather then my own code.

How can I remove this race condition?

1

There are 1 answers

2
ikegami On BEST ANSWER

Normally, you'd control access to the object or attribute from the outside.

my $foo_bar_lock = Coro::Semaphore->new();
my $foo = Foo->new();

{
   my $guard = $foo_bar_lock->guard;
   # ... use $foo's bar ...
}

{
   my $guard = $foo_bar_lock->guard;
   # ... use $foo's bar ...
}

But, it can be done from the inside too.

has bar_lock => (
   reader  => '_get_bar_lock',
   default => sub { Coro::Semaphore->new() },
);

has bar => (
   reader  => '_get_bar',
   writer  => '_set_bar',
   builder => '_build_bar',
   lazy    => 1,
);

sub _build_bar { ... }

sub get_bar { 
   my $self = shift;
   my $guard = $self->_get_bar_lock->guard;
   return $self->_get_bar();
} 

sub set_bar { 
   my $self = shift;
   my $guard = $self->_get_bar_lock->guard;
   return $self->_set_bar(@_);
} 

(If you prefer a single get-set accessor instead of separate get and set accessors, use accessor instead of reader and writer.)