@@ -165,9 +165,13 @@ will be run.
165165- `worker_init_expr::Expr`: an expression that will be run on each worker process before any tests are run.
166166 Can be used to load packages or set up the environment. Must be a `:block` expression.
167167- `test_end_expr::Expr`: an expression that will be run after each testitem is run.
168- By default, the expression is `GC.gc(true)`, when `nworkers > 1`, to help with memory pressure,
169- otherwise it is a no-op.
170168 Can be used to verify that global state is unchanged after running a test. Must be a `:block` expression.
169+ - `gc_between_testitems::Bool`: If `true`, a full garbage collection (GC) will be run after each test item is run.
170+ Defaults to `nworkers > 1`, i.e. `true` when running with multiple worker processes, since multiple worker processes
171+ cannot coordinate to trigger Julia's GC, and it should not be necessary to invoke the GC directly if running without
172+ workers or with a single worker (since the GC will be triggered automatically by the single process running all the tests).
173+ Can also be set using the `RETESTITEMS_GC_BETWEEN_TESTITEMS` environment variable.
174+ Tip: For complete control over GC, set `gc_between_testitems=false` and manually trigger GC in `test_end_expr`.
171175- `memory_threshold::Real`: Sets the fraction of memory that can be in use before a worker processes are
172176 restarted to free memory. Defaults to $(DEFAULT_MEMORY_THRESHOLD[]) . Only supported with `nworkers > 0`.
173177 For example, if set to 0.8, then when >80% of the available memory is in use, a worker process will be killed and
@@ -242,9 +246,10 @@ function runtests(
242246 report:: Bool = parse (Bool, get (ENV , " RETESTITEMS_REPORT" , " false" )),
243247 logs:: Symbol = Symbol (get (ENV , " RETESTITEMS_LOGS" , default_log_display_mode (report, nworkers))),
244248 verbose_results:: Bool = (logs != = :issues && isinteractive ()),
245- test_end_expr:: Expr = nworkers > 1 ? :(GC . gc ( true )) : Expr (:block ),
249+ test_end_expr:: Expr = Expr (:block ),
246250 validate_paths:: Bool = parse (Bool, get (ENV , " RETESTITEMS_VALIDATE_PATHS" , " false" )),
247251 timeout_profile_wait:: Real = parse (Int, get (ENV , " RETESTITEMS_TIMEOUT_PROFILE_WAIT" , " 0" )),
252+ gc_between_testitems:: Bool = parse (Bool, get (ENV , " RETESTITEMS_GC_BETWEEN_TESTITEMS" , string (nworkers > 1 ))),
248253)
249254 nworker_threads = _validated_nworker_threads (nworker_threads)
250255 paths′ = _validated_paths (paths, validate_paths)
@@ -267,10 +272,10 @@ function runtests(
267272 debuglvl = Int (debug)
268273 if debuglvl > 0
269274 LoggingExtras. withlevel (LoggingExtras. Debug; verbosity= debuglvl) do
270- _runtests (shouldrun_combined, paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, timeout, retries, memory_threshold, verbose_results, debuglvl, report, logs, timeout_profile_wait)
275+ _runtests (shouldrun_combined, paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, timeout, retries, memory_threshold, verbose_results, debuglvl, report, logs, timeout_profile_wait, gc_between_testitems )
271276 end
272277 else
273- return _runtests (shouldrun_combined, paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, timeout, retries, memory_threshold, verbose_results, debuglvl, report, logs, timeout_profile_wait)
278+ return _runtests (shouldrun_combined, paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, timeout, retries, memory_threshold, verbose_results, debuglvl, report, logs, timeout_profile_wait, gc_between_testitems )
274279 end
275280end
276281
284289# By tracking and reusing test environments, we can avoid this issue.
285290const TEST_ENVS = Dict {String, String} ()
286291
287- function _runtests (shouldrun, paths, nworkers:: Int , nworker_threads:: String , worker_init_expr:: Expr , test_end_expr:: Expr , testitem_timeout:: Int , retries:: Int , memory_threshold:: Real , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol , timeout_profile_wait:: Int )
292+ function _runtests (shouldrun, paths, nworkers:: Int , nworker_threads:: String , worker_init_expr:: Expr , test_end_expr:: Expr , testitem_timeout:: Int , retries:: Int , memory_threshold:: Real , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol , timeout_profile_wait:: Int , gc_between_testitems :: Bool )
288293 # Don't recursively call `runtests` e.g. if we `include` a file which calls it.
289294 # So we ignore the `runtests(...)` call in `test/runtests.jl` when `runtests(...)`
290295 # was called from the command line.
@@ -304,7 +309,7 @@ function _runtests(shouldrun, paths, nworkers::Int, nworker_threads::String, wor
304309 if is_running_test_runtests_jl (proj_file)
305310 # Assume this is `Pkg.test`, so test env already active.
306311 @debugv 2 " Running in current environment `$(Base. active_project ()) `"
307- return _runtests_in_current_env (shouldrun, paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debug, report, logs, timeout_profile_wait)
312+ return _runtests_in_current_env (shouldrun, paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debug, report, logs, timeout_profile_wait, gc_between_testitems )
308313 else
309314 @debugv 1 " Activating test environment for `$proj_file `"
310315 orig_proj = Base. active_project ()
@@ -317,7 +322,7 @@ function _runtests(shouldrun, paths, nworkers::Int, nworker_threads::String, wor
317322 testenv = TestEnv. activate ()
318323 TEST_ENVS[proj_file] = testenv
319324 end
320- _runtests_in_current_env (shouldrun, paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debug, report, logs, timeout_profile_wait)
325+ _runtests_in_current_env (shouldrun, paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debug, report, logs, timeout_profile_wait, gc_between_testitems )
321326 finally
322327 Base. set_active_project (orig_proj)
323328 end
328333function _runtests_in_current_env (
329334 shouldrun, paths, projectfile:: String , nworkers:: Int , nworker_threads, worker_init_expr:: Expr , test_end_expr:: Expr ,
330335 testitem_timeout:: Int , retries:: Int , memory_threshold:: Real , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol ,
331- timeout_profile_wait:: Int ,
336+ timeout_profile_wait:: Int , gc_between_testitems :: Bool ,
332337)
333338 start_time = time ()
334339 proj_name = something (Pkg. Types. read_project (projectfile). name, " " )
@@ -359,6 +364,10 @@ function _runtests_in_current_env(
359364 ts = res. testset
360365 print_errors_and_captured_logs (testitem, run_number; logs)
361366 report_empty_testsets (testitem, ts)
367+ if gc_between_testitems
368+ @debugv 2 " Running GC"
369+ GC. gc (true )
370+ end
362371 if any_non_pass (ts) && run_number != max_runs
363372 run_number += 1
364373 @info " Retrying $(repr (testitem. name)) . Run=$run_number ."
@@ -368,7 +377,8 @@ function _runtests_in_current_env(
368377 end
369378 end
370379 elseif ! isempty (testitems. testitems)
371- # Try to free up memory on the coordinator before starting workers
380+ # Try to free up memory on the coordinator before starting workers, since
381+ # the workers won't be able to collect it if they get under memory pressure.
372382 GC. gc (true )
373383 # Use the logger that was set before we eval'd any user code to avoid world age
374384 # issues when logging https://github.com/JuliaLang/julia/issues/33865
@@ -392,7 +402,7 @@ function _runtests_in_current_env(
392402 ti = starting[i]
393403 @spawn begin
394404 with_logger (original_logger) do
395- manage_worker ($ w, $ proj_name, $ testitems, $ ti, $ nworker_threads, $ worker_init_expr, $ test_end_expr, $ testitem_timeout, $ retries, $ memory_threshold, $ verbose_results, $ debug, $ report, $ logs, $ timeout_profile_wait)
405+ manage_worker ($ w, $ proj_name, $ testitems, $ ti, $ nworker_threads, $ worker_init_expr, $ test_end_expr, $ testitem_timeout, $ retries, $ memory_threshold, $ verbose_results, $ debug, $ report, $ logs, $ timeout_profile_wait, $ gc_between_testitems )
396406 end
397407 end
398408 end
503513
504514function manage_worker (
505515 worker:: Worker , proj_name:: AbstractString , testitems:: TestItems , testitem:: Union{TestItem,Nothing} , nworker_threads, worker_init_expr:: Expr , test_end_expr:: Expr ,
506- default_timeout:: Int , retries:: Int , memory_threshold:: Real , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol , timeout_profile_wait:: Int
516+ default_timeout:: Int , retries:: Int , memory_threshold:: Real , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol , timeout_profile_wait:: Int ,
517+ gc_between_testitems:: Bool
507518)
508519 ntestitems = length (testitems. testitems)
509520 run_number = 1
@@ -547,6 +558,10 @@ function manage_worker(
547558 push! (testitem. stats, testitem_result. stats)
548559 print_errors_and_captured_logs (testitem, run_number; logs)
549560 report_empty_testsets (testitem, ts)
561+ if gc_between_testitems
562+ @debugv 2 " Running GC on $worker "
563+ remote_fetch (worker, :(GC. gc (true )))
564+ end
550565 if any_non_pass (ts) && run_number != max_runs
551566 run_number += 1
552567 @info " Retrying $(repr (testitem. name)) on $worker . Run=$run_number ."
0 commit comments