Skip to content
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

Characterize eval plugin race leading to GetLinkable errors #4185

Closed
wants to merge 3 commits into from

Conversation

jhrcek
Copy link
Collaborator

@jhrcek jhrcek commented Apr 21, 2024

Here's the full characterization of the race that leads to occasional GetLinkable errors that are still reproducible on master (see steps to reproduce)

Here's my best attempt to describe the race condition succinctly:

  1. we end up in this error branch sometimes because hieCoreFp is Nothing
  2. It is Nothing because getLinkableType here returns Nothing, which leads to writeCoreFileIfNeeded on this line to NOT write a core file.

So the question is why does getLinkableType sometimes return Nothing (instead of Just BCOLinkable which would be required for writing core file)?

  1. eval plugin redefines the NeedsCompilation rule - so it returns linkableType == Just BCOLinkable when the file is evaluating
  2. This rule uses IsEvaluating rule to decide if given file is evaluating. This rule consults a global variable that stores "set of files being evaluated"
  3. Now comes the racy part: the file is present in the "is evaluating" set only temporarily. It is inserted there by queueForEvaluation and then later removed by unqueueForEvaluation.

EDIT: @soulomoon pointed out mistake in my explanation - the error we see in the tests is a different than the original explanation claimed. Explanation corrected slightly, but it doesn't change what the actual race condition is.
Here is the corrected explanation in a video form walking through the code
Explanation.webm

The race condition is that IsEvaluating returns False (and thus linkableType ends up as Nothing, so .core file is not generated) in case that queueForEvaluation is called AFTER the isEvaluating check is done or if unqueueForEvaluation is called BEFORE isEvaluating check is done.

Here are 2 examples of one successful test and one failing test.
The thing that makes the difference is whether IsEvaluating rule is triggered WHILE given file is in the "set of files being evaluated".
Screenshot from 2024-04-21 07-44-20
Screenshot from 2024-04-21 07-37-38

@soulomoon
Copy link
Collaborator

I am wondering under what circumstances we are using unqueueForEvaluation, is that another shutdown problem?

@jhrcek
Copy link
Collaborator Author

jhrcek commented Apr 21, 2024

No, it's only used in one place, in this bracket_:

final_hscEnv <- liftIO $ bracket_
(do queueForEvaluation st nfp
setSomethingModified VFSUnmodified st [toKey IsEvaluating nfp] "Eval")
(do unqueueForEvaluation st nfp
setSomethingModified VFSUnmodified st [toKey IsEvaluating nfp] "Eval")
(initialiseSessionForEval (needsQuickCheck tests) st nfp)

@soulomoon
Copy link
Collaborator

soulomoon commented Apr 21, 2024

ThreadId 403 | 2024-04-17T13:02:14.285792Z | Error | eval: Internal Error: BadDependency "GetLinkable"
...
 "message": "called GetLinkable for a file without a linkable: NormalizedFilePath \"/tmp/hls-test-root/extra-dir-56738026422965/TI_Info.hs\"\nCallStack (from HasCallStack):\n  error, called at src/Development/IDE/Core/Rules.hs:1068:18 in ghcide-2.7.0.0-inplace:Development.IDE.Core.Rules"

@jhrcek I don't quite understand why we have the above error if we have getLinkableType(Result = Nothing),
It seems we have the following chain by the above error:
getLinkableRule failed => GetModIface(result hirCoreFp=Nothing) => writeCoreFileIfNeeded(result mbHiFile=Nothing) => writeCoreFileIfNeeded(condition: Maybe LinkableType is not Nonthing) => getLinkableType(result is not Nothing)
Is there some error in my duducation 🤔.

writeCoreFileIfNeeded :: ShakeExtras -> HscEnv -> Maybe LinkableType -> Action (IdeResult ModGuts) -> TcModuleResult -> Action (IdeResult HiFileResult)
writeCoreFileIfNeeded _ hsc Nothing _ tmr = do
  incrementRebuildCount
  res <- liftIO $ mkHiFileResultNoCompile hsc tmr
  pure ([], Just $! res)
 ....
  
