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?
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
.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With