From 7592b71887b7fad0dab5d0a1cef58bfebcb0138e Mon Sep 17 00:00:00 2001 From: lishanglin Date: Wed, 1 Feb 2023 15:02:33 +0800 Subject: [PATCH] LocalTimeR: dont call localtime_r to avoid __tz_convert deadlock --- env/env_posix.cc | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ port/sys_time.h | 4 +-- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/env/env_posix.cc b/env/env_posix.cc index 8b24a7a2788..07e69febceb 100644 --- a/env/env_posix.cc +++ b/env/env_posix.cc @@ -96,6 +96,87 @@ static const std::string kSharedLibExt = ".so"; #endif #endif +namespace port { + +static int is_leap_year(time_t year) { + if (year % 4) + return 0; /* A year not divisible by 4 is not leap. */ + else if (year % 100) + return 1; /* If div by 4 and not 100 is surely leap. */ + else if (year % 400) + return 0; /* If div by 100 *and* 400 is not leap. */ + else + return 1; /* If div by 100 and not by 400 is leap. */ +} + +static int g_daylight_active = [] { + tzset(); // Now 'timezome' global is populated. + time_t t = time(NULL); + struct tm* aux = localtime(&t); // safe in global cons + return aux->tm_isdst; +}(); + +void nolocks_localtime(struct tm* tmp, time_t t, time_t tz, int dst) { + const int secs_min = 60; + const int secs_hour = 3600; + const int secs_day = 3600 * 24; + + t -= tz; /* Adjust for timezone. */ + t += 3600 * dst; /* Adjust for daylight time. */ + int days = int(t / secs_day); /* Days passed since epoch. */ + int seconds = int(t % secs_day); /* Remaining seconds. */ + + tmp->tm_isdst = dst; + tmp->tm_hour = seconds / secs_hour; + tmp->tm_min = (seconds % secs_hour) / secs_min; + tmp->tm_sec = (seconds % secs_hour) % secs_min; + + /* 1/1/1970 was a Thursday, that is, day 4 from the POV of the tm structure + * where sunday = 0, so to calculate the day of the week we have to add 4 + * and take the modulo by 7. */ + tmp->tm_wday = (days + 4) % 7; + + /* Calculate the current year. */ + tmp->tm_year = 1970; + while (1) { + /* Leap years have one day more. */ + int days_this_year = 365 + is_leap_year(tmp->tm_year); + if (days_this_year > days) break; + days -= days_this_year; + tmp->tm_year++; + } + tmp->tm_yday = days; /* Number of day of the current year. */ + /* We need to calculate in which month and day of the month we are. To do + * so we need to skip days according to how many days there are in each + * month, and adjust for the leap year that has one more day in February. */ + int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + mdays[1] += is_leap_year(tmp->tm_year); + + tmp->tm_mon = 0; + while (days >= mdays[tmp->tm_mon]) { + days -= mdays[tmp->tm_mon]; + tmp->tm_mon++; + } + + tmp->tm_mday = days + 1; /* Add 1 since our 'days' is zero-based. */ + tmp->tm_year -= 1900; /* Surprisingly tm_year is year-1900. */ +} + +void nolocks_localtime(struct tm* tmp, time_t t, time_t tz) { + return nolocks_localtime(tmp, t, tz, g_daylight_active); +} + +void nolocks_localtime(struct tm* tmp, time_t t) { + return nolocks_localtime(tmp, t, timezone, g_daylight_active); +} + +struct tm* LocalTimeR(const time_t* timep, struct tm* result) { + nolocks_localtime(result, *timep); + return result; +} + +} // namespace port + namespace { ThreadStatusUpdater* CreateThreadStatusUpdater() { diff --git a/port/sys_time.h b/port/sys_time.h index f2137526b13..48aad986aa3 100644 --- a/port/sys_time.h +++ b/port/sys_time.h @@ -52,9 +52,7 @@ inline void GetTimeOfDay(TimeVal* tv, struct timezone* tz) { gettimeofday(tv, tz); } -inline struct tm* LocalTimeR(const time_t* timep, struct tm* result) { - return localtime_r(timep, result); -} +struct tm* LocalTimeR(const time_t* timep, struct tm* result); } // namespace port