Search code examples
c++liststlcontainersatomic

C++ atomic list container


I'm playing around with std::atomic but I think I didn't fully understand the concept. I'm wondering why there are no atomic Containers. So I played a little bit around. First I tried the following:

std::atomic<std::list<int> > atomicList;

But as already some other people pointed out that this doesn't work because the constructor is noexcept. So I created some kind of hack:

template<class T>
class MyList
{
public:
    //Make sure that no exception is thrown
    MyList() noexcept
        try: l()
    {}catch(...) {}

    void push_back(const T &t) { l.push_back(t); }

    void pop_front() { l.pop_front(); }

    size_t size() const { return l.size(); }

private:
    list<T> l;

};

atomic<MyList<int> > atomicList;

Now I worked with it but I discovered that it doesn't work properly and I get Segmentation fault errors.

Can somebody please explain why it is not possible to create an atomic list this way?

EDIT: If someone wants to see how my test program really looks for better understanding:

#include <list>
#include <thread>
#include <sys/time.h>
#include <iostream>
#include <atomic>

using namespace std;

template<class T>
class MyList
{

public:
    MyList() noexcept
        try: l()
    {}catch(...) {}

    void push_back(const T &t) { l.push_back(t); }

    void pop_front() { l.pop_front(); }

    size_t size() const { return l.size(); }

private:
    list<T> l;

};

atomic<MyList<int> > l;

void work()
{
    for(unsigned int i = 0; i < 100000; ++i)
    {
        //Called operator()
        ((MyList<int>&)l).push_back(i);
        ((MyList<int>&)l).push_back(((MyList<int>&)l).size());
        ((MyList<int>&)l).pop_front();
    }
}

int main(int argc, char *args[])
{
    struct timeval time1;
    struct timeval time2;
    gettimeofday(&time1, 0);
    thread t1(work);
    thread t2(work);
    thread t3(work);
    thread t4(work);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    gettimeofday(&time2, 0);
    cout<<((time2.tv_sec-time1.tv_sec)+double(time2.tv_usec-time1.tv_usec)/1000000)<<endl;
}

Solution

  • The first and most important problem: this can't possibly work. You need synchronization around the execution of the member functions, not around retrieving the list. std::atomic doesn't even begin to resemble what you need.

    Regarding your attempted implementation, casting an atomic<T> to T& can't do anything reasonable.

    And even if it were meaningful, such a cast would completely forget the atomicness of your object, and so anything you do with the references won't be atomic operations.