getLinkableRule :: Recorder (WithPriority Log) -> Rules ()
getLinkableRule recorder =
  defineEarlyCutoff (cmapWithPrio LogShake recorder) $ Rule $ \GetLinkable f -> do
    ModSummaryResult{msrModSummary = ms} <- use_ GetModSummary f
    HiFileResult{hirModIface, hirModDetails, hirCoreFp} <- use_ GetModIface f
    let obj_file  = ml_obj_file (ms_location ms)
        core_file = ml_core_file (ms_location ms)
    case hirCoreFp of
      Nothing -> error $ "called GetLinkable for a file without a linkable: " ++ show f
...


getModIfaceRule :: Recorder (WithPriority Log) -> Rules ()
getModIfaceRule recorder = defineEarlyCutoff (cmapWithPrio LogShake recorder) $ Rule $ \GetModIface f -> do
  fileOfInterest <- use_ IsFileOfInterest f
  res <- case fileOfInterest of
    IsFOI status -> do
      -- Never load from disk for files of interest
      tmr <- use_ TypeCheck f
      linkableType <- getLinkableType f
      hsc <- hscEnv <$> use_ GhcSessionDeps f
      let compile = fmap ([],) $ use GenerateCore f
      se <- getShakeExtras
      (diags, !mbHiFile) <- writeCoreFileIfNeeded se hsc linkableType compile tmr
      let fp = hiFileFingerPrint <$> mbHiFile
      hiDiags <- case mbHiFile of
        Just hiFile
          | OnDisk <- status
          , not (tmrDeferredError tmr) -> liftIO $ writeHiFile se hsc hiFile
        _ -> pure []
      return (fp, (diags++hiDiags, mbHiFile))
....

@jhrcek
Copy link
Collaborator Author

jhrcek commented Apr 21, 2024

I'm not sure I follow your reasoning. I don't understand what you mean by

writeCoreFileIfNeeded(condition: Maybe LinkableType is not Nonthing)

I think the problem is that when you call writeCoreFileIfNeeded with Maybe LinkableType argument set to Nothing, the .core file is not written to disk. But the rule fails even before touching the core file, because we throw error every time when getLinkableType results in Nothing:

Nothing -> error $ "called GetLinkable for a file which doesn't need compilation: " ++ show f

@soulomoon
Copy link
Collaborator

soulomoon commented Apr 21, 2024

I'm not sure I follow your reasoning. I don't understand what you mean by

writeCoreFileIfNeeded(condition: Maybe LinkableType is not Nonthing)

I think the problem is that when you call writeCoreFileIfNeeded with Maybe LinkableType argument set to Nothing, the .core file is not written to disk. But the rule fails even before touching the core file, because we throw error every time when getLinkableType results in Nothing:

Nothing -> error $ "called GetLinkable for a file which doesn't need compilation: " ++ show f

This seems to be a different error than the one in #4093 (comment)


'called GetLinkable for a file without a linkable' versus 'called GetLinkable for a file which doesn't need compilation: '

@jhrcek
Copy link
Collaborator Author

jhrcek commented Apr 21, 2024

Ah, you're right. We end up in the first error branch because hirCoreFp is Nothing.

But I still don't get why you think we should end up calling writeCoreFileIfNeeded(condition: Maybe LinkableType is not Nonthing)

@soulomoon
Copy link
Collaborator

soulomoon commented Apr 21, 2024

Ah, you're right. We end up in the first error branch because hirCoreFp is Nothing.

But I still don't get why you think we should end up calling writeCoreFileIfNeeded(condition: Maybe LinkableType is not Nonthing)

