.. currentmodule:: Base

****************************************************
Proper maintenance and care of multi-threading locks
****************************************************

The following strategies are used to ensure that the code is dead-lock free
(generally by addressing the 4th Coffman condition: circular wait).

 1. structure code such that only one lock will need to be acquired at a time

 2. always acquire shared locks in the same order, as given by the table below

 3. avoid constructs that expect to need unrestricted recursion


Locks
-----

Below are all of the locks that exist in the system and the mechanisms
for using them that avoid the potential for deadlocks (no Ostrich algorithm allowed here):

The following are definitely leaf locks (level 1), and must not try to acquire any other lock:

   * safepoint

       Note that this lock is acquired implicitly by ``JL_LOCK`` and ``JL_UNLOCK``.
       use the ``_NOGC`` variants to avoid that for level 1 locks.

       While holding this lock, the code must not do any allocation or hit any safepoints.
       Note that there are safepoints when doing allocation, enabling / disabling GC,
       entering / restoring exception frames, and taking / releasing locks.

   * shared_map
   * finalizers
   * pagealloc
   * gc_perm_lock
   * flisp

       flisp itself is already threadsafe, this lock only protects the ``jl_ast_context_list_t`` pool


The following is a leaf lock (level 2), and only acquires level 1 locks (safepoint) internally:

   * typecache

The following is a level 3 lock, which can only acquire level 1 or level 2 locks internally:

   * Method->writelock

     but note that this is violated by staged functions!

The following is a level 4 lock, which can only recurse to acquire level 1, 2, or 3 locks:

   * MethodTable->writelock

     but note that this is violated by staged functions!

The following is a proposed level 5 lock, which can only recurse to acquire locks at lower levels:

   * staged

       this theoretical lock would create a priority inversion from the `method->writelock` (level 3),
       but only prohibiting running any staging function in parallel
       (thus allowing temporary release of the MethodTable and Method locks)

The following is a level 6 lock, which can only recurse to acquire locks at lower levels:

   * codegen

The following is an almost root lock (level end-1), meaning only the root look may be held when trying to acquire it:

   * typeinf

       this one is perhaps one of the most tricky ones, since type-inference can be invoked from many points

The following is the root lock, meaning no other lock shall be held when trying to acquire it:

   * toplevel

       this should be held while attempting a top-level action (such as making a new type or defining a new method):
       trying to obtain this lock inside a staged function will cause a deadlock condition!

       additionally, it's unclear if *any* code can safely run in parallel with an arbitrary toplevel expression,
       so it may require all threads to get to a safepoint first

Broken Locks
------------

The following locks are broken:

* toplevel

    doesn't exist right now

    fix: create it

* codegen

    recursive (through ``static_eval``), but caller might also be holding locks (due to staged functions)

    other issues?

    fix: prohibit codegen while holding any other lock (possibly by checking ``ptls->current_task->locks.len != 0`` & explicitly check the locks that are OK to hold simultaneously)?

* typeinf

    not certain of whether there are issues here or what they are. staging functions, of course, are a source of deadlocks here.

    fix: unknown

* staged

    possible solution to prevent staged functions from causing deadlock.

    this theoretical lock would create a priority inversion such that the Method and MethodTable write locks could be released
    by ensuring that no staging functions can run in parallel allow this level 5 lock to protect staged function conflicts (a level 3 operation)

    fix: create it


Shared Global Data Structures
-----------------------------

These data structures each need locks due to being shared mutable global state.
It is the inverse list for the above lock priority list.
This list does not include level 1 leaf resources due to their simplicity.

MethodTable modifications (def, cache, kwsorter type) : MethodTable->writelock

Type declarations : toplevel lock

Type application : typecache lock

Module serializer : toplevel lock

JIT : codegen lock

LLVMContext : codegen lock

Method : Method->writelock
    - roots array (serializer and codegen)
    - invoke / specializations / tfunc modifications
