如果lua被信号打断会怎么样?

时间:2014-03-06 22:15:13

标签: c lua signals posix

Lua docs说:

  

Lua库根本没有定义全局变量。它将其所有状态保存在动态结构lua_State中,并指向此状态   结构作为参数传递给Lua中的所有函数。这个   实现使Lua可以重入并准备好使用   多线程代码。

但这是真的吗?有没有人试图验证这个?如果在执行lua期间由自定义处理程序引发并捕获信号该怎么办?也就是说,lua本身(从不介意系统调用它)真正可以重入吗?

编辑:

lua中一个众所周知的问题是缺少计时器实现。这些可以使用POSIX定时器来实现,这些定时器会产生信号。但是提出这样的信号可能会中断lua本身的执行。解决这个问题的规范解决方案是屏蔽/取消屏蔽信号,但如果lua真正重入,则不需要这样做。

2 个答案:

答案 0 :(得分:3)

lua确实将所有变量保存在lua_State中。如果发生信号,该信号将在C中处理。您无法从信号处理程序安全地调用lua,就像您无法从信号处理程序中调用某些线程安全函数一样。

文档说的是,如果你有不同的lua_State变量的线程,他们可以安全地运行lua而不需要在它们之间进行同步。

答案 1 :(得分:3)

AFAICT,re-entrancy是一个单线程概念,有点独立于多线程。多线程安全涉及并发读/写共享数据时的数据一致性,而重入涉及一个线程内的函数前/后信号的状态一致性。

函数可以是多线程安全的,也可以不是。中间没有。然而,就重新入职而言,并非如此简单:在某种情况下,某种功能是可重入的,而在某种条件下,这种功能并非如此;对于某些功能,没有条件可以重新进入。我不是计算机科学家,但我的猜测是,在所有条件下都可以重入的函数很少(如果有的话)。像void f() {}一样,但它不是很有用:)

以下情况可能属实:

  1. 函数可重入的必要条件是它不能使用任何可以从外部设置的静态或全局数据或数据(例如寄存器或DMA)。
  2. 重新进入的另一个必要条件是该函数仅调用可重入函数。在这种情况下,函数是可重入的,并且所谓的函数被认为是可重入的所有条件的总和。因此,如果A调用B和C,并且如果条件b为真,则B是可重入的,并且如果条件c为真,则C是重入的,则A重新进入的必要条件是条件b和c必须是真正。
  3. 如果1和2为真且信号处理程序不直接或间接调用具有相同参数的函数,则只接受至少一个参数的函数才是可重入的。
  4. API以与其全部功能相同的方式重入。这意味着在某些特定条件下(1-3),可能只有一部分API可以说是可重入的,而其他功能不是可重入的。这并不意味着API不可重入;只是在某些条件下它的一部分是可重入的。
  5. 如果上述内容是正确的,那么在询问(或说明)Lua是否可重入时,您必须更具体,在什么条件下询问Lua函数的哪个子集是可重入的。显然所有的Lua函数都满足1,但哪些满足2?几乎所有的Lua API函数都接受至少一个参数,所以在你的信号处理程序不直接或间接调用具有相同Lua状态变量的相同Lua函数的情况下,你可以说Lua可以重新进入那些函数#39; t调用非重入函数。

    更新1:为什么条件3:

    考虑

    void f(const Foo& foo) {
       if (foo.bar) 
           do stuff 
       signal happens here, calling isr()
       modify fo
    }
    
    Foo* isrFoo; 
    
    void g() {
       Foo foo; 
       isrFoo = & foo; 
       f(foo)
    }
    
    void isr() {
       f(*isrFoo)
    }
    

    尽管f(const Foo&)不使用全局变量或静态变量(尽管严格来说它并不知道a是否是对这样的变量的引用),但是接收的对象可以被多个对象共享,因此,在isr()中,可以修改,以便在f()恢复时,foo不再与中断时相同。可以说f()是可重入的(在单线程中)但是isr()在这里干扰,使得f()在该特定情况下不可重入。假设对象副本操作可以是原子的,如果将f()复制到isr()的局部变量中,foo即使f的特定设计也可以重新进入isr()。在使用之前,或foo制作本地副本,或function checkTimerFlagSet() if flag then ... end ... do stuff ... if flag then ... end 是值传递。

    更新2:俄罗斯轮盘赌

    俄罗斯轮盘赌是一场机会游戏。所以不,重新入门不是机会游戏:鉴于上述情况,手册基本上说如果你的信号处理程序没有调用(直接或间接)Lua C API函数,那么你可以考虑重新进入Lua C API函数因为API的设计和实现方式。

    例如,如果你有一个每100毫秒滴答(信号)的定时器,但是处理程序只是将标志设置为真为"尽快做某事",并且你的代码无休止地循环,调用Lua函数(通过lua_pcall)在每次迭代时,为了检查标志,你不应该有任何问题:如果在检查标志之前Lua函数被定时器中断,标志将被设置,然后从信号返回时flag将被视为true,您的函数将按照设计采取行动。

    但是,如果你不小心,你的Lua函数(不是调用它的C API)可能不是可重入的,因此在调用你的Lua函数时会导致lua_pcall不可重入。例如,如果你的Lua函数(通过lua_pcall调用)在两个地方检查标志:

    {{1}}

    并且在两次检查之间发生定时器信号,然后在相同的函数调用期间,标志在信号之前可以被视为假,而在之后可以被视为真,这可能导致您的Lua函数的行为不一致。但这只是规则#1没有被你的函数遵循(没有选择,因为你的信号处理程序只能设置全局变量),而不是Lua C API:这个"坏" (即,非重入)设计Lua函数是导致其中一个Lua C API函数(lua_pcall)不再可重入的原因。否则它是重入的。