Sorry that I am not stating it clear.
If writeCoreFileIfNeeded(result mbHiFile=Nothing), than writeCoreFileIfNeeded's arguments Maybe LinkableType must not be Nothing,
since if Maybe LinkableType is Nothing, we have writeCoreFileIfNeeded(result mbHiFile=Just xxx) (by the definition of writeCoreFileIfNeeded)

@jhrcek
Copy link
Collaborator Author

jhrcek commented Apr 21, 2024

But as you can see in the screenshot of failing test case linkableType=Nothing (printed by this trace)

@soulomoon
Copy link
Collaborator

But as you can see in the screenshot of failing test case linkableType=Nothing (printed by this trace)

Right, It confused me :(

@jhrcek
Copy link
Collaborator Author

jhrcek commented Apr 22, 2024

@soulomoon explanation in PR description corrected based on your pointers.
I also added a video (no sound) walkthrough of the code showing how we get to the error branch.

@wz1000
Copy link
Collaborator

wz1000 commented Apr 22, 2024

I still don't understand why isEvaluating returns false after we queue a file for evaluation, because setSomethingModified VFSUnmodified st [toKey IsEvaluating nfp] "Eval" is supposed to invalidate the IsEvaluating key and then restart the build session.

@jhrcek
Copy link
Collaborator Author

jhrcek commented Apr 22, 2024

I still don't understand why isEvaluating returns false after we queue a file for evaluation, because setSomethingModified VFSUnmodified st [toKey IsEvaluating nfp] "Eval" is supposed to invalidate the IsEvaluating key and then restart the build session.

I didn't check this deeply yet, so this is just me theorizing now. But if you invalidate IsEvaluating, the NeedsCompilation rule might already be in flight (e.g in the True if branch here) and might have already decided not to (re)generate core file - I think this is what we see in the failed test screenshot.

I mean the race is that (1) and (2) are swapped sometimes.

  • we start IsEvaluating rule
  • we enqueue file for evaluation which triggers invalidation of IsEvaluating rule
  • IsEvaluating rule finishes with False (1)
  • the invalidation of IsEvaluating finishes, but it's too late, because it already finished evaluating (2)

@wz1000
Copy link
Collaborator

wz1000 commented Apr 22, 2024

I believe that any on going compile should have been cancelled by the restart and the NeedsCompilation rule should be re-evaluated.

@@ -56,13 +56,13 @@ instance IsIdeGlobal EvaluatingVar
queueForEvaluation :: IdeState -> NormalizedFilePath -> IO ()
queueForEvaluation ide nfp = do
EvaluatingVar var <- getIdeGlobalState ide
atomicModifyIORef' var (\fs -> (Set.insert nfp fs, ()))
atomicModifyIORef' var (\fs -> (trace ("TRACE: queueForEvaluation: " <> show nfp ) $ Set.insert nfp fs, ()))
Copy link
Collaborator

@soulomoon soulomoon Apr 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is odd that we are still seeing IsEvaluating: False, inbetween queueForEvaluation and unqueueForEvaluation
Maybe we can add the trace to runEvalCmd to make sure the restart session take effect.

              (do queueForEvaluation st nfp
                  setSomethingModified VFSUnmodified st [toKey IsEvaluating nfp] "Eval"
                  (trace ("TRACE: queueForEvaluation done")

@soulomoon
Copy link
Collaborator

soulomoon commented Apr 22, 2024

Some reproduction in /aarch64-osx/ghc-9.8.2/ after the modification suggested in the my review comment, not the same error, but a timeout

TRACE: compNeeded=Nothing (NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-93896921344/TInfo.hs")
TRACE: isMemberEvaluatingVar=False(NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-93896921344/TI_Info.hs")
TRACE: isEvaluating=False(NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-93896921344/TI_Info.hs")
TRACE: queueForEvaluation: NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-93896921344/TI_Info.hs"
TRACE: linkableType=Nothing (NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-93896921344/TI_Info.hs")
TRACE: unqueueForEvaluation: NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-93896921344/TI_Info.hs"
TRACE: isMemberEvaluatingVar=False(NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-93896921344/TI_Info.hs")
                                          FAIL
      Exception: Timed out waiting to receive a message from the server.
      Last message received:
      {
          "jsonrpc": "2.0",
          "method": "textDocument/publishDiagnostics",
          "params": {
              "diagnostics": [],
              "uri": "file:///private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-93896921344/TInfo.hs"
          }
      }

1 out of 1 tests failed (68.85s)


It is odd that we are not seeing the recomputation of IsEvaluating after the session is restared.

@jhrcek
Copy link
Collaborator Author

jhrcek commented Apr 22, 2024

Indeed, the "Timed out waiting to receive a message from the server." is the standard way this test fails.
The error itself doesn't appear in the test output by default - You have to enable logging of messages/server output via lsp-test's env variables to see the error printed:
LSP_TEST_LOG_MESSAGES=1 LSP_TEST_LOG_STDERR=1 cabal test ...

@soulomoon
Copy link
Collaborator

soulomoon commented Apr 22, 2024

Right, I accidentally added a line break after LSP_TEST_LOG_MESSAGES=1 LSP_TEST_LOG_STDERR=1 in my run script

After queueForEvaluation,
we should be seeing recompilation of a lot rules, so strange that we are not seeing all taces of them.
Something might be seriously wrong in our session restart.


I know why, we need to add trace right after the old session stop but before setting up a new one.
So we know wether recomputation is taking effect after the seesion have restarted.


Indeed even after the session restart, we have linkableType=Nothing and isEvaluating is not being reevaluated again.

TRACE: compNeeded=Nothing (NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-79390545335/TInfo.hs")
TRACE: compNeeded=Nothing (NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-79390545335/TProperty.hs")
TRACE: compNeeded=Nothing (NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-79390545335/TPropertyError.hs")
TRACE: compNeeded=Nothing (NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-79390545335/TInfoMany.hs")
TRACE: isMemberEvaluatingVar=False(NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-79390545335/TI_Info.hs")
TRACE: isEvaluating=False(NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-79390545335/TI_Info.hs")
TRACE: shakeRestart
TRACE: queueForEvaluation done: NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-79390545335/TI_Info.hs"
before initialiseSessionForEval: NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-79390545335/TI_Info.hs"
TRACE: linkableType=Nothing (NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-79390545335/TI_Info.hs")
TRACE: unqueueForEvaluation: NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-79390545335/TI_Info.hs"
TRACE: shakeRestart
TRACE: isMemberEvaluatingVar=False(NormalizedFilePath "/private/var/folders/36/_743psv11gv2wrj9dclrpd500000gn/T/hls-test-root/extra-dir-79390545335/TI_Info.hs")

@jhrcek
Copy link
Collaborator Author

jhrcek commented Apr 22, 2024

I see we're already logging shake session restarts, so here's the screenshot including those restarts within the failing tests in the context of other messages.
In the screenshot you can see two restarts corresponding to "initialization" and "cleanup" of this bracket:

final_hscEnv <- liftIO $ bracket_
(do queueForEvaluation st nfp
setSomethingModified VFSUnmodified st [toKey IsEvaluating nfp] "Eval")
(do unqueueForEvaluation st nfp
setSomethingModified VFSUnmodified st [toKey IsEvaluating nfp] "Eval")
(initialiseSessionForEval (needsQuickCheck tests) st nfp)

As you can see there doesn't seem to be any retriggering of eval stuff within the two restarts - just two messages that look like finishing rules that started before triggering session restart.

The restart works a bit differently than I imagined in the sense that the "currently running build actions" are simply cancelled (unless they are enqueued in which case they are requeued for later execution).
See this comment:

-- | Restart the current 'ShakeSession' with the given system actions.
-- Any actions running in the current session will be aborted,
-- but actions added via 'shakeEnqueue' will be requeued.

Screenshot from 2024-04-22 14-55-42

@soulomoon
Copy link
Collaborator

soulomoon commented Apr 22, 2024

We should be seeing the IsEvaluating being marked as dirty in the restart log:

    LogBuildSessionRestart reason actionQueue keyBackLog abortDuration shakeProfilePath ->
      vcat
        [ "Restarting build session due to" <+> pretty reason
        , "Action Queue:" <+> pretty (map actionName actionQueue)
        , "Keys:" <+> pretty (map show $ toListKeySet keyBackLog)
        , "Aborting previous build session took" <+> pretty (showDuration abortDuration) <+> pretty shakeProfilePath ]

and incDatabase db keysChanged would be used to mark the dirty keys that need to be rerun.
It seems we are not doing it. workRun is ran asyn. The action to mark dirty keys in hls-graph are being incorrectly delayed.
If I am correct, I think I found a way to fix this. We just need to mark those keys as dirty in the hls-graph synchronously
when we restart the session.

@jhrcek
Copy link
Collaborator Author

jhrcek commented Apr 22, 2024

Here is the full log of a single failing test run if you want to verify some things about it: https://gist.github.com/jhrcek/74fbd87d3d34190aef2ce0de4e99801f

@soulomoon
Copy link
Collaborator

soulomoon commented Apr 22, 2024

I am giving an PR merging into this branch.
#4190
See if this would fix the Race?

Here is the full log of a single failing test run if you want to verify some things about it: https://gist.github.com/jhrcek/74fbd87d3d34190aef2ce0de4e99801f

Great
It seems we do have the IsEvaluating key as dirty key there in the log.

Nop


I am giving 500 runs on it now.

@jhrcek
Copy link
Collaborator Author

jhrcek commented Apr 22, 2024

I got a test failure with your PR branch after 95 iterations.
Here are the logs of the failed run: https://gist.github.com/jhrcek/96cde598185cb7abf0842ef0b0a47dea

@soulomoon
Copy link
Collaborator

soulomoon commented Apr 22, 2024

I got a test failure with your PR branch after 95 iterations.

Yep, the above change won't fix the issue, now I realized. initialiseSessionForEval is already running using the shakeEnqueue, which should already pick up the IsEvaluating changed when running for rule GetLinkable in the session.

But thanx for the test logs.


Here are the logs of the failed run: https://gist.github.com/jhrcek/96cde598185cb7abf0842ef0b0a47dea

Some new finding, we are not seeing IsEvaluating in dirty keys, which it should be in. Since we are calling setSomethingModified VFSUnmodified st [toKey IsEvaluating nfp] "Eval")

ThreadId 409 | 2024-04-21T05:33:50.767537Z | Debug | Restarting build session due to Eval
Action Queue: [InitialLoad]
Keys: [ GetModificationTime; /tmp/hls-test-root/.cache/ghcide/test-0.1.0.0-inplace-a69b9349d8eb1e5391c6596f62fcfe33d189fd7b/TProperty.hi
      , GetModificationTime; /tmp/hls-test-root/.cache/ghcide/test-0.1.0.0-inplace-a69b9349d8eb1e5391c6596f62fcfe33d189fd7b/TInfoBangMany.hi
      , GetModificationTime; /tmp/hls-test-root/.cache/ghcide/test-0.1.0.0-inplace-a69b9349d8eb1e5391c6596f62fcfe33d189fd7b/TInfo.hi
      , GetModificationTime; /tmp/hls-test-root/.cache/ghcide/test-0.1.0.0-inplace-a69b9349d8eb1e5391c6596f62fcfe33d189fd7b/TInfoMany.hi
      , GetModificationTime; /tmp/hls-test-root/.cache/ghcide/test-0.1.0.0-inplace-a69b9349d8eb1e5391c6596f62fcfe33d189fd7b/TInfoBang.hi
      , GetModificationTime; /tmp/hls-test-root/.cache/ghcide/test-0.1.0.0-inplace-a69b9349d8eb1e5391c6596f62fcfe33d189fd7b/TInfo.hie
      , GetModificationTime; /tmp/hls-test-root/.cache/ghcide/test-0.1.0.0-inplace-a69b9349d8eb1e5391c6596f62fcfe33d189fd7b/TProperty.hie
      , GetModificationTime; /tmp/hls-test-root/.cache/ghcide/test-0.1.0.0-inplace-a69b9349d8eb1e5391c6596f62fcfe33d189fd7b/TInfoBangMany.hie
      , GetModificationTime; /tmp/hls-test-root/.cache/ghcide/test-0.1.0.0-inplace-a69b9349d8eb1e5391c6596f62fcfe33d189fd7b/TInfoMany.hie
      , GetModificationTime; /tmp/hls-test-root/.cache/ghcide/test-0.1.0.0-inplace-a69b9349d8eb1e5391c6596f62fcfe33d189fd7b/TInfoBang.hie
      , GetModificationTime; /tmp/hls-test-root/.cache/ghcide/test-0.1.0.0-inplace-a69b9349d8eb1e5391c6596f62fcfe33d189fd7b/TI_Info.hie ]fo.hs"]

This might be deleted right before the call to restartShakeSession in setSomethingModified(My guess is that the IsEvaluation called before the queueForEvaluation, but end right before the restartShakeSession, hence remove itself from the dirty key set),
I will do a test after passing the keys of setSomethingModified directly to restartShakeSession, so it will not get removed.
done here:
#4190

@soulomoon
Copy link
Collaborator

soulomoon commented Apr 23, 2024

For the above change, after doing 500 runs, the error does not appear locally.


i think marking the keys as dirty while the session still running in general would have this race condition.
Since dirty keys are not flush to hls-graph db right away, but caching in shakeExtra.

In broader scale, we maintain dirtiness both in shakeExtra and the shakeDB, and race conditions grows from the gap.

I am thinking about refactor this part of the code to prevent such problem once and for all.

@jhrcek jhrcek force-pushed the jan/characterize-eval-plugin-race branch from e426e76 to 6216417 Compare April 24, 2024 05:17
@jhrcek
Copy link
Collaborator Author

jhrcek commented Apr 24, 2024

Updated based on lateset findings from @soulomoon
added few more traces to demonstrate that IsEvaluating key is removed from dirty key set before restart of shake session happens. CC @wz1000

Screenshot from 2024-04-24 07-10-43

soulomoon added a commit that referenced this pull request May 10, 2024
…nd rule values [flaky test #4185 #4093] (#4190)

The main problem is the out of sync state in the build system. Several states involved, the shake database running result state for key. shake extra's dirty state for key and shake extra's rule values state.
To stablize the build system. This PR force some of the updates of these state into a single STM block.

1. collect dirtykeys and ship it to session restart directly. No more invalid removal before session restart. Fixing Flaky test failure result in error of GetLinkable #4093
2. move the dirtykey removal and rules values updating to hls-graph by adding a call back fucntion to RunResult. Properly handle the dirtykeys and rule value state after session start and closely followed by another session restart Fixing ghcide-tests' addDependentFile test #4194
3. properly handle clean up by wrapping the asyncWithCleanUp to refreshDeps

---------

Co-authored-by: wz1000 <zubin.duggal@gmail.com>
Co-authored-by: Jan Hrcek <2716069+jhrcek@users.noreply.github.com>
Co-authored-by: Michael Peyton Jones <me@michaelpj.com>
@jhrcek
Copy link
Collaborator Author

jhrcek commented May 11, 2024

No longer needed as the issue has been fixed in #4190.
Thanks @soulomoon!

@jhrcek jhrcek closed this May 11, 2024
@jhrcek jhrcek deleted the jan/characterize-eval-plugin-race branch May 11, 2024 04:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants