On 2025-09-23 04:16, Dag-Erling Smørgrav wrote:
I wouldn't call these differences minor, the diff in localtime.c alone
is 450+ lines and includes changes which you've rejected in the past.
Sorry about that; to be honest I'd forgotten. Let's revisit it.
A first review found a problem: the FreeBSD implementation does not
conform to the C standard or to POSIX. These standards require that
successful calls to localtime and gmtime must always return the same
pointer; see, for example:
https://pubs.opengroup.org/onlinepubs/9799919799/functions/gmtime.html
It says, "The asctime(), ctime(), gmtime(), and localtime() functions
shall return values in one of two static objects: a broken-down time
structure and an array of type char. Execution of any of the functions
that return a pointer to one of these object types may overwrite the
information in any object of the same type pointed to by the value
returned from any previous call to any of them."
FreeBSD doesn't do that: it returns a pointer to thread-local storage.
Is it intended that FreeBSD not conform to POSIX and C here? If so, we
could make that part of the behavior subject to another ifdef; if not,
we could simplify the code significantly.
I see that the FreeBSD behavior was introduced circa 2009; see:
https://github.com/freebsd/freebsd-src/commit/a3102e987093bd9225a1c69c834edce19d9ffc23
I suppose the idea was that poorly-written unportable programs that use
localtime in multiple threads are more likely to behave as their
misguided authors intended. Still, it seems clear there is a conformance
issue here. I suspect that when the 2009 change was put in, its
reviewers didn't know about the conformance bug.
Attached is a test program illustrating where FreeBSD does not conform
to the standards. On Fedora 42 this program outputs nothing and
succeeds. When I ran it on a FreeBSD 15 platform it output "gmtime
returns disagreeing pointers 0x40c29000, 0x41a00000" and failed.
I see that a single-threaded program does not have this issue: gmtime
and localtime return the same pointer in single-threaded programs. So if
the motivation is to cater to poorly-written unportable programs, it
appears that this motivation doesn't extend to the common mistake of
thinking that gmtime and localtime return different pointers.
#include <pthread.h>
#include <stdio.h>
#include <time.h>
time_t epoch = 0;
void *
thread_gmtime (void *arg)
{
return gmtime (arg);
}
int
main ()
{
pthread_t th;
int err = pthread_create (&th, 0, thread_gmtime, &epoch);
if (err)
return perror ("pthread_create"), 1;
void *thread_result;
err = pthread_join (th, &thread_result);
if (err)
return perror ("pthread_join"), 1;
void *main_result = gmtime (&epoch);
if (main_result != thread_result)
{
fprintf (stderr, "diagreeing pointers %p, %p\n",
main_result, thread_result);
return 1;
}
}