собственно, что означает, что он потенциально более тесно связан с компилятором.
ем эту простую функцию, которая увеличивает целое число под блокировкой, реализованнойstd::mutex
:
#include <mutex>
std::mutex m;
void inc(int& i) {
std::unique_lock<std::mutex> lock(m);
i++;
}
Я ожидал бы, что это (после вставки) будет скомпилировано прямым способом к вызовуm.lock()
приращениеi
а потомm.unlock()
.
Проверка сгенерированной сборки на наличие последних версийgcc
а такжеclang
Однако мы видим дополнительное осложнение. Принимаяgcc
версия первая:
inc(int&):
mov eax, OFFSET FLAT:__gthrw___pthread_key_create(unsigned int*, void (*)(void*))
test rax, rax
je .L2
push rbx
mov rbx, rdi
mov edi, OFFSET FLAT:m
call __gthrw_pthread_mutex_lock(pthread_mutex_t*)
test eax, eax
jne .L10
add DWORD PTR [rbx], 1
mov edi, OFFSET FLAT:m
pop rbx
jmp __gthrw_pthread_mutex_unlock(pthread_mutex_t*)
.L2:
add DWORD PTR [rdi], 1
ret
.L10:
mov edi, eax
call std::__throw_system_error(int)
Это первые пару строк, которые интересны. Собранный код проверяет адрес__gthrw___pthread_key_create
(который является реализацией дляpthread_key_create
- функция для создания ключа локального потока), и если он равен нулю, он переходит к.L2
который реализует приращение в одной инструкции без какой-либо блокировки вообще.
Если он не равен нулю, он работает так, как ожидалось: блокирует мьютекс, делает приращение и разблокирует.
clang
делает еще больше: проверяет адрес функциидваждыоднажды передlock
и однажды передunlock
:
inc(int&): # @inc(int&)
push rbx
mov rbx, rdi
mov eax, __pthread_key_create
test rax, rax
je .LBB0_4
mov edi, m
call pthread_mutex_lock
test eax, eax
jne .LBB0_6
inc dword ptr [rbx]
mov eax, __pthread_key_create
test rax, rax
je .LBB0_5
mov edi, m
pop rbx
jmp pthread_mutex_unlock # TAILCALL
.LBB0_4:
inc dword ptr [rbx]
.LBB0_5:
pop rbx
ret
.LBB0_6:
mov edi, eax
call std::__throw_system_error(int)
Какова цель этой проверки?
Может быть, это для поддержки случая, когда объектный файл в конечном итоге скомпилирован в двоичный файл без поддержки pthreads, а затем для возврата к версии без блокировки в этом случае? Я не смог найти никакой документации по этому поведению.