zoukankan      html  css  js  c++  java
  • lua的bug:lua的os.date()在多线程下的问题

    lua的os.date()在多线程下的问题

    我使用的lua版本是5.1.2,其他版本估计也有这个问题。
    lua的os.date()函数在多线程下存在问题,即使是每个线程都是独立的Lua_State.
    原因:
    lua的loslib.c中,对os.date函数的实现采用了localtime和gmtime这两个函数,而这两个函数都是非线程安全的,这意味着在多线程下使用这两个函数有可能导致取时间错误.
    所以无论如何,在多线程下调用os.date()都含有安全隐患.
    例如,在线程A中有这样的代码:
    local t = os.time() -24*3600
    local st1 = os.date("%Y%m%d", t)
    线程B中:
    local t = os.time()
    local st2 = os.date("%Y%m%d", t)

    这样,上面两份代码在不同线程频繁调用的情况下就有可能出现错误,st2得到的结果未必是当前时间,而是一天前,而st1的结果页未必是一天前,而有可能是当前时间,因为结果被后一次的调用覆盖了。

    修复方案:
    1.自己写个lua扩展,直接拷贝原来loslib.c中os_date的实现,将其中调用localtime和gmtime的地方改为线程安全的localtime_r 和gmtime_r即可
    2.修改lua源码,将localtime和gmtime的地方改为线程安全的localtime_r 和gmtime_r。
    这个应该算是lua的一个bug了。

    附上修正的os_date源代码:

    static void setfield (lua_State *L, const char *key, int value) {
      lua_pushinteger(L, value);
      lua_setfield(L, -2, key);
    }
    
    static void setboolfield (lua_State *L, const char *key, int value) {
      if (value < 0)  /* undefined? */
        return;  /* does not set field */
      lua_pushboolean(L, value);
      lua_setfield(L, -2, key);
    }
    
    static int os_date (lua_State *L) {
      const char *s = luaL_optstring(L, 1, "%c");
      time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
      struct tm tmx;
      struct tm *stm = &tmx;
      char buf[512];
      FILE* f = NULL;
      int n  =lua_tonumber(L, 3);
      if (n == 1) 
      {
          f = fopen("c:\\osdate.txt", "a+");
          if (!f) {
              printf("open file osdate.txt error");
          }
          else{
              sprintf(buf, "now:%d, s=%s, t=%d, result=", time(NULL), s, t);
              fwrite(buf, strlen(buf), 1, f);
          }
      }
    
    
    
    
      if (*s == '!') {  /* UTC? */
        gmtime_r(&t, stm);
        s++;  /* skip `!' */
      }
      else
        localtime_r(&t, stm);
      if (stm == NULL)  /* invalid date? */
        lua_pushnil(L);
      else if (strcmp(s, "*t") == 0) {
        lua_createtable(L, 0, 9);  /* 9 = number of fields */
        setfield(L, "sec", stm->tm_sec);
        setfield(L, "min", stm->tm_min);
        setfield(L, "hour", stm->tm_hour);
        setfield(L, "day", stm->tm_mday);
        setfield(L, "month", stm->tm_mon+1);
        setfield(L, "year", stm->tm_year+1900);
        setfield(L, "wday", stm->tm_wday+1);
        setfield(L, "yday", stm->tm_yday+1);
        setboolfield(L, "isdst", stm->tm_isdst);
      }
      else {
        char cc[3];
        luaL_Buffer b;
        cc[0] = '%'; cc[2] = '\0';
        luaL_buffinit(L, &b);
        for (; *s; s++) {
          if (*s != '%' || *(s + 1) == '\0')  /* no conversion specifier? */
            luaL_addchar(&b, *s);
          else {
            size_t reslen;
            char buff[200];  /* should be big enough for any conversion result */
            cc[1] = *(++s);
            reslen = strftime(buff, sizeof(buff), cc, stm);
            luaL_addlstring(&b, buff, reslen);
          }
        }
        if (n==1) {
            fwrite(b.buffer, b.p - b.buffer, 1, f);
            fwrite("\n", 1,1,f);
        }
        luaL_pushresult(&b);
      }
    
      if (n==1 && f) {
          fclose(f);
          f = NULL;
      }
      return 1;
    }
  • 相关阅读:
    python 包管理工具 pip 的配置
    Python 变量作用域 LEGB (下)—— Enclosing function locals
    Python 变量作用域 LEGB (上)—— Local,Global,Builtin
    2020 Java 面试题 小结 (答案慢慢补上,有错误请指出)
    mysql 根据日期(date)做年,月,日分组统计查询
    jvm指令
    正则表达式 分割地址 获取省市区详细地址
    .Net 异常记录
    WCF设计服务协议(一)
    plsql ORA-01789:查询块具有不正确的结果列数
  • 原文地址:https://www.cnblogs.com/moodlxs/p/2795016.html
Copyright © 2011-2022 走看看