The ARM Transactional Memory Extensions have a fairly straightforward description of how one would use them:
sem_post:
TSTART X0 // Start of outer transaction
CBNZ test_fail // No reason for this routine to cancel or fail
LDR X1, [X2] // X2 points to semaphore
ADD X1, X1, #1 // Increment semaphore value
STR X1, [X2]. // Store incremented value
TCOMMIT // Commit transaction and exit
What I'm trying to figure out is whether these transactions replay based on collisions with transaction in other parts of the code, and whether they replay based on collisions with any sort of accesses. To elaborate, let's say we have this routine:
sem_wait:
TSTART X0 // Start of outer transaction
CBNZ retry_check // This routine checks for retry (RTRY) and restarts transaction
LDR X1, [X2] // X2 points to semaphore
CMP X1, #0 // Check if semaphore is already used
CBNZ decrement // If it's non-zero, we can decrement the semaphore
TCANCEL #0xFF // If it's zero, we gotta retry
decrement:
SUB X1, X1, #1 // Decrement semaphore value
STR X1, [X2]. // Store decremented value
TCOMMIT // Commit transaction and exit
So this transaction would be in another part of the code, but would be accessing the locations in memory as the sem_post transaction.
My first question: would a thread executing the sem_post
transaction potentially replay due to a thread executing the sem_wait
transaction concurrently?
For the second part of my question, let's say we have a simple routine like this:
break_semaphore:
MOV X0, #0xFF
STR X0, [X1] // X1 points to semaphore
The above routine isn't a transaction at all, it's just messing with the semaphore.
My second question: Would the thread executing the sem_post
transaction potentially replay due to any concurrent access to locations that are to be updated and committed in the sem_post
transaction?
For clarity, I fully understand that this isn't really how the TME instructions are supposed to be used, and that locks would be implemented more like this: https://www.gem5.org/project/2020/10/27/tme.html
I'm more wondering what it is that transactions actually linearize: two transactions with common regions of code, all transactions with each other, or the transaction with respect to all other accesses to memory?
TME definitely linearizes accesses to shared memory regions. In the case of your example, the reason why these transactions are aborting is not due to the fact that they are executing the same code, but due to the shared memory address.
From the ARM TME documentation, any conflicting state to a memory address will cause TSTART to fail with the MEM bit set. In the context of your semaphore example, because there is no fallback code for the call to sem_post
, the transaction would cancel program execution state would revert back to non-Transactional state.
For a similar reason, transactions do not necessarily linearize by executing the same code because they may refer to different regions of memory (i.e., multiple semaphores with different pointers), which is perfectly legal.
Whether or not transactions linearize amongst each other is more difficult to answer, because it is typically hardware dependent. For example, two transactions could be legally executing on different cores with different memory objects, but if two transactions are attempting the be executed on the same core and registers (i.e., with hyper-threading), this behavior would be more difficult to define.