murphsghost murphsghost - 2 months ago 14
C Question

<time.h> Shared Internal Object - struct tm

I was looking into the routines of the C time library, since I needed a way to keep track of time on the log file of a program. I found the way to do it was to have a

time_t
object, which holds nothing more than the number of seconds since 1st Jan 1970 00:00 UTC. Then, I'd parse this
time_t
object to the
localtime(time_t* argument)
routine, that would return a pointer to a
tm
structure. This latter would hold the time in a more complex structure, translating the time in seconds for years, months, days, hours, etc., since 1st Jan 1970. This
tm
structure pointer could be finally used by
asctime(strcut tm* argument)
to return a human friendly time for the log (i.e. Wed Sep 7 13:45:23 2016).

My question is about the
struct tm
object. As we can see in the example code from Cplusplus, a
struct tm
object is never declared, only a pointer. Meaning this object is declared somewhere else and we are just accessing it. The reference link itself states:

"The function also accesses and modifies a shared internal object,
which may introduce data races on concurrent calls to gmtime and
localtime. Some libraries provide an alternative function that avoids
this data race: localtime_r (non-portable)."


So, who creates the
struct tm
object? Is it the C time library in the first time it is loaded? That would mean that the first process that loads the library would declare the object and all other processes would just share this library with the object already declared? Wouldn't it be better, in order to avoid those Data Races issues, to just create a new
struct tm
object for each call and return a pointer to it so each program has its own structure?

Maybe having a
struct tm MyProgramStruct; localtime(&Rawtime, &MyProgramStruct);
instead of a single struct for every program using Ctime? Any reason it's done this way instead?

Finally, is using C time library
localtime
routine a bad practice because of the possibility of the unsynchronization of programs leading to a wrong output?

Example code from the link:

/* localtime example */
#include <stdio.h> /* puts, printf */
#include <time.h> /* time_t, struct tm, time, localtime */

int main ()
{
time_t rawtime;
struct tm * timeinfo;

time (&rawtime);
timeinfo = localtime (&rawtime);
printf ("Current local time and date: %s", asctime(timeinfo));

return 0;
}

Answer

So, who creates the struct tm object? Is it the C time library in the first time it is loaded?

According to the docs for the localtime() function, it returns a pointer to a statically allocated struct. That struct belongs to the C library; the function provides a pointer to it. The fine details of exactly where it lives and exactly when and how storage for it is allocated don't matter and may vary from implementation to implementation. You just have to understand that different calls by the same process work with and provide pointers to the same struct.

That would mean that the first process that loads the library would declare the object and all other processes would just share this library with the object already declared?

No. You do not need to worry about the struct being shared between processes, only about it being shared across multiple calls and between multiple threads in the same process.

Wouldn't it be better, in order to avoid those Data Races issues, to just create a new struct tm object for each call and return a pointer to it so each program has its own structure? Maybe having a struct tm MyProgramStruct; localtime(&Rawtime, &MyProgramStruct); instead of a single struct for every program using Ctime? Any reason it's done this way instead?

Again, it's not a cross-process problem, only an intra-process problem, which is still bad enough. Yes, that problem would be addressed by not sharing the struct, and that's what distinguishes the function localtime_r(), where it is available. But the existing function cannot be changed to do the same, because that would introduce a new requirement on users to free the provided struct.

The designers of localtime() wanted to make it easy to use, and indeed it is, as long as you don't run afoul of the shared data issue. If your program is single-threaded, then you can avoid it fairly easily. localtime() is not the only standard library function with an issue of this sort.

Finally, is using C time library localtime routine a bad practice because of the possibility of the unsynchronization of programs leading to a wrong output?

Not for that reason, no, because there is no cross-process problem. You do need to pay attention when using localtime() and other functions with similar static storage, such as strtok(). But you can judge on a program by program basis whether there is any data race -- you do not need to worry about interference from unspecified other programs.

Comments