Поточно-ориентированная версия стандартной функции C "localtime"

Я использую локальное время в многопоточном приложении.

Я должен заменить его потокобезопасной версией, которая, насколько я понимаю, называется localtime_r.

Однако, когда я это делаю, я не могу завершить сборку из-за сбоя привязки.

Я даже не знаю, с чего начать поиск решения.

Компоненты моей системы:

  • Чип - STM32 (кортекс на базе ARM)
  • IDE - IAR (компилятор и компоновщик для ST-микроконтроллеров)
  • ОС — ThreadX

Я предполагаю (хотя я не уверен в этом), что эта функция обычно предоставляется в стандартных библиотеках, поставляемых вместе с IDE. Я прав?

Есть ли еще одна библиотека, которую мне нужно добавить в мой проект, или это что-то в настройках проекта?

Если нет жизнеспособного способа «импортировать» эту функцию в мой код, какие еще варианты у меня есть для потокобезопасной версии функции локального времени?

ОБНОВЛЕНИЕ:

Я стараюсь избегать использования ресурсов ОС (т. е. мьютекса) и вместо этого вызывать стандартную процедуру, которая делает именно то, что делает «локальное время», только без статической структуры, которую она использует (что делает ее небезопасной для потоков).

Я не вижу причин, по которым localtime для начала будет использовать статическую структуру, поэтому я предполагаю, что соответствующий localtime_r не решает ее с помощью каких-либо ресурсов ОС, а просто воздерживается от использования статической структуры.

Если такой вариант невозможен, то я мог бы реализовать его сам, только я предпочитаю использовать то, что уже проверено должным образом из-за «неправильности» системы датирования.

Вопрос здесь в том, как связать localtime_r с моим проектом (НЕ как реализовать localtime_r).

Спасибо


person barak manos    schedule 28.12.2013    source источник


Ответы (2)


Вам нужно будет создать свою собственную оболочку для localtime(), которая является потокобезопасной, то есть в основном реализует 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;
}

Вам нужно будет вызвать my_localtime_init где-нибудь при запуске.

Это также означает, что вам нужно везде заменить все вызовы функцией my_localtime_r, никто больше не может вызывать localtime() напрямую, так как это может вызвать состояние гонки с кодом, вызывающим новый my_localtime_r.

