Является ли встроенная функция атомарной? - PullRequest
2 голосов
/ 27 января 2011

Может ли linux context переключаться после разблокировки в приведенном ниже коде, если так, у нас есть проблема, если два потока вызывают это

inline bool CMyAutoLock::Lock(
    pthread_mutex_t *pLock,
    bool bBlockOk 
)
throw ()
{
    Unlock();
    if (pLock == NULL)
        return (false);
// **** can context switch happen here ? ****///
    return ((((bBlockOk)? pthread_mutex_lock(pLock) :
        pthread_mutex_trylock(pLock)) == 0)? (m_pLock = pLock, true) : false);
}

Ответы [ 5 ]

6 голосов
/ 27 января 2011

Нет, это не атомарно.

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

4 голосов
/ 27 января 2011

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

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

4 голосов
/ 27 января 2011

Встроенные функции не являются автоматически атомарными.Ключевое слово inline означает «при компиляции этого кода попытайтесь оптимизировать вызов, заменив вызов инструкциями по сборке из тела вызова».В любой из этих инструкций по сборке вы можете получить контекстное переключение так же, как и в любых других инструкциях по сборке, поэтому вам потребуется защитить код с помощью блокировки.

1 голос
/ 29 января 2011

Inline заставляет функцию работать как макрос. Inline никак не связан с атомарным.

AFAIK inline - это подсказка, и gcc может ее игнорировать. При встраивании код из встроенного функционала, называемого его B, копируется в функцию вызывающего абонента, A. Не будет вызова от A к B. Это, вероятно, сделает ваш исполняемый файл быстрее за счет увеличения размера. Наверное? Exe может стать меньше, если ваша встроенная функция мала. Исполняемый файл может стать медленнее, если встраивание затруднит оптимизацию функции A. Если вы не укажете inline, во многих случаях gcc примет для вас встроенное решение. Функции-члены классов являются встроенными по умолчанию. Вы должны явно указать gcc не делать автоматических строк. Кроме того, gcc не будет встроен, когда оптимизации отключены.

Линкер не будет встроенным. Таким образом, если модуль A extern ссылался на функцию, помеченную как встроенную, но код находился в модуле B, модуль A будет вызывать функцию, а не вставлять ее. Вы должны определить функцию в заголовочном файле и объявить ее как extern inline func foo (a, b, c). На самом деле все намного сложнее.

inline void test(void); // __attribute__((always_inline));

inline void test(void)
{
    int x = 10;
    long y = 10;
    long long z = 10;
    y++;
    z = z + 10;
}
int main(int argc, char** argv)
{
    test();
    return (0);
}

Не в строке:

!{
main+0: lea    0x4(%esp),%ecx
main+4: and    $0xfffffff0,%esp
main+7: pushl  -0x4(%ecx)
main+10: push   %ebp
main+11: mov    %esp,%ebp
main+13: push   %ecx
main+14: sub    $0x4,%esp
!   test();
main+17: call   0x8048354 <test>    <--- making a call to test.
!   return (0);
main()
main+22: mov    $0x0,%eax
!}
main+27: add    $0x4,%esp
main+30: pop    %ecx
main+31: pop    %ebp
main+32: lea    -0x4(%ecx),%esp
main+35: ret    

Инлайн:

inline void test(void)__attribute__((always_inline));


!   int x = 10;
main+17: movl   $0xa,-0x18(%ebp)                  <-- hey this is test code....in main()!
!   long y = 10;
main+24: movl   $0xa,-0x14(%ebp)
!   long long z = 10;
main+31: movl   $0xa,-0x10(%ebp)
main+38: movl   $0x0,-0xc(%ebp)
!   y++;
main+45: addl   $0x1,-0x14(%ebp)
!   z = z + 10;
main+49: addl   $0xa,-0x10(%ebp)
main+53: adcl   $0x0,-0xc(%ebp)
!}
!int main(int argc, char** argv)
!{
main+0: lea    0x4(%esp),%ecx
main+4: and    $0xfffffff0,%esp
main+7: pushl  -0x4(%ecx)
main+10: push   %ebp
main+11: mov    %esp,%ebp
main+13: push   %ecx
main+14: sub    $0x14,%esp
!   test();                           <-- no jump here
!   return (0);
main()
main+57: mov    $0x0,%eax
!}
main+62: add    $0x14,%esp
main+65: pop    %ecx
main+66: pop    %ebp
main+67: lea    -0x4(%ecx),%esp
main+70: ret  

Вы можете быть уверены, что только атомарные функции - это gcc атомарные встроенные . Вероятно, простые инструкции по сборке одного кода операции также являются атомарными, но они не могут быть такими. По моему опыту, установка 6x86 или чтение 32-битного целого числа являются атомарными. Вы можете догадаться, может ли строка кода c быть атомарной, посмотрев на сгенерированный код сборки.

Приведенный выше код был скомпилирован в 32-битном режиме. Вы можете видеть, что long long требует 2 кода операции для загрузки. Я предполагаю, что это не атомно. Интенты и длинные принимают один код операции для установки. Вероятно, атомный. y ++ реализован с помощью addl, который, вероятно, является атомарным. Я продолжаю говорить, вероятно, потому что микрокод на процессоре может использовать более одной инструкции для реализации операции, и знание этого выше моего уровня оплаты. Я предполагаю, что все 32-битные записи и чтения являются атомарными. Я предполагаю, что приращения нет, потому что они обычно выполняются с чтением и записью.

Но проверьте это, когда скомпилировано в 64-битном

!   int x = 10;
main+11: movl   $0xa,-0x14(%rbp)
!   long y = 10;
main+18: movq   $0xa,-0x10(%rbp)
!   long long z = 10;
main+26: movq   $0xa,-0x8(%rbp)
!   y++;
main+34: addq   $0x1,-0x10(%rbp)
!   z = z + 10;
main+39: addq   $0xa,-0x8(%rbp)
!}
!int main(int argc, char** argv)
!{
main+0: push   %rbp
main+1: mov    %rsp,%rbp
main+4: mov    %edi,-0x24(%rbp)
main+7: mov    %rsi,-0x30(%rbp)
!   test();
!   return (0);
main()
main+44: mov    $0x0,%eax
!}
main+49: leaveq 
main+50: retq   

Я предполагаю, что addq может быть атомарным.

0 голосов
/ 27 января 2011

Большинство операторов не являются атомарными.Ваш поток может быть прерван в середине операции ++i.Правило состоит в том, что что-либо не является атомарным, если оно не определено явно как атомарное.

...