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

[mono] Fix deadlock in static constructor initialization #93875

Merged
merged 6 commits into from
Oct 25, 2023

Commits on Oct 23, 2023

  1. [mono] Fix deadlock in static constructor initialization

    If two threads (A and B) need to call the static constructors for 3
    classes X, Y, Z in this order:
    
    Thread A: X, Z, Y
    Thread B: Y, Z, X
    
    where the cctor for X in thread A invokes the cctor for Z and for Y, and the
    cctor for Y in thread B invokes the cctor for Z and X, then we can get
    into a situation where A and B both start the cctors for X and Y (so
    they will be in the type_initialization_hash for those two classes)
    and then both will try to init Z.  In that case it could be that A
    will be responsible for initializing Z and B will block.  Then A could
    finish initializing Z but B may not have woken up yet (and so it will
    be in blocked_thread_hash waiting for Z).  At that point A (who is at
    this point already need to init Y) may think that it can wait for B to
    finish initializing Y.  That is we get to
    `mono_coop_cond_timedwait (&lock->cond, &lock->mutex, timeout_ms)`
    with "lock" being the lock for `Y`.  But in fact thread B will not be
    able to complete initializing Y because it will attempt to init X next
    - but meanwhile we did not indicate that thread A is blocked.
    As a result in thread A the timed wait will eventually timeout.  And
    in this case we need to go back to the top and now correctly detect
    that A is waiting for Y and B is waiting for X.  (At that point there
    is a cctor deadlock and ECMA rules allow one of the threads to return
    without calling the cctor)
    
    The old code here used to do an infinite wait:
      while (!lock->done)
        mono_coop_cond_wait (&lock->cond, &lock->mutex)
    
    This cannot succeed because "lock" (in thread A it's the lock for Y)
    will not be signaled since B (who is supposed to init Y) will instead
    block on the cctor for X.
    
    Fixes dotnet#93778
    lambdageek committed Oct 23, 2023
    Configuration menu
    Copy the full SHA
    8726f51 View commit details
    Browse the repository at this point in the history
  2. Add test case

    lambdageek committed Oct 23, 2023
    Configuration menu
    Copy the full SHA
    2194668 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    ddebe93 View commit details
    Browse the repository at this point in the history

Commits on Oct 24, 2023

  1. disable mt test on wasm

    lambdageek committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    5e4207d View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    1f55c2b View commit details
    Browse the repository at this point in the history

Commits on Oct 25, 2023

  1. code review feedback

    lambdageek committed Oct 25, 2023
    Configuration menu
    Copy the full SHA
    56a260d View commit details
    Browse the repository at this point in the history