Starting Code
Pseudocode
This "pythonesque" pseudocode with some pointer operations thrown in shows the general API and implementation details of the vector. The pseudocode is a conversion of the paper's pseudocode into a more (in my opinion) understandable form. It completely ignores memory reclamation.
You don't need to read this entire thing, it's just here as a reference.
# Calculate the index of the correct bucket
# Return a pointer
def at(vector, i):
pos = i + FIRST_BUCKET_SIZE
hibit = highest_bit(pos)
index = pos ^ 2 ** hibit
return &vector.memory[hibit - highest_bit(FIRST_BUCKET_SIZE)][index]
# Perform an atomic load at the correct index
def read(vector, i):
return *at(vector, i).load(Ordering)
# Perform an atomic store at the correct index
def write(vector, i, elem):
return *at(vector, i).store(elem, Ordering)
# Calculate the number of allocations needed
# Then perform each allocation
def reserve(vector, size):
i = highest_bit(vector.descriptor.size + FIRST_BUCKET_SIZE - 1)
- highest_bit(FIRST_BUCKET_SIZE)
if i < 0 {
i = 0
}
while i < highest_bit(size + FIRST_BUCKET_SIZE - 1)
- highest_bit(FIRST_BUCKET_SIZE):
i += 1
allocate_bucket(vector, i)
# Calculate the amount of memory needed
# Allocate that much memory
# Try to CAS it in
# If CAS fails, the bucket is already initalized, so free the memory
def allocate_bucket(vector, bucket):
bucket_size = FIRST_BUCKET_SIZE * (2 ** bucket)
mem = allocate(bucket_size)
if not CAS(&vector.memory[bucket], nullptr, mem):
free(mem)
# Get the size of the current descriptor
# If there is a pending write operation, subtract one from the size
def size(vector):
size = vector.descriptor.size
if descriptor.writeop.pending:
size -= 1
return size
# Get the current descriptor
# Complete a pending write operation
# Allocate memory if needed
# Make a new WriteDescriptor
# Try to CAS it in
# If CAS failed go back to first step
# Complete a pending write operation
def push(vector, elem):
while True:
current_desc = vector.descriptor
complete_write(vector, current_desc.pending)
bucket = highest_bit(current_desc.size + FIRST_BUCKET_SIZE)
- highest_bit(FIRST_BUCKET_SIZE)
if vector.memory[bucket] == nullptr:
allocate_bucket(vector, bucket)
writeop = WriteDescriptor(
*at(vector, current_desc.size),
elem,
current_desc.size
)
next_desc = Descriptor(1 + current_desc.size, writeop)
if CAS(&vector.descriptor, current_desc, next_desc):
break
complete_write(vector, next_desc.pending)
# Get the current descriptor
# Complete a pending write operation
# Read the last element of the vector
# Make a new WriteDescriptor
# Try to CAS it in
# If CAS failed go back to first step
# Return the last element
def pop(vector):
while True:
current_desc = vector.descriptor
complete_write(vector, current_desc.pending)
elem = *at(current_desc.size - 1).load(Ordering)
next_desc = Descriptor(curr_desc.size - 1, null)
if CAS(&vector.descriptor, current_desc, next_desc):
break
return elem