Skip to content

Commit

Permalink
Merge pull request #4917 from vrurg/problem-solving-323
Browse files Browse the repository at this point in the history
Resolve race conditions when working with symbols and repositories
  • Loading branch information
vrurg committed May 19, 2022
2 parents aaa5615 + 2c6bccb commit 6bd19e4
Show file tree
Hide file tree
Showing 22 changed files with 717 additions and 244 deletions.
15 changes: 12 additions & 3 deletions src/Perl6/Actions.nqp
Expand Up @@ -2300,9 +2300,18 @@ class Perl6::Actions is HLL::Actions does STDActions {
my $lexpad := $world.cur_lexpad();
my $block := $lexpad.ann('code_object');
$block := $world.blocks[+$world.blocks - 2] if $block.HOW.name($block) eq 'Code';
if !$lexpad.symbol('%REQUIRE_SYMBOLS') {
declare_variable($/, $past, '%', '', 'REQUIRE_SYMBOLS', []);
$world.mark_lexical_used_implicitly($lexpad, '%REQUIRE_SYMBOLS');
my $req-sym-name := '%?REQUIRE-SYMBOLS';
if !$lexpad.symbol($req-sym-name) {
my $Stash := $world.find_single_symbol_in_setting('Stash');
my $st := $Stash.new;
$world.add_object_if_no_sc($st);
$lexpad[0].push(
QAST::Op.new(
:op<bind>,
QAST::Var.new(:name($req-sym-name), :scope<lexical>, :decl<static>, :value($st)),
QAST::Op.new(:op<callmethod>, :name<new>, QAST::WVal.new(:value($Stash)))));
$lexpad.symbol($req-sym-name, :scope<lexical>, :value($st));
$world.mark_lexical_used_implicitly($lexpad, $req-sym-name);
}
my $require_past := WANTED(QAST::Op.new(:node($/), :op<call>,
:name<&REQUIRE_IMPORT>,
Expand Down
7 changes: 6 additions & 1 deletion src/Perl6/ModuleLoader.nqp
Expand Up @@ -129,7 +129,12 @@ class Perl6::ModuleLoader does Perl6::ModuleLoaderVMConfig {
$name eq $stub_how_name || $name eq $nqp_stub_how_name
}
method merge_globals($target, $source) {
if stash_hash($source) -> %source {
my $metamodel-configuration := nqp::gethllsym('Raku', 'METAMODEL_CONFIGURATION');
if !nqp::isnull($metamodel-configuration) && nqp::istype($target, $metamodel-configuration.stash_type()) {
# merge-symbols will loop back on this method again, but would lock-protect itself first.
$target.merge-symbols($source);
}
elsif stash_hash($source) -> %source {
# Start off merging top-level symbols. Easy when there's no
# overlap. Otherwise, we need to recurse.
my %known_symbols;
Expand Down
36 changes: 15 additions & 21 deletions src/core.c/CompUnit/PrecompilationRepository.pm6
Expand Up @@ -33,7 +33,12 @@ class CompUnit::PrecompilationRepository::Default

my $loaded := nqp::hash;
my $resolved := nqp::hash;
#?if moar
my $loaded-lock := Lock::Soft.new;
#?endif
#?if !moar
my $loaded-lock := Lock.new;
#?endif
my $first-repo-id;

my constant $compiler-id =
Expand All @@ -50,10 +55,7 @@ class CompUnit::PrecompilationRepository::Default
$!RMD("try-load source at $source") if $!RMD;

# Even if we may no longer precompile, we should use already loaded files
$loaded-lock.protect: {
my \precomped := nqp::atkey($loaded,$id.Str);
return precomped if precomped;
}
return $_ if $_ := $loaded-lock.protect: { nqp::atkey($loaded,$id.Str) };

my ($handle, $checksum) = (
self.may-precomp and (
Expand Down Expand Up @@ -175,25 +177,20 @@ Need to re-check dependencies.")
if $!RMD;

if $resolve {
$loaded-lock.protect: {
my str $serialized-id = $dependency.serialize;
nqp::ifnull(
nqp::atkey($resolved,$serialized-id),
nqp::bindkey($resolved,$serialized-id, do {
my str $serialized-id = $dependency.serialize;
nqp::ifnull(
nqp::atkey($resolved,$serialized-id),
nqp::if(do {
my $comp-unit := $REPO.resolve($dependency.spec);
$!RMD("Old id: $dependency.id(), new id: {
$comp-unit and $comp-unit.repo-id
}")
if $!RMD;

return False
unless $comp-unit
and $comp-unit.repo-id eq $dependency.id;

True
})
);
}
$comp-unit and $comp-unit.repo-id eq $dependency.id
},
$loaded-lock.protect({ nqp::bindkey($resolved,$serialized-id, 1) }),
(return False)));
}

my $dependency-precomp := @precomp-stores
Expand Down Expand Up @@ -276,10 +273,7 @@ Need to re-check dependencies.")
CompUnit::PrecompilationStore :@precomp-stores =
Array[CompUnit::PrecompilationStore].new($.store),
) {
$loaded-lock.protect: {
my \precomped := nqp::atkey($loaded,$id.Str);
return precomped if precomped;
}
return $_ if $_ := $loaded-lock.protect: { nqp::atkey($loaded,$id.Str) };

if self!load-file(@precomp-stores, $id) -> $unit {
if (not $since or $unit.modified > $since)
Expand Down
11 changes: 8 additions & 3 deletions src/core.c/CompUnit/PrecompilationStore/FileSystem.pm6
Expand Up @@ -13,10 +13,15 @@ class CompUnit::PrecompilationStore::FileSystem
has $!loaded;
has $!dir-cache;
has $!compiler-cache;
has Lock $!update-lock;
has $!update-lock;

submethod TWEAK(--> Nil) {
#?if moar
$!update-lock := Lock::Soft.new;
#?endif
#?if !moar
$!update-lock := Lock.new;
#?endif
$!loaded := nqp::hash;
$!dir-cache := nqp::hash;
$!compiler-cache := nqp::hash;
Expand Down Expand Up @@ -60,8 +65,8 @@ class CompUnit::PrecompilationStore::FileSystem
$!lock := "$path.lock".IO.open(:create, :rw)
unless $!lock;
#?if moar
$!lock-count++;
$!lock.lock if $!lock-count == 1;
$!lock.lock if$!lock-count == 0;
++$!lock-count;
#?endif
#?if !moar
$!lock.lock if $!lock-count++ == 0;
Expand Down
36 changes: 21 additions & 15 deletions src/core.c/CompUnit/PrecompilationUnit/File.pm6
Expand Up @@ -9,7 +9,7 @@ my class CompUnit::PrecompilationUnit::File does CompUnit::PrecompilationUnit {

has Bool $!initialized;
has IO::Handle $!handle;
has Lock $!update-lock;
has $!update-lock;

submethod TWEAK(--> Nil) {
if $!bytecode {
Expand All @@ -19,7 +19,12 @@ my class CompUnit::PrecompilationUnit::File does CompUnit::PrecompilationUnit {
else {
$!initialized := False;
}
#?if moar
$!update-lock := Lock::Soft.new;
#?endif
#?if !moar
$!update-lock := Lock.new;
#?endif
}

method modified(--> Instant:D) {
Expand All @@ -28,22 +33,23 @@ my class CompUnit::PrecompilationUnit::File does CompUnit::PrecompilationUnit {

method !read-dependencies(--> Nil) {
$!initialized || $!update-lock.protect: {
return if $!initialized; # another thread beat us
$!handle := $!path.open(:r) unless $!handle;
unless $!initialized { # another thread beat us
$!handle := $!path.open(:r) unless $!handle;

$!checksum = $!handle.get;
$!source-checksum = $!handle.get;
my $dependency := $!handle.get;
my $dependencies := nqp::create(IterationBuffer);
while $dependency {
nqp::push(
$dependencies,
CompUnit::PrecompilationDependency::File.deserialize($dependency)
);
$dependency := $!handle.get;
$!checksum = $!handle.get;
$!source-checksum = $!handle.get;
my $dependency := $!handle.get;
my $dependencies := nqp::create(IterationBuffer);
while $dependency {
nqp::push(
$dependencies,
CompUnit::PrecompilationDependency::File.deserialize($dependency)
);
$dependency := $!handle.get;
}
nqp::bindattr(@!dependencies,List,'$!reified',$dependencies);
$!initialized := True;
}
nqp::bindattr(@!dependencies,List,'$!reified',$dependencies);
$!initialized := True;
}
}

Expand Down
9 changes: 7 additions & 2 deletions src/core.c/CompUnit/Repository/AbsolutePath.pm6
@@ -1,10 +1,15 @@
class CompUnit::Repository::AbsolutePath does CompUnit::Repository {
has Lock $!lock;
has $!loaded;
has $!lock;
has $!loaded;

method TWEAK(--> Nil) {
$!loaded := nqp::hash;
#?if moar
$!lock := Lock::Soft.new;
#?endif
#?if !moar
$!lock := Lock.new;
#?endif
}

method need(CompUnit::Repository::AbsolutePath:D:
Expand Down

0 comments on commit 6bd19e4

Please sign in to comment.