-
Notifications
You must be signed in to change notification settings - Fork 33
Add persistent_tasks test #174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
03c6407
Add persistent_tasks test
timholy cbbdc9c
Run JuliaFormatter, response to review comments
timholy ae7d50b
Improve robustness
timholy a6262d0
More JuliaFormatter
timholy 75d7542
Escape paths (windows)
timholy b2913a0
Update src/persistent_tasks.jl
timholy d1a202f
fails -> broken
timholy 1ca48e7
Add to CHANGELOG
timholy 4cfa84c
Adapt interface
lgoettgens d618794
Update changelog
lgoettgens 72dd130
Activate new test in `test_smoke`
lgoettgens be6b2bf
Update src/persistent_tasks.jl
lgoettgens 5992b0c
Restore the previous environment
lgoettgens 0dcd8bf
Apply suggestions from code review
timholy 77f7fd7
Turn `test_persistent_tasks` on by default
lgoettgens File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| """ | ||
| Aqua.test_persistent_tasks(package) | ||
|
|
||
| Test whether loading `package` creates persistent `Task`s | ||
| which may block precompilation of dependent packages. | ||
|
|
||
| # Motivation | ||
|
|
||
| Julia 1.10 and higher wait for all running `Task`s to finish | ||
| before writing out the precompiled (cached) version of the package. | ||
| One consequence is that a package that launches | ||
| `Task`s in its `__init__` function may precompile successfully, | ||
| but block precompilation of any packages that depend on it. | ||
|
|
||
| # Example | ||
|
|
||
| Let's create a dummy package, `PkgA`, that launches a persistent `Task`: | ||
|
|
||
| ```julia | ||
| module PkgA | ||
| const t = Ref{Any}() # to prevent the Timer from being garbage-collected | ||
| __init__() = t[] = Timer(0.1; interval=1) # create a persistent `Timer` `Task` | ||
| end | ||
| ``` | ||
|
|
||
| `PkgA` will precompile successfully, because `PkgA.__init__()` does not | ||
| run when `PkgA` is precompiled. However, | ||
|
|
||
| ```julia | ||
| module PkgB | ||
| using PkgA | ||
| end | ||
| ``` | ||
|
|
||
| fails to precompile: `using PkgA` runs `PkgA.__init__()`, which | ||
| leaves the `Timer` `Task` running, and that causes precompilation | ||
| of `PkgB` to hang. | ||
|
|
||
| # How the test works | ||
|
|
||
| This test works by launching a Julia process that tries to precompile a | ||
| dummy package similar to `PkgB` above, modified to signal back to Aqua when | ||
| `PkgA` has finished loading. The test fails if the gap between loading `PkgA` | ||
| and finishing precompilation exceeds time `tmax`. | ||
|
|
||
| # How to fix failing packages | ||
|
|
||
| Often, the easiest fix is to modify the `__init__` function to check whether the | ||
| Julia process is precompiling some other package; if so, don't launch the | ||
| persistent `Task`s. | ||
|
|
||
| ``` | ||
| function __init__() | ||
| # Other setup code here | ||
| if ccall(:jl_generating_output, Cint, ()) == 0 # if we're not precompiling... | ||
| # launch persistent tasks here | ||
| end | ||
| end | ||
| ``` | ||
|
|
||
| In more complex cases, you may need to set up independently-callable functions | ||
| to launch the tasks and set conditions that allow them to cleanly exit. | ||
|
|
||
| # Arguments | ||
| - `package`: a top-level `Module` or `Base.PkgId`. | ||
|
|
||
| # Keyword Arguments | ||
| - `broken::Bool = false`: If true, it uses `@test_broken` instead of | ||
| `@test`. | ||
| - `tmax::Real = 5`: the maximum time (in seconds) to wait after loading the | ||
| package before forcibly shutting down the precompilation process (triggering | ||
| a test failure). | ||
| """ | ||
| function test_persistent_tasks(package::PkgId; broken::Bool = false, kwargs...) | ||
| if broken | ||
| @test_broken !has_persistent_tasks(package; kwargs...) | ||
| else | ||
| @test !has_persistent_tasks(package; kwargs...) | ||
| end | ||
| end | ||
|
|
||
| function test_persistent_tasks(package::Module; kwargs...) | ||
| test_persistent_tasks(PkgId(package); kwargs...) | ||
| end | ||
|
|
||
| function has_persistent_tasks(package::PkgId; tmax = 10) | ||
| result = root_project_or_failed_lazytest(package) | ||
| result isa LazyTestResult && error("Unable to locate Project.toml") | ||
| return !precompile_wrapper(result, tmax) | ||
| end | ||
|
|
||
| """ | ||
| Aqua.find_persistent_tasks_deps(package; broken = Dict{String,Bool}(), kwargs...) | ||
|
|
||
| Test all the dependencies of `package` with [`Aqua.test_persistent_tasks`](@ref). | ||
| On Julia 1.10 and higher, it returns a list of all dependencies failing the test. | ||
| These are likely the ones blocking precompilation of your package. | ||
|
|
||
| Any additional kwargs (e.g., `tmax`) are passed to [`Aqua.test_persistent_tasks`](@ref). | ||
| """ | ||
| function find_persistent_tasks_deps(package::PkgId; kwargs...) | ||
| result = root_project_or_failed_lazytest(package) | ||
| result isa LazyTestResult && error("Unable to locate Project.toml") | ||
| prj = TOML.parsefile(result) | ||
| deps = get(prj, "deps", Dict{String,Any}()) | ||
| filter!(deps) do (name, uuid) | ||
| id = PkgId(UUID(uuid), name) | ||
| return has_persistent_tasks(id; kwargs...) | ||
| end | ||
| return [name for (name, _) in deps] | ||
| end | ||
|
|
||
| function find_persistent_tasks_deps(package::Module; kwargs...) | ||
| find_persistent_tasks_deps(PkgId(package); kwargs...) | ||
| end | ||
|
|
||
| function precompile_wrapper(project, tmax) | ||
| prev_project = Base.active_project() | ||
timholy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| isdefined(Pkg, :respect_sysimage_versions) && Pkg.respect_sysimage_versions(false) | ||
| try | ||
| pkgdir = dirname(project) | ||
| pkgname = get(TOML.parsefile(project), "name", nothing) | ||
| if isnothing(pkgname) | ||
| @error "Unable to locate package name in $project" | ||
| return false | ||
| end | ||
| wrapperdir = tempname() | ||
| wrappername, _ = only(Pkg.generate(wrapperdir)) | ||
| Pkg.activate(wrapperdir) | ||
| Pkg.develop(PackageSpec(path = pkgdir)) | ||
| statusfile = joinpath(wrapperdir, "done.log") | ||
| open(joinpath(wrapperdir, "src", wrappername * ".jl"), "w") do io | ||
| println( | ||
| io, | ||
| """ | ||
| module $wrappername | ||
fingolfin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| using $pkgname | ||
| # Signal Aqua from the precompilation process that we've finished loading the package | ||
| open("$(escape_string(statusfile))", "w") do io | ||
| println(io, "done") | ||
| flush(io) | ||
| end | ||
| end | ||
| """, | ||
| ) | ||
| end | ||
| # Precompile the wrapper package | ||
| cmd = `$(Base.julia_cmd()) --project=$wrapperdir -e 'using Pkg; Pkg.precompile()'` | ||
| proc = run(cmd; wait = false) | ||
| while !isfile(statusfile) && process_running(proc) | ||
| sleep(0.5) | ||
| end | ||
| if !isfile(statusfile) | ||
| @error "Unexpected error: $statusfile was not created, but precompilation exited" | ||
| return false | ||
| end | ||
| # Check whether precompilation finishes in the required time | ||
| t = time() | ||
| while process_running(proc) && time() - t < tmax | ||
| sleep(0.1) | ||
| end | ||
| success = !process_running(proc) | ||
| if !success | ||
| kill(proc) | ||
| end | ||
| return success | ||
| finally | ||
| isdefined(Pkg, :respect_sysimage_versions) && Pkg.respect_sysimage_versions(true) | ||
| Pkg.activate(prev_project) | ||
timholy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| name = "PersistentTask" | ||
| uuid = "e5c298b6-d81d-47aa-a9ed-5ea983e22986" |
6 changes: 6 additions & 0 deletions
6
test/pkgs/PersistentTasks/PersistentTask/src/PersistentTask.jl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| module PersistentTask | ||
|
|
||
| const t = Ref{Any}() | ||
| __init__() = t[] = Timer(0.1; interval = 1) # create a persistent `Timer` `Task` | ||
|
|
||
| end # module PersistentTask |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| name = "TransientTask" | ||
| uuid = "94ae9332-58b0-4342-989c-0a7e44abcca9" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| module TransientTask | ||
|
|
||
| __init__() = Timer(0.1) # create a transient `Timer` `Task` | ||
|
|
||
| end # module TransientTask |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| name = "UsesBoth" | ||
| uuid = "96f12b6e-60f8-43dc-b131-049a88a2f499" | ||
|
|
||
| [deps] | ||
| PersistentTask = "e5c298b6-d81d-47aa-a9ed-5ea983e22986" | ||
| TransientTask = "94ae9332-58b0-4342-989c-0a7e44abcca9" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| module UsesBoth | ||
|
|
||
| using TransientTask | ||
| using PersistentTask | ||
|
|
||
| end # module UsesBoth |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| module TestPersistentTasks | ||
|
|
||
| include("preamble.jl") | ||
| using Base: PkgId, UUID | ||
| using Pkg: TOML | ||
|
|
||
| function getid(name) | ||
| path = joinpath(@__DIR__, "pkgs", "PersistentTasks", name) | ||
| if path ∉ LOAD_PATH | ||
| pushfirst!(LOAD_PATH, path) | ||
| end | ||
| prj = TOML.parsefile(joinpath(path, "Project.toml")) | ||
| return PkgId(UUID(prj["uuid"]), prj["name"]) | ||
| end | ||
|
|
||
|
|
||
| @testset "PersistentTasks" begin | ||
| @test !Aqua.has_persistent_tasks(getid("TransientTask")) | ||
|
|
||
| result = Aqua.find_persistent_tasks_deps(getid("TransientTask")) | ||
| @test result == [] | ||
|
|
||
| if Base.VERSION >= v"1.10-" | ||
| @test Aqua.has_persistent_tasks(getid("PersistentTask")) | ||
|
|
||
| result = Aqua.find_persistent_tasks_deps(getid("UsesBoth")) | ||
| @test result == ["PersistentTask"] | ||
| end | ||
| filter!(str -> !occursin("PersistentTasks", str), LOAD_PATH) | ||
| end | ||
|
|
||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.