Skip to content

Commit

Permalink
Avoid race condition in GC_Example test (#10665)
Browse files Browse the repository at this point in the history
Re-enables https://github.com/enso-org/enso/pull/10602/files#r1690919173 - uses `IO.println` to allow us to see what the CI actually does.
  • Loading branch information
JaroslavTulach authored Jul 29, 2024
1 parent 4dfbbd5 commit 07bc728
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 40 deletions.
12 changes: 5 additions & 7 deletions build/build/src/engine/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,12 +661,10 @@ pub async fn runner_sanity_test(
.args(["--run", repo_root.test.join("Base_Tests").as_str()])
.set_env_opt(ENSO_JAVA, enso_java)?
.set_env(ENSO_DATA_DIRECTORY, engine_package)?
.run_stdout()
.await?;
ensure!(
test_base.contains("0 tests failed."),
"All tests shall succeed. Output:\n{test_base}",
);
.run_ok()
.await;
test_base
} else {
Ok(())
}
Ok(())
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,29 +177,34 @@ private final class ProcessItems extends ThreadLocalAction implements Runnable {
*/
@Override
protected void perform(ThreadLocalAction.Access access) {
var isMyThreadChoosen = false;
for (; ; ) {
Item[] toProcess;
synchronized (pendingItems) {
request.cancel(false);
if (!isMyThreadChoosen) {
if (request == null || request.isCancelled()) {
// some thread is already handing the request
return;
} else {
// I am choosen and I will loop and process pendingItems
// until they are available
isMyThreadChoosen = true;
// signal others this request has choosen thread
request.cancel(false);
}
}
if (pendingItems.isEmpty()) {
// nothing to process,
// signal request is finished
// nothing to process anymore,
// signal request is finished and new one shall be scheduled
request = null;
return;
}
toProcess = pendingItems.toArray(Item[]::new);
// mark as being processed
pendingItems.set(0, null);
pendingItems.clear();
}
try {
for (var it : toProcess) {
it.finalizeNow(context);
removeFromItems(it);
}
} finally {
synchronized (pendingItems) {
pendingItems.subList(0, toProcess.length).clear();
}
for (var it : toProcess) {
it.finalizeNow(context);
removeFromItems(it);
}
}
}
Expand Down
48 changes: 32 additions & 16 deletions test/Base_Tests/src/Runtime/GC_Example.enso
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from Standard.Base import all
import Standard.Base.Errors.Illegal_State.Illegal_State
import Standard.Base.Runtime.Managed_Resource.Managed_Resource
import Standard.Base.Runtime.Ref.Ref

Expand All @@ -15,27 +16,42 @@ type My_Resource

close_resource resource = resource.close

repeat_cleanup_until_done counter =
go i =
if counter.get == 0 then Nothing else
if i % 100 == 0 then
IO.println "Still "+counter.get.to_text+" resources to clean up..."
Runtime.gc
@Tail_Call go i+1
go 1
repeat_cleanup_until_done counter println =
deadline = Date_Time.now + (Duration.new minutes=5)
while_counter_not_zero i = if counter.get != 0 then
if i % 10 == 0 then
println "Still "+counter.get.to_text+" resources to clean up..."

perform_test n:Integer println =
Runtime.gc

deadline_not_reached at = if at < deadline then True else
message = "Timeout has been reached, but there are "+counter.get.to_text+" resources remaining that were not cleaned up."
println message
False

if deadline_not_reached Date_Time.now then
@Tail_Call while_counter_not_zero i+1

while_counter_not_zero 1

perform_test n:Integer println =
resource_holder = Ref.new Nothing
counter = Ref.new 0
println "Allocating "+n.to_text+" resources..."
0.up_to n . each _->
My_Resource.allocate counter

println "Cleaning up..."
repeat_cleanup_until_done counter
println "All cleaned up! Remaining: "+counter.get.to_text
println "Allocating "+n.to_text+" resources..."
allocate_resources ref =
all = 0.up_to n . map _->
My_Resource.allocate counter
ref.put all
allocate_resources resource_holder

println "Cleaning up "+resource_holder.get.length.to_text+" resources"
# We replace the vector with Nothing to make the resources inaccessible and schedule them for GC
resource_holder.put Nothing

repeat_cleanup_until_done counter println
println "Cleaning finished! Remaining: "+counter.get.to_text
counter.get

main n=1000000 =
perform_test n IO.println

12 changes: 9 additions & 3 deletions test/Base_Tests/src/Runtime/Managed_Resource_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,15 @@ add_specs suite_builder = suite_builder.group "Managed_Resource" group_builder->
r_3 = Panic.recover Any <| Managed_Resource.bracket 42 (_-> Nothing) (_-> Panic.throw "action")
r_3.catch . should_equal "action"

group_builder.specify "allocate lots of resources at once" pending=(if Environment.get "CI" . is_nothing . not then "GC Example is marked as pending as it was causing problems on the CI.") <|
remaining = GC_Example.perform_test 100000 (_->Nothing)
remaining . should_equal 0
group_builder.specify "allocate lots of resources at once" <|
messages = Vector.build builder->
builder.append '\n'
remaining = GC_Example.perform_test 100000 builder.append
if remaining == 0 then
builder.append "OK"

if messages.last != "OK" then
Test.fail (messages.join '\n')

main filter=Nothing =
suite = Test.build suite_builder->
Expand Down

0 comments on commit 07bc728

Please sign in to comment.