Search This Blog

Friday, May 20, 2005

Atomic Functions - Part 2

Okay, let's write these functions. Starting with the first, a quick look at the Intel IA-32 manual reveals how it's done:
XADD—Exchange and Add
Exchanges the first operand (destination operand) with the second operand (source operand),
then loads the sum of the two values into the destination operand. The destination operand can
be a register or a memory location; the source operand is a register.
This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically.

That makes things pretty straightforward:
long AtomicExchangeAdd(long volatile *lpnNumber, long nAddend)
{
assert(lpnNumber);

__asm
{
push ebx

mov eax, nAddend
mov ebx, lpnNumber

lock xadd [ebx], eax

pop ebx
};
}

On to XCHG:
Exchanges the contents of the destination (first) and source (second) operands. The operands
can be two general-purpose registers or a register and a memory location. If a memory operand
is referenced, the processor’s locking protocol is automatically implemented for the duration of
the exchange operation, regardless of the presence or absence of the LOCK prefix or of the value
of the IOPL. (See the LOCK prefix description in this chapter for more information on the
locking protocol.)
This instruction is useful for implementing semaphores or similar data structures for process
synchronization. (See “Bus Locking” in Chapter 7 of the IA-32 Intel Architecture Software
Developer’s Manual, Volume 3, for more information on bus locking.)
The XCHG instruction can also be used instead of the BSWAP instruction for 16-bit operands.

long AtomicExchange(long volatile *lpnDest, long nSource)
{
assert(lpnDest);

__asm
{
push ebx

mov eax, nSource
mov ebx, lpnDest

lock xchg [ebx], eax

pop ebx
};
}

CMPXCHG:
Compares the value in the AL, AX, or EAX register (depending on the size of the operand) with
the first operand (destination operand). If the two values are equal, the second operand (source
operand) is loaded into the destination operand. Otherwise, the destination operand is loaded
into the AL, AX, or EAX register.
This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically.
To simplify the interface to the processor’s bus, the destination operand receives a write
cycle without regard to the result of the comparison. The destination operand is written back if
the comparison fails; otherwise, the source operand is written into the destination. (The
processor never produces a locked read without also producing a locked write.)

long AtomicCompareExchange(long volatile *lpnDest, long nSource, long nComparand)
{
assert(lpnDest);

__asm
{
push ebx
push edx

mov eax, nComparand
mov ebx, lpnDest
mov edx, nSource

lock cmpxchg [ebx], edx

pop edx
pop ebx
};
}

BTS:
Selects the bit in a bit string (specified with the first operand, called the bit base) at the bitposition
designated by the bit offset operand (second operand), stores the value of the bit in the
CF flag, and sets the selected bit in the bit string to 1. The bit base operand can be a register or
a memory location; the bit offset operand can be a register or an immediate value. If the bit base
operand specifies a register, the instruction takes the modulo 16 or 32 (depending on the register
size) of the bit offset operand, allowing any bit position to be selected in a 16- or 32-bit register,
respectively (see Figure 3-1). If the bit base operand specifies a memory location, it represents
the address of the byte in memory that contains the bit base (bit 0 of the specified byte) of the
bit string (see Figure 3-2). The offset operand then selects a bit position within the range −231 to
231 − 1 for a register offset and 0 to 31 for an immediate offset.
Some assemblers support immediate bit offsets larger than 31 by using the immediate bit offset
field in combination with the displacement field of the memory operand. See “BT—Bit Test” in
this chapter for more information on this addressing mechanism.
This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically.

BTR:
Selects the bit in a bit string (specified with the first operand, called the bit base) at the bitposition
designated by the bit offset operand (second operand), stores the value of the bit in the
CF flag, and clears the selected bit in the bit string to 0. The bit base operand can be a register
or a memory location; the bit offset operand can be a register or an immediate value. If the bit
base operand specifies a register, the instruction takes the modulo 16 or 32 (depending on the
register size) of the bit offset operand, allowing any bit position to be selected in a 16- or 32-bit
register, respectively (see Figure 3-1). If the bit base operand specifies a memory location, it
represents the address of the byte in memory that contains the bit base (bit 0 of the specified
byte) of the bit string (see Figure 3-2). The offset operand then selects a bit position within the
range −231 to 231 − 1 for a register offset and 0 to 31 for an immediate offset.
Some assemblers support immediate bit offsets larger than 31 by using the immediate bit offset
field in combination with the displacement field of the memory operand. See “BT—Bit Test” in
this chapter for more information on this addressing mechanism.
This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically.

long AtomicBitExchange(long volatile *lpnDest, long nBit, long bSet)
{
assert(lpnDest);
assert(nBit >= 0 && nBit < 32);

__asm
{
push ebx
push edx

mov eax, bSet
mov ebx, lpnDest
mov edx, nBit

test eax, eax // Test if bSet is true or false
jz ClearBit

SetBit:
lock bts [ebx], edx
jmp Done

ClearBit:
lock btr [ebx], edx

Done:
setc al
movzx eax, al

pop edx
pop ebx
};
}

BTC: Selects the bit in a bit string (specified with the first operand, called the bit base) at the bitposition designated by the bit offset operand (second operand), stores the value of the bit in the CF flag, and complements the selected bit in the bit string. The bit base operand can be a register or a memory location; the bit offset operand can be a register or an immediate value. If the bit base operand specifies a register, the instruction takes the modulo 16 or 32 (depending on the register size) of the bit offset operand, allowing any bit position to be selected in a 16- or 32-bit register, respectively (see Figure 3-1). If the bit base operand specifies a memory location, it represents the address of the byte in memory that contains the bit base (bit 0 of the specified byte) of the bit string (see Figure 3-2). The offset operand then selects a bit position within the range −231 to 231 − 1 for a register offset and 0 to 31 for an immediate offset. Some assemblers support immediate bit offsets larger than 31 by using the immediate bit offset field in combination with the displacement field of the memory operand. See “BT—Bit Test” in this chapter for more information on this addressing mechanism. This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically.

long AtomicBitExchangeCompliment(long volatile *lpnDest, long iBit)
{
assert(lpnDest);
assert(iBit >= 0 && iBit < 32);

__asm
{
push ebx

mov eax, iBit
mov ebx, lpnDest

lock btc [ebx], eax

setc al
movzx eax, al

pop ebx
};
}

So lots and lots of text, but pretty trivial.

No comments: