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

Reply via email to