2

I am using localtime in a multi-threaded application.

I have to replace it with a thread-safe version, which to my understanding is called localtime_r.

However, when I do so, I am unable to complete the build, due to a linkage failure.

I am not even sure where to start looking for the solution.

My system components are:

  • Chip - STM32 (ARM based cortex)
  • IDE - IAR (compiler and linker for ST-Microcontrollers)
  • OS - ThreadX

I'm assuming (though I'm not sure about it), that this function would typically be supplied within the standard libraries provided along with the IDE. Am I correct?

Is there another library that I need to add to my project, or is it something in the project settings?

If there is no viable way to "import" this function into my code, what other options do I have for a thread-safe version of the localtime function?

UPDATE:

I am looking to avoid the use of OS resources (i.e., a mutex), and instead call a standard routine which does exactly what 'localtime' does, only without the static structure that it uses (making it thread-unsafe).

I don't see any reason why localtime would use a static structure to begin with, so I assume that the corresponding localtime_r does not solve it with any OS resources whatsoever, but merely refrains the use of a static structure.

If no such option is viable, then I might as well implement it myself, only I prefer to use something that has already been tested properly, due to the "irregularity" of the dating system.

The question here is, how do I link localtime_r' into my project (NOT how to implement localtime_r).

Thanks

barak manos
  • 29,648
  • 10
  • 62
  • 114

2 Answers2

5

You will need to create your own wrapper for localtime() that is threadsafe, that is basically implementing localtime_r().

e.g.

static TX_MUTEX localtime_lock;
void my_localtime_init(void)
{
  tx_Mutex_create( &localtime_lock, "localtime_lock", TX_INHERIT);
}

struct tm *my_localtime_r(const time_t *tim, struct tm *result)
{
   struct tm *t;

   tx_Mutex_get(&localtime_lock, TX_WAIT_FOREVER);
   t = localtime(tim);
   if (t)
       *result = *t;
   tx_Mutex_put(&localtime_lock);

  return t ? result : NULL;
}

You'll need to call my_localtime_init somewhere at startup.

This also means you need to replace all calls everywhere with the my_localtime_r function, noone can be calling localtime() directly any more as that could cause race conditions with the code calling the new my_localtime_r.

nos
  • 223,662
  • 58
  • 417
  • 506
  • 2
    But the code should not be calling `localtime()` anyway if it is threaded — because `localtime()` is not thread-safe in the first place. Incidentally, you could crash if `localtime()` returns a null pointer. – Jonathan Leffler Dec 28 '13 at 23:40
  • I would like to avoid the use of OS resources. I don't see any reason why localtime would use a static structure to begin with, so I assume that the corresponding localtime_r does not solve it with any OS resources whatsoever, but merely refrains the use of a static structure. – barak manos Dec 29 '13 at 11:04
  • @barakmanos localtime() returns a pointer, what it points to have to live somewhere, how could it not use a static resource ? (It could use a per thread static variable. but nothing in the IAR manuals says it does that) localtime_r()/my_localtime_r() as you see takes an extra argument for where to store the `struct tm`, so it doesn't have to use a static buffer for the result, since you pass it in. – nos Dec 29 '13 at 11:07
  • What I meant is, localtime should have taken that pointer as a user arguments to begin with (instead of returning the address of a static structure). I'm pretty sure that's what localtime_r is doing... And so my question is really - how do I link localtime_r into my project? (BTW, this is exactly what's written inside the question) – barak manos Dec 29 '13 at 11:09
  • @barakmanos I don't know what you mean then, localtime() does obviously not take that pointer as a user argument - it only takes 1 argument. localtime_r does. So does my_localttime_r() shown here, but it is implemented by calling localtime(), which needs the mutexes to make it thread safe. – nos Dec 29 '13 at 11:13
  • @nos: I would expect a very simple, OS-agnostic, yet thread-safe solution for converting date-time into timestamp. I was not aware of the fact that localtime_r wraps localtime with a mutex. If that is indeed the case (is it?) then I guess, I will just implement it myself. Thank you! – barak manos Dec 29 '13 at 11:17
  • 1
    @barakmanos It depends on the platform whether localtime_r is implemented on top of localtime(), or localtime() is implemented on top of localtime_r. If you want to create localtime_r from scratch, that's fine, but it is not simple, you'll have to care for the timezone, track leap seconds and other weirdness and corner cases. You're best off finding a suitable existing implementation and port that over to your OS. (e.g. from one of the *BSDs or perhaps from the newlib library) – nos Dec 29 '13 at 11:29
  • @nos - thanks. I only care about DD/MM/YY and HH:MM:SS (not about timezones, day of the week, etc). Also, I am only interested in the period between 2000 - 2099. See my other question on this issue: http://stackoverflow.com/q/19756465/1382251. So I think I can feel pretty safe implementing it myself... right? – barak manos Dec 29 '13 at 11:51
  • @barakmanos: no, I don't think you should implement the body of `localtime_r()` yourself. You are creating yourself (and your employer) a major liability. If it was `gmtime_r()`, you'd have an easier time of it, but `localtime_r()` has to know about time zones because it deals in local time, not UTC. Obviously, if it is hobby project that no-one else will ever use, then you can do as you like. But if other people are going to use your handiwork, you have to get `localtime_r()` right — and that is not a trivial exercise. It's a lot easier to use a mutex around someone else's `localtime()`! – Jonathan Leffler Dec 29 '13 at 16:15
3

If there isn't a thread-safe version available, you'll have to create one, which has the standard interface for localtime_r() but which uses a mutex to ensure mutual exclusion. I'd use the same name, probably, but there are arguments for using an alternative name that you always call, which calls localtime_r direct if it is available.

And if your system uses different mutexes than Pthread mutexes, the concept applies; use the native mutexes instead of Pthread mutexes.

static pthread_mutex_t mx_localtime_r = PTHREAD_MUTEX_INITIALIZER;

struct tm *localtime_r(const time_t *restrict timer, struct tm *restrict result)
{
    struct tm *r = 0;
    if (pthread_mutex_lock(&mx_localtime_r) == 0)
    {
        r = localtime(timer);
        if (r != 0)
        {
            *result = *r;
            r = result;
        }
        if (pthread_mutex_unlock(&mx_localtime_r) != 0)
            r = 0;
    }
    return r;
}

Uncompiled — much less tested.

If the default attributes of a mutex are not appropriate for you, then you'll need to invest more effort into initializing the mutex properly.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    Is there a reason `mx_localtime_r` should not be place inside `localtime_r()`? – chux - Reinstate Monica Dec 29 '13 at 02:25
  • 2
    @chux: come to think of it, probably not — if the default initializer is OK. If it isn't, then you'll need one function to initialize the mutex, and the `localtime_r()` surrogate will need to access it, so it would then need to be outside a function. – Jonathan Leffler Dec 29 '13 at 02:49
  • I don't see any reason why localtime would use a static structure to begin with, so I assume that the corresponding localtime_r does not solve it with any OS resources whatsoever, but merely refrains the use of a static structure. – barak manos Dec 29 '13 at 11:03
  • 1
    @barakmanos: `localtime()` was designed a long time ago, before Unix had threads (in the early to mid 70s), and its design was not a major problem (though it was never great). What happens in a system with thread-safety is that the main work is done by `localtime_r()`, and `localtime()` is a simple cover function that has a static `struct tm` that it passes to `localtime_t()` and returns to the user: `struct tm *localtime(const time_t *restrict timer) { static struct tm result; return localtime_r(timer, &result); }`. – Jonathan Leffler Dec 29 '13 at 16:10