Engee 文档

多线程锁的正确维护

以下策略确保代码中没有死锁(通常通过观察第4个Coffman条件:循环等待)。

  1. 代码需要以这样一种方式结构化,您一次只需要获得一个锁。

  2. 共享锁应始终按照下表所示的相同顺序获取。

  3. 有必要避免预期需要无限递归的构造。

堵塞物

以下是系统中存在的所有锁,以及它们用于避免潜在死锁的机制(Straus算法在这里是不可接受的)。

下面的锁绝对是最终锁(第一级),不应该尝试获取任何其他锁。

  • 安全点

    请注意,此锁由`JL_LOCK`和`JL_UNLOCK’隐式获取。 使用`_NOGC’选项来消除1级锁的这种情况。

    持有此锁时,代码不应执行任何分配或到达安全点。 请注意,在分配、启用或禁用垃圾回收、输入或恢复异常帧以及接受或释放锁时都有安全点。

  • shared_map的

  • 终结者

  • pagealloc

  • gc_perm_lock

  • flisp的

  • jl_in_stackwalk(Win32)

  • ResourcePool<?>::互斥锁

  • RLST_mutex

  • llvm打印互斥

  • jllockedstream::互斥锁

  • debuginfo_asyncsafe

  • 推理timingmutex

  • 执行引擎::会话锁

    flisp本身已经是线程安全的。 此锁仅保护池’jl_ast_context_list_t'。 同样,ResourcePool<?>::互斥锁仅保护关联的资源池。

下面是最终的锁(第二级),它内部只接收第一级(安全点)的锁。

  • 全局

  • 模块->锁

  • JLDebuginfoPlugin::PluginMutex

  • 推断互斥

下面是一个3级锁,只能在内部接收1级或2级锁。

  • 方法->writelock

  • 打字机,打字机

下面是一个4级锁,它只能递归地接收1级、2级或3级锁。

  • 方法表->写锁

当锁在这一点上方时,Julia代码不能被调用。

orc::ThreadSafeContext(TSCtx)锁在锁层次结构中占有特殊的位置。 它们用于保护LLVM的全局非线程安全状态,但可以有任意数量的它们。 默认情况下,与层次结构的其余部分相比,所有这些锁都可以被视为5级锁。 您应该仅从TSCtx JIT池接收TSCtx,并且必须在该tsctx返回池之前解除该tsctx上的所有锁定。 如果需要在同一时间获取多个TSCtx锁(由于递归编译),则应按照从池中获取TSCtx锁的相同顺序获取它们。

以下是5级锁定:

  • JuliaOJIT::EmissionMutex

下面是一个6级锁,可以递归地只接收较低级别的锁。

  • 科德根

  • jl_modules_mutex

下一个锁几乎是根锁(倒数第二级),这意味着在尝试获取它时只能持有根锁。

  • n.打字,打字

    此选项可能是最困难的选项之一,因为类型推断可以从许多点调用。

    目前,此锁与代码生成锁相结合,因为它们以递归方式相互调用。

下一个锁同步I/O操作。 请记住,在保持上面列出的任何其他锁的同时执行任何I/O操作(例如,输出警告消息或调试信息)都可能导致危险且难以检测的死锁。 要非常小心!

  • 伊奥洛克

  • 单独的ThreadSynchronizers锁

    您可以在释放iolock锁后继续持有它们或在没有它的情况下接收它们,但要非常小心,不要试图在持有这些锁时获得iolock锁。

  • 阻止Libdl。[医]拉齐奥图书馆

下一个锁是根锁,这意味着在尝试获取它时不能持有其他锁。

  • 最高层

    当尝试执行顶级操作(例如,创建新类型或定义新方法)时,应持有此锁:尝试在中间函数中获取此锁将导致死锁条件。

    此外,还不清楚是否可以安全地与任意顶级表达式并行执行任何代码。 因此,可能需要所有线程首先到达安全点。

破锁

以下锁不起作用。

  • 最高层

    现在不存在>>修复:创建它。

  • 模块->锁

    它很容易受到死锁的影响,因为无法确定它是按顺序接收的。 >某些操作(例如,'import_module')没有锁。 >>修复:替换’jl_modules_mutex'?

  • 装货。jl’require’和’register_root_module`

    这个文件可能有很多问题。 修复:需要锁。

通用全局数据结构

每个这样的数据结构都需要锁,因为它们共享一个可变的全局状态。 下面是上述锁定优先级列表的反向列表。 它不包括1st级别的最终资源,因为它们太简单了。

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

类型声明:顶级锁

类型的应用:阻塞typecache

全局变量表:模块->锁

模块序列化器:顶级锁

JIT和类型推断:阻塞代码生成

MethodInstance/CodeInstance的更新:Method->writelock,阻止代码生成

  • 这些是在创建时设置的,并且是不可变的。: 眼镜 sparam_vals def 业主

  • 这些是使用`jl_type_infer’设置的(同时持有代码生成锁): 缓存 rettype **推断*可接受年龄

  • 'InInference’标志: 优化,以便在`jl_type_infer`函数已经运行时快速防止重复 实际状态("推断"安装,然后是"fptr")由代码生成锁保护

  • 函数指针: **在代码生成锁被保持的情况下,执行一次从"NULL"到值的转换。

  • 代码生成器缓存(functionObjectsDecls’的内容): 可以跳转几次,但只有在代码生成锁被持有时才会跳转 您可以使用其旧版本或阻止新版本,因此竞赛并不危险,除非代码引用方法实例中的其他数据(例如,`rettype)并假设它们是一致的,除非它持有代码生成锁。

LLVMContext:阻塞代码生成

方法:方法->writelock

  • 根数组(序列化器和代码生成)

  • TFUNC挑战/专业化/修改