Continuous simple storage algorithm

I am working on a free version of Simple Segregated Storage memory pool in C ++.

The SSS memory pool is similar to the slab allocator: it is basically just a piece of memory that is divided into blocks of equal sizes, and we have a free list pointer pointing to the first available block. Highlighting simply moves the pointer to the next block, and freeing just sets the free list pointer to the freed block and points the β€œnext” pointer to the freed block to the old value of the freelist pointer.

So this is basically a singly linked list.

Now I'm trying to encode an unencrypted version of the Simple Segregated Storage algorithm. Assuming that the SEGREGATION of the initial block of memory (i.e., creating a linked list) is always performed before entering a multi-threaded environment, we only need to worry about the allocation and release of blocks - in this case this problem will be very similar to blocking - a free, simply connected list, which is a well understood problem.

So, it seems to me that the distribution and release from unauthorized access can be easily done without blocking, using simple comparison and swap commands.

Assuming we have the following free list pointer:

std::atomic<unsigned char*> m_first

, , nextof(), " ". , nextof :

unsigned char*& nextof(void* ptr)
{
    return *static_cast<unsigned char**>(ptr);
}

, Boost Simple Segregated Storage.

, , allocate :

void* malloc()
{
    unsigned char* first = m_first.load();
    if (!first) return nullptr;

    while (!m_first.compare_exchange_strong(first, nextof(first))) 
    {
        if (!first) break;
    }

    return first;
}

- . , , m_first , , m_first. , , m_first.store(nextof(m_first)) - , , , , - m_first, m_first, .

, , Compare and Swap. m_first . , null, m_first CAS, , . , , m_first, , - .

- m_first , , , m_first.

:

void free(void* chunk)
{
    unsigned char* first = m_first.load();
    nextof(chunk) = first;

    while (!m_first.compare_exchange_strong(first, static_cast<unsigned char*>(chunk)))
    {
        nextof(chunk) = first;
    }
}

, , , . -, m_first . --free'd m_first. , m_first - , CAS, , m_first , . , .

, , CAS malloc free.

: , , . - , ?

+4
1

, , . , , ABA.

nextof(first), compare_exchange_strong malloc. while malloc:

while (!m_first.compare_exchange_strong(first, nextof(first)))
{
    if (!first) return nullptr;
}

, nextof(first) , compare_exchange_strong, :

while (true)
{
    auto tmp_next = nextof(first);

    // this is where the chance for ABA exists

    if (m_first.compare_exchange_strong(first, tmp_next))
        break;

    if (!first) return nullptr;
}

, m_first , nextof(m_first) :

// I will represent the linked list using letters,
// i.e. m_first -> "A" means that "A" is at the head,
// and "A" -> "B" means that "A" points to "B"

void* malloc()
{
    // at the start, m_first points to "A": m_first -> "A" -> "B" -> "C"
    unsigned char* first = m_first.load();

    // --> thread #2 calls malloc() here and acquires "A", and
    //     removes it, so: m_first -> "B" -> "C"

    if (!first) return nullptr;

    while (true)
    {
        unsigned char * tmp_next = nextof(first);
        // tmp_next is "B" at this moment, because 'first' is still "A"            

        // --> some third thread returns some even older object now,
        //     so m_first -> "M" -> "B" -> "C"

        // --> thread #2 now calls free() to return "A", resulting in
        //     my_first -> "A" -> "M" -> "B" -> "C"

        // m_first is now back to "A", but tmp_next points
        // to old "B" instead of "M", and the following line succeeds
        // and "M" is lost forever: m_first -> "B" -> "C"

        if (m_first.compare_exchange_strong(first, tmp_next))
            break;

        if (!first) return nullptr;
    }

    return first;
}

ABA , , "tagged state reference" ( - " " , , "" ), " ", (, . ).

0

All Articles