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

Are static constructors thread-safe? #10243

Closed
michael-hawker opened this issue Jan 30, 2019 — with docs.microsoft.com · 6 comments · Fixed by #23562
Closed

Are static constructors thread-safe? #10243

michael-hawker opened this issue Jan 30, 2019 — with docs.microsoft.com · 6 comments · Fixed by #23562

Comments

Copy link

Would be nice if this article could call out thread-safety aspects of static constructors. Thanks!


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

@BillWagner
Copy link
Member

/cc @jcouv to check this answer.

My interpretation of the C# spec section on static constructors, it states:

The static constructor for a closed class type executes at most once in a given application domain.

That would seem to imply that only thread will invoke the static constructor.

However, there are no restrictions on the code you write inside a static constructor. You could write code that starts new threads in the constructor. That would require your own thread synchronization.

@michael-hawker
Copy link
Author

Yeah, this is a similar discussion #10244, there's always unsafe things you can do anywhere in code, but it's always nice to know when you don't need an extra layer of mutexing around things or not or can use the inherit constructs in the language. :)

@davidwrighton
Copy link
Member

I'm not quite sure how you want to express this in this documentation, but C# static class constructors are thread-safe, and executed under a lock that is specific to the exact specific type of the class. (This is important for generics, i.e. the List static class constructor executes under a different lock than the List static class constructor.) Adding additional mutexes is both non-necessary, and possibly a mechanism to cause deadlock issues.

The precise behavior of the thread-safety of class constructors can be found in sections 10.5.3.1 and 10.5.3.3 in partition II of the ECMA 335 standard. See http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf

Here is a short, somewhat more easily readable description of the special behavior of the class constructor lock. For exact details see section 105.3.3 as referenced above.

This lock is a deadlock aware lock that will not deadlock if the class constructor for type A depends on the class constructor for type B, which in turn depends on the class constructor for type A. In that case, the class constructor lock will allow the class constructor for type B to observe the partially uninitialized state of type A, and would also allow type A to observe the partially initialized state of type B if initialization started with type B. This deadlock avoidance mechanism is robust, and works even in cases where the initialization of types A and B started on separate threads, but reasoning about the correctness of such class constructors is difficult, such that it is recommended that circular references in static constructors are to be avoided as a matter of good software engineering practice.

Standard thread synchronization primitives such as monitors, semaphores, mutexes, etc, do not participate in the deadlock avoidance scheme, and it is possible to build a deadlock in their presence, if they are not used in a safe manner.

@BillWagner BillWagner added P2 and removed ⌚ Not Triaged Not triaged labels Feb 26, 2019
@BillWagner
Copy link
Member

I'll propose the following text based on the above comments:

The runtime calls a static constructor no more than once in a single application domain. Furthermore, that call is made in a locked region based on the specific type of the class. No additional locking mechanisms are needed in the body of a static constructor.

Please vote up or down, with additional suggestions.

@BillWagner BillWagner added this to the Backlog milestone Feb 26, 2019
@EugeneKramer
Copy link
Contributor

As it's executed under a lock, might also be worth noting what to avoid:

Static constructor deadlocks:

So, to avoid the risk of deadlocks, avoid blocking the current thread in static constructors and initializers: don’t wait on tasks, threads, wait handles or events, don’t acquire locks, and don’t execute blocking parallel operations like parallel loops, Parallel.Invoke and PLINQ queries.

@dotnet-bot dotnet-bot added the won't fix Issues that were closed as part of automated backlog grooming label Jan 25, 2021
@dotnet-bot
Copy link
Contributor

This issue has been closed as part of the issue backlog grooming process outlined in #22351.

That automated process may have closed some issues that should be addressed. If you think this is one of them, reopen it with a comment explaining why. Tag the @dotnet/docs team for visibility.

@tdykstra tdykstra reopened this Mar 30, 2021
@dotnet-bot dotnet-bot removed the won't fix Issues that were closed as part of automated backlog grooming label Mar 30, 2021
@tdykstra tdykstra self-assigned this Mar 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants