@@ -85,6 +85,9 @@ Global variable, a set of `Manifest.toml` files from the active projects used du
85
85
"""
86
86
const watched_manifests = Set {String} ()
87
87
88
+ # Locks access to `revision_queue` to prevent race conditions, see issues #837 and #845
89
+ const revise_lock = ReentrantLock ()
90
+
88
91
"""
89
92
Revise.revision_queue
90
93
@@ -586,9 +589,11 @@ This is generally called via a [`Revise.TaskThunk`](@ref).
586
589
end
587
590
if id != NOPACKAGE
588
591
pkgdata = pkgdatas[id]
589
- if hasfile (pkgdata, key) # issue #228
590
- push! (revision_queue, (pkgdata, relpath (key, pkgdata)))
591
- notify (revision_event)
592
+ lock (revise_lock) do
593
+ if hasfile (pkgdata, key) # issue #228
594
+ push! (revision_queue, (pkgdata, relpath (key, pkgdata)))
595
+ notify (revision_event)
596
+ end
592
597
end
593
598
end
594
599
end
@@ -639,7 +644,11 @@ function revise_file_queued(pkgdata::PkgData, file)
639
644
640
645
# Check to see if we're still watching this file
641
646
stillwatching = haskey (watched_files, dirfull)
642
- PkgId (pkgdata) != NOPACKAGE && push! (revision_queue, (pkgdata, relpath (file, pkgdata)))
647
+ if PkgId (pkgdata) != NOPACKAGE
648
+ lock (revise_lock) do
649
+ push! (revision_queue, (pkgdata, relpath (file, pkgdata)))
650
+ end
651
+ end
643
652
end
644
653
return
645
654
end
734
743
Attempt to perform previously-failed revisions. This can be useful in cases of order-dependent errors.
735
744
"""
736
745
function retry ()
737
- for (k, v) in queue_errors
738
- push! (revision_queue, k)
746
+ lock (revise_lock) do
747
+ for (k, v) in queue_errors
748
+ push! (revision_queue, k)
749
+ end
739
750
end
740
751
revise ()
741
752
end
@@ -749,100 +760,102 @@ otherwise these are only logged.
749
760
"""
750
761
function revise (; throw= false )
751
762
sleep (0.01 ) # in case the file system isn't quite done writing out the new files
752
- have_queue_errors = ! isempty (queue_errors)
753
-
754
- # Do all the deletion first. This ensures that a method that moved from one file to another
755
- # won't get redefined first and deleted second.
756
- revision_errors = Tuple{PkgData,String}[]
757
- queue = sort! (collect (revision_queue); lt= pkgfileless)
758
- finished = eltype (revision_queue)[]
759
- mexsnews = ModuleExprsSigs[]
760
- interrupt = false
761
- for (pkgdata, file) in queue
762
- try
763
- push! (mexsnews, handle_deletions (pkgdata, file)[1 ])
764
- push! (finished, (pkgdata, file))
765
- catch err
766
- throw && Base. throw (err)
767
- interrupt |= isa (err, InterruptException)
768
- push! (revision_errors, (pkgdata, file))
769
- queue_errors[(pkgdata, file)] = (err, catch_backtrace ())
763
+ lock (revise_lock) do
764
+ have_queue_errors = ! isempty (queue_errors)
765
+
766
+ # Do all the deletion first. This ensures that a method that moved from one file to another
767
+ # won't get redefined first and deleted second.
768
+ revision_errors = Tuple{PkgData,String}[]
769
+ queue = sort! (collect (revision_queue); lt= pkgfileless)
770
+ finished = eltype (revision_queue)[]
771
+ mexsnews = ModuleExprsSigs[]
772
+ interrupt = false
773
+ for (pkgdata, file) in queue
774
+ try
775
+ push! (mexsnews, handle_deletions (pkgdata, file)[1 ])
776
+ push! (finished, (pkgdata, file))
777
+ catch err
778
+ throw && Base. throw (err)
779
+ interrupt |= isa (err, InterruptException)
780
+ push! (revision_errors, (pkgdata, file))
781
+ queue_errors[(pkgdata, file)] = (err, catch_backtrace ())
782
+ end
770
783
end
771
- end
772
- # Do the evaluation
773
- for ((pkgdata, file), mexsnew) in zip (finished, mexsnews)
774
- defaultmode = PkgId (pkgdata). name == " Main" ? :evalmeth : :eval
775
- i = fileindex (pkgdata, file)
776
- i === nothing && continue # file was deleted by `handle_deletions`
777
- fi = fileinfo (pkgdata, i)
778
- modsremaining = Set (keys (mexsnew))
779
- changed, err = true , nothing
780
- while changed
781
- changed = false
782
- for (mod, exsnew) in mexsnew
783
- mod ∈ modsremaining || continue
784
- try
785
- mode = defaultmode
786
- # Allow packages to override the supplied mode
787
- if isdefined (mod, :__revise_mode__ )
788
- mode = getfield (mod, :__revise_mode__ ):: Symbol
789
- end
790
- mode ∈ (:sigs , :eval , :evalmeth , :evalassign ) || error (" unsupported mode " , mode)
791
- exsold = get (fi. modexsigs, mod, empty_exs_sigs)
792
- for rex in keys (exsnew)
793
- sigs, includes = eval_rex (rex, exsold, mod; mode= mode)
794
- if sigs != = nothing
795
- exsnew[rex] = sigs
784
+ # Do the evaluation
785
+ for ((pkgdata, file), mexsnew) in zip (finished, mexsnews)
786
+ defaultmode = PkgId (pkgdata). name == " Main" ? :evalmeth : :eval
787
+ i = fileindex (pkgdata, file)
788
+ i === nothing && continue # file was deleted by `handle_deletions`
789
+ fi = fileinfo (pkgdata, i)
790
+ modsremaining = Set (keys (mexsnew))
791
+ changed, err = true , nothing
792
+ while changed
793
+ changed = false
794
+ for (mod, exsnew) in mexsnew
795
+ mod ∈ modsremaining || continue
796
+ try
797
+ mode = defaultmode
798
+ # Allow packages to override the supplied mode
799
+ if isdefined (mod, :__revise_mode__ )
800
+ mode = getfield (mod, :__revise_mode__ ):: Symbol
796
801
end
797
- if includes != = nothing
798
- maybe_add_includes_to_pkgdata! (pkgdata, file, includes; eval_now= true )
802
+ mode ∈ (:sigs , :eval , :evalmeth , :evalassign ) || error (" unsupported mode " , mode)
803
+ exsold = get (fi. modexsigs, mod, empty_exs_sigs)
804
+ for rex in keys (exsnew)
805
+ sigs, includes = eval_rex (rex, exsold, mod; mode= mode)
806
+ if sigs != = nothing
807
+ exsnew[rex] = sigs
808
+ end
809
+ if includes != = nothing
810
+ maybe_add_includes_to_pkgdata! (pkgdata, file, includes; eval_now= true )
811
+ end
799
812
end
813
+ delete! (modsremaining, mod)
814
+ changed = true
815
+ catch _err
816
+ err = _err
800
817
end
801
- delete! (modsremaining, mod)
802
- changed = true
803
- catch _err
804
- err = _err
805
818
end
806
819
end
820
+ if isempty (modsremaining)
821
+ pkgdata. fileinfos[i] = FileInfo (mexsnew, fi)
822
+ delete! (queue_errors, (pkgdata, file))
823
+ else
824
+ throw && Base. throw (err)
825
+ interrupt |= isa (err, InterruptException)
826
+ push! (revision_errors, (pkgdata, file))
827
+ queue_errors[(pkgdata, file)] = (err, catch_backtrace ())
828
+ end
807
829
end
808
- if isempty (modsremaining)
809
- pkgdata. fileinfos[i] = FileInfo (mexsnew, fi)
810
- delete! (queue_errors, (pkgdata, file))
830
+ if interrupt
831
+ for pkgfile in finished
832
+ haskey (queue_errors, pkgfile) || delete! (revision_queue, pkgfile)
833
+ end
811
834
else
812
- throw && Base. throw (err)
813
- interrupt |= isa (err, InterruptException)
814
- push! (revision_errors, (pkgdata, file))
815
- queue_errors[(pkgdata, file)] = (err, catch_backtrace ())
835
+ empty! (revision_queue)
816
836
end
817
- end
818
- if interrupt
819
- for pkgfile in finished
820
- haskey (queue_errors, pkgfile) || delete! (revision_queue, pkgfile)
821
- end
822
- else
823
- empty! (revision_queue)
824
- end
825
- errors (revision_errors)
826
- if ! isempty (queue_errors)
827
- if ! have_queue_errors # only print on the first time errors occur
828
- io = IOBuffer ()
829
- println (io, " \n " ) # better here than in the triple-quoted literal, see https://github.com/JuliaLang/julia/issues/34105
830
- for (pkgdata, file) in keys (queue_errors)
831
- println (io, " " , joinpath (basedir (pkgdata), file))
837
+ errors (revision_errors)
838
+ if ! isempty (queue_errors)
839
+ if ! have_queue_errors # only print on the first time errors occur
840
+ io = IOBuffer ()
841
+ println (io, " \n " ) # better here than in the triple-quoted literal, see https://github.com/JuliaLang/julia/issues/34105
842
+ for (pkgdata, file) in keys (queue_errors)
843
+ println (io, " " , joinpath (basedir (pkgdata), file))
844
+ end
845
+ str = String (take! (io))
846
+ @warn """ The running code does not match the saved version for the following files:$str
847
+ If the error was due to evaluation order, it can sometimes be resolved by calling `Revise.retry()`.
848
+ Use Revise.errors() to report errors again. Only the first error in each file is shown.
849
+ Your prompt color may be yellow until the errors are resolved."""
850
+ maybe_set_prompt_color (:warn )
832
851
end
833
- str = String (take! (io))
834
- @warn """ The running code does not match the saved version for the following files:$str
835
- If the error was due to evaluation order, it can sometimes be resolved by calling `Revise.retry()`.
836
- Use Revise.errors() to report errors again. Only the first error in each file is shown.
837
- Your prompt color may be yellow until the errors are resolved."""
838
- maybe_set_prompt_color (:warn )
852
+ else
853
+ maybe_set_prompt_color (:ok )
839
854
end
840
- else
841
- maybe_set_prompt_color (:ok )
842
- end
843
- tracking_Main_includes[] && queue_includes (Main)
855
+ tracking_Main_includes[] && queue_includes (Main)
844
856
845
- process_user_callbacks! (throw= throw)
857
+ process_user_callbacks! (throw= throw)
858
+ end
846
859
847
860
nothing
848
861
end
0 commit comments