person nos    schedule 28.12.2013
comment
Но в любом случае код не должен вызывать localtime(), если он многопоточный, потому что localtime() в первую очередь не является потокобезопасным. Между прочим, вы можете аварийно завершить работу, если localtime() вернет нулевой указатель. - person Jonathan Leffler; 29.12.2013
comment
Я хотел бы избежать использования ресурсов ОС. Я не вижу причин, по которым localtime для начала использовал бы статическую структуру, поэтому я предполагаю, что соответствующий localtime_r не решает ее с помощью каких-либо ресурсов ОС, а просто воздерживается от использования статической структуры. - person barak manos; 29.12.2013
comment
@barakmanos localtime() возвращает указатель, на что он указывает, что нужно где-то жить, как он может не использовать статический ресурс? (Он мог бы использовать статическую переменную для каждого потока, но в руководствах IAR ничего не говорится об этом) статический буфер для результата, так как вы передаете его. - person nos; 29.12.2013
comment
Я имел в виду, что localtime должен был принять этот указатель в качестве пользовательских аргументов для начала (вместо того, чтобы возвращать адрес статической структуры). Я почти уверен, что это то, что делает localtime_r... И поэтому мой вопрос на самом деле - как мне связать localtime_r с моим проектом? (Кстати, это именно то, что написано внутри вопроса) - person barak manos; 29.12.2013
comment
@barakmanos Тогда я не знаю, что вы имеете в виду, очевидно, что localtime() не принимает этот указатель в качестве пользовательского аргумента - он принимает только 1 аргумент. localtime_r делает. Так же как и my_localttime_r(), показанная здесь, но она реализована путем вызова localtime(), которая нуждается в мьютексе, чтобы сделать ее потокобезопасной. - person nos; 29.12.2013
comment
@nos: я ожидаю очень простого, независимого от ОС, но потокобезопасного решения для преобразования даты и времени в отметку времени. Я не знал о том, что localtime_r оборачивает локальное время мьютексом. Если это действительно так (так ли это?), то, думаю, я просто реализую это сам. Спасибо! - person barak manos; 29.12.2013
comment
@barakmanos Это зависит от платформы, реализовано ли localtime_r поверх localtime() или localtime() поверх localtime_r. Если вы хотите создать localtime_r с нуля, это нормально, но это не просто, вам придется заботиться о часовом поясе, отслеживать дополнительные секунды и другие странности и угловые случаи. Лучше всего найти подходящую существующую реализацию и перенести ее на свою ОС. (например, из одной из *BSD или, возможно, из библиотеки newlib) - person nos; 29.12.2013
comment
@nos - спасибо. Меня интересуют только ДД/ММ/ГГ и ЧЧ:ММ:СС (а не часовые пояса, день недели и т. д.). Кроме того, меня интересует только период с 2000 по 2099 год. См. мой другой вопрос по этому вопросу: stackoverflow.com/q/19756465/ 1382251. Так что я думаю, что могу чувствовать себя в безопасности, внедряя его самостоятельно... верно? - person barak manos; 29.12.2013
comment
@barakmanos: нет, я не думаю, что вам следует реализовать тело localtime_r() самостоятельно. Вы создаете себе (и своему работодателю) большую ответственность. Если бы это было gmtime_r(), вам было бы легче, но localtime_r() должен знать о часовых поясах, потому что он работает с местным временем, а не с UTC. Очевидно, что если это хобби-проект, которым больше никто никогда не воспользуется, то можно делать как угодно. Но если другие люди собираются использовать вашу работу, вы должны сделать localtime_r() правильно — и это нетривиальное упражнение. Гораздо проще использовать мьютекс вокруг чужого localtime()! - person Jonathan Leffler; 29.12.2013

Если потокобезопасная версия недоступна, вам придется создать ее со стандартным интерфейсом для localtime_r(), но использующую мьютекс для обеспечения взаимного исключения. Возможно, я бы использовал то же имя, но есть аргументы в пользу использования альтернативного имени, которое вы всегда называете, которое вызывает localtime_r напрямую, если оно доступно.

И если ваша система использует другие мьютексы, отличные от мьютексов Pthread, эта концепция применима; используйте собственные мьютексы вместо мьютексов Pthread.

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

Некомпилированный — гораздо менее проверенный.

Если атрибуты мьютекса по умолчанию вам не подходят, вам потребуется приложить больше усилий для правильной инициализации мьютекса.

person Jonathan Leffler    schedule 28.12.2013
comment
Есть ли причина, по которой mx_localtime_r не следует помещать внутрь localtime_r()? - person chux - Reinstate Monica; 29.12.2013
comment
@chux: если подумать, вероятно, нет - если инициализатор по умолчанию в порядке. Если это не так, вам понадобится одна функция для инициализации мьютекса, и суррогат localtime_r() должен будет получить к нему доступ, поэтому тогда он должен быть вне функции. - person Jonathan Leffler; 29.12.2013
comment
Я не вижу причин, по которым localtime для начала использовал бы статическую структуру, поэтому я предполагаю, что соответствующий localtime_r не решает ее с помощью каких-либо ресурсов ОС, а просто воздерживается от использования статической структуры. - person barak manos; 29.12.2013
comment
@barakmanos: localtime() был разработан давным-давно, до того, как в Unix появились потоки (в начале-середине 70-х), и его дизайн не был серьезной проблемой (хотя он никогда не был великим). Что происходит в системе с потокобезопасностью, так это то, что основную работу выполняет localtime_r(), а localtime() — это простая функция покрытия, имеющая статическое struct tm, которое она передает localtime_t() и возвращает пользователю: struct tm *localtime(const time_t *restrict timer) { static struct tm result; return localtime_r(timer, &result); }. - person Jonathan Leffler; 29.12.2013