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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM