Sophie

Sophie

distrib > Mandriva > 2009.1 > x86_64 > media > main-backports > by-pkgid > ec081eb1f0fb87b7640153d3a3340fac > files > 157

mono-doc-2.4.2.2-1mdv2009.1.x86_64.rpm


1. Thread safety of metadata structures
----------------------------------------

1.1 Synchronization of read-only data
-------------------------------------

Read-only data is data which is not modified after creation, like the
actual binary metadata in the metadata tables.

There are three kinds of threads with regards to read-only data:
- readers
- the creator of the data
- the destroyer of the data

Most threads are readers.

- synchronization between readers is not neccesary
- synchronization between the writers is done using locks.
- synchronization between the readers and the creator is done by not exposing
  the data to readers before it is fully constructed.
- synchronization between the readers and the destroyer: TBD.

1.2 Deadlock prevention plan
----------------------------

Hold locks for the shortest time possible. Avoid calling functions inside 
locks which might obtain global locks (i.e. locks known outside this module).

1.3 Locks
----------

1.3.1 Simple locks
------------------

 There are a lot of global data structures which can be protected by a 'simple' lock. Simple means:
    - the lock protects only this data structure or it only protects the data structures in a given C module.
      An example would be the appdomains list in domain.c
    - the lock is only held for a short amount of time, and no other lock is acquired inside this simple lock. Thus there is
      no possibility of deadlock.

1.3.2 The class loader lock
---------------------------

This locks is held by the class loading routines in class.c and loader.c. It
protects the various caches inside MonoImage which are used by these modules.

1.3.3 The domain lock
---------------------

Each appdomain has a lock which protects the per-domain data structures.

1.3.4 The locking hierarchy
---------------------------

It is useful to model locks by a locking hierarchy, which is a relation between locks, which is reflexive, transitive,
and antisymmetric, in other words, a lattice. If a thread wants to acquire a lock B, while already holding A, it can only
do it if A < B. If all threads work this way, then no deadlocks can occur.

Our locking hierarchy so far looks like this:
    <DOMAIN LOCK>
        \
	<CLASS LOADER LOCK>
		\			\
	<SIMPLE LOCK 1> 	<SIMPLE LOCK 2>

1.4 Notes
----------

Some common scenarios:
- if a function needs to access a data structure, then it should lock it itself, and do not count on its caller locking it.
  So for example, the image->class_cache hash table would be locked by mono_class_get().

- there are lots of places where a runtime data structure is created and stored in a cache. In these places, care must be 
  taken to avoid multiple threads creating the same runtime structure, for example, two threads might call mono_class_get () 
  with the same class name. There are two choices here:

	<enter mutex>
	<check that item is created>
	if (created) {
		<leave mutex>
		return item
	}
	<create item>
	<store it in cache>
	<leave mutex>

      This is the easiest solution, but it requires holding the lock for the whole time which might create a scalability 	problem, and could also lead to deadlock.

	<enter mutex>
	<check that item is created>
	<leave mutex>
	if (created) {
		return item
	}
	<create item>
	<enter mutex>
	<check that item is created>
	if (created) {
		/* Another thread already created and stored the same item */
		<free our item>
		<leave mutex>
		return orig item
	}
	else {
		<store item in cache>
		<leave mutex>
		return item
	}

	This solution does not present scalability problems, but the created item might be hard to destroy (like a MonoClass).

- lazy initialization of hashtables etc. is not thread safe