I read the cppreference guide over the sperimental feature of transactional memory and i try it.
I write some simple code with sincronized
that as say cpp reference is not a transaction but only guarantees that the operation in the block are executed in a total order, the i write the same code with atomic_noexcept
and atomic_commit
, not with atomic_cancel
that seems to be not yet implemented.
The doubt that i have is about the difference between atomic_noexcept
, atomic_commit
and synchronized
, apparently they work in the same way, except for the compilation error if a no transaction safe function is called in an atomic block.
So I analyze the assembly code for the 3 variants, and result the same, as reported below:
cpp atomic_noexcept:
int a;
void thread_func() {
atomic_noexcept
{
++a;
}
}
assembly atomic_noexcept:
thread_func():
subq $8, %rsp
movl $43, %edi
xorl %eax, %eax
call _ITM_beginTransaction
testb $2, %al
jne .L2
movl $a, %edi
call _ITM_RfWU4
movl $a, %edi
leal 1(%rax), %esi
call _ITM_WaWU4
call _ITM_commitTransaction
addq $8, %rsp
ret
.L2:
addl $1, a(%rip)
addq $8, %rsp
jmp _ITM_commitTransaction
a:
.zero 4
cpp atomic_commit:
int a;
void thread_func() {
atomic_commit
{
++a;
}
}
assembly atomic_commit:
thread_func():
subq $8, %rsp
movl $43, %edi
xorl %eax, %eax
call _ITM_beginTransaction
testb $2, %al
jne .L2
movl $a, %edi
call _ITM_RfWU4
movl $a, %edi
leal 1(%rax), %esi
call _ITM_WaWU4
call _ITM_commitTransaction
addq $8, %rsp
ret
.L2:
addl $1, a(%rip)
addq $8, %rsp
jmp _ITM_commitTransaction
a:
.zero 4
cpp synchronized:
int a;
void thread_func() {
synchronized
{
++a;
}
}
assembly synchronized:
thread_func():
subq $8, %rsp
movl $43, %edi
xorl %eax, %eax
call _ITM_beginTransaction
testb $2, %al
jne .L2
movl $a, %edi
call _ITM_RfWU4
movl $a, %edi
leal 1(%rax), %esi
call _ITM_WaWU4
call _ITM_commitTransaction
addq $8, %rsp
ret
.L2:
addl $1, a(%rip)
addq $8, %rsp
jmp _ITM_commitTransaction
a:
.zero 4
How can they work differently? For example i report the explanation of different atomic block of cppreference:
atomic_noexcept : If an exception is thrown, std::abort is called
atomic_cancel : If an exception is thrown, std::abort is called, unless the exception is one of the exceptions uses for transaction cancellation (see below) in which case the transaction is cancelled: the values of all memory locations in the program that were modified by side effects of the operations of the atomic block are restored to the values they had at the time the start of the atomic block was executed, and the exception continues stack unwinding as usual.
atomic_commit : If an exception is thrown, the transaction is committed normally.
How can atomic_noexcept
work differently from atomic_commit
if has the same assembly code?
How can syncronized block work differently from atomic block if has the same assembly code?
EDIT:
All these test and assembly code are extracted from last version of GCC (V. 10.2)
EDIT2:
After some test and research i haven't found yet a logical explanation for the said different behaviour.