Forum OpenACS Development: localtime_s

Request notifications

Collapse
Posted by Maurizio Martignano on

Dear all,

please notice that in the file reentrant.c the following lines

    int errNum;

    NS_NONNULL_ASSERT(clock != NULL);

    errNum = localtime_s(&tlsPtr->ltbuf, clock);
    if (errNum != 0) {
        NsThreadFatal("ns_localtime", "localtime_s", errNum);
    }

when compiled with MS VC 2019 x64, do compile but break at run time, with errno = 22, while the following lines

    Tls *tlsPtr = GetTls();
    errno_t errNum;

    NS_NONNULL_ASSERT(clock != NULL);
 
    errNum = _localtime32_s(&tlsPtr->ltbuf, clock);
    if (errNum != 0) {
        NsThreadFatal("ns_localtime", "_localtime32_s", errNum);
    }

though generating a warning at compile time, do not break at run time.

Hope it helps, Maurizio

Collapse
2: Re: localtime_s (response to 1)
Posted by Gustaf Neumann on
Maurizio,

many thanks for reporting. I wonder, why this did not show up earlier, since the usage of localtime_s with time_t was there at least since 5 years.

I've addressed this issue via the change [1]. Please double check.

-g
PS: the NaviServer developer list is a better place to post this.
[1] https://bitbucket.org/naviserver/naviserver/commits/ce82fd694aaac0dfd316ac658f4481f27f3d92c4

Collapse
3: Re: localtime_s (response to 2)
Posted by Maurizio Martignano on
Dear Gustaf,
thank you for fixing the code!

Trying to answer your question, I believe that yes the problem was there, hidden in the code, but that it actually got exposed only recently (24th of January) when in the file winthread.c the following two lines

wait.usec /= 1000;
if (wait.sec < 0 || (wait.sec == 0 && wait.usec <= 0)) {

got changed into

msec = Ns_TimeToMilliseconds(&wait);
if (msec < 0) {

So the function Ns_TimeToMilliseconds got called and the problem exposed.

Again thank you,
Maurizio

Collapse
4: Re: localtime_s (response to 3)
Posted by Gustaf Neumann on
Thanks, this explains it.
Collapse
5: Re: localtime_s (response to 2)
Posted by Maurizio Martignano on
Dear Gustaf,
first of all sorry if I continue this discussion where I started it. Next similar issue will be published on Naviserver developer list. Anyhow I had to change your fix into this:
errNum = _localtime32_s(&tlsPtr->ltbuf, clock);
/*
if (sizeof(clock) == 4) {
errNum = _localtime32_s(&tlsPtr->ltbuf, clock);
} else {
errNum = _localtime64_s(&tlsPtr->ltbuf, clock);
}
*/
Why? Because the parameter const time_t *clock comes/goes from/to the "sec" field of variables of type Ns_Time, that is:
typedef struct Ns_Time {
long sec;
long usec;
} Ns_Time;
So, basically, we are mixing up "long" with "time_t". But these two types do not have the same size in Windows 64, where, we have:
sizeof(long) = 4.
sizeof(time_t) = 8.
sizeof(__time32_t) = 4.
while on Linux 64, we have:
sizeof(long) = 8.
sizeof(time_t) = 8.
I hope this clarifies the issue,
Maurizio
Collapse
6: Re: localtime_s (response to 1)
Posted by Gustaf Neumann on
Maurizio, the diagnosis is right, but the cure is not.

ns_localtime() is supposed to be interface compatible with Linux's localtime(3), and receives therefore as argument (const time_t *timep); the function was called in all but one place with the proper type in NaviServer, in one case (in the file sched.c) the long value of Ns_Time.sec was passed incorrectly to it (the compiler should have complained about this).

The updated version in the repository should be fine now with win32 and win64.

Collapse
7: Re: localtime_s (response to 6)
Posted by Maurizio Martignano on
Thank you Gustaf,
once the bug in sched.c is removed, you can come back to the original implementation of ns_localtime, leaving the code cleaner:
struct tm *
ns_localtime(const time_t *timep)
{

Tls *tlsPtr = GetTls();
int errNum;

NS_NONNULL_ASSERT(timep != NULL);

errNum = localtime_s(&tlsPtr->ltbuf, timep);
if (errNum != 0) {
NsThreadFatal("ns_localtime", "localtime_s", errNum);
}

return &tlsPtr->ltbuf;
}
Again thank you,
Maurizio