Commit 6423f957 authored by O'Reilly Media, Inc.'s avatar O'Reilly Media, Inc.
Browse files

Initial commit

parents
Loading
Loading
Loading
Loading
+25 −0
Changes for High-Performance Computing and Concurrency - Working Files/Chapter 1/README.rtf: 25 added lines, 0 removed lines.
Original line number Diff line number Diff line
{\rtf1\ansi\ansicpg1252\cocoartf1348\cocoasubrtf170
\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\margl1440\margr1440\vieww9000\viewh8400\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural

\f0\fs24 \cf0 Examples in this class use GoogleTest (unit test framework) and GoogleBenchmark\
(microbenchmarking library). Before using the examples, you should build these\
tools. We have provided the source archives for both tools, in subdirectories\
gtest/ and gbench/ in this directory. You can use these or download the latest\
version from https://github.com/google/googletest and\
https://github.com/google/benchmark. Follow the instructions that accompany\
both tools to build them on your system. \
\
The makefiles in the examples assume that the test and benchmark libraries are\
built in directories gtest/ and gbench/, respectively, and that these\
directories are at the same level as the directories containing the example\
source code. For example, if you are compiling examples in the directory\
"/work/HPC/Chapter4", the makefile expects to find "../gtest", i.e.\
"/work/HPC/gtest" (and the same for gbench). You can build the GoogleTest and\
GoogleBenchmark tools anywhere else, but then you have to change the lines\
GTEST_DIR = ...  GBENCH_DIR = to point to the new location of these tools. Note\
that you should choose the location before you build the test and benchmark\
libraries, instead of moving the directories after you built the libraries.\
}
 No newline at end of file
+155 −0
Changes for High-Performance Computing and Concurrency - Working Files/Chapter 10/atomic-forward-list.h: 155 added lines, 0 removed lines.
Original line number Diff line number Diff line
#ifndef ATOMIC_FORWARD_LIST_H_
#define ATOMIC_FORWARD_LIST_H_

#include <assert.h>
#include <atomic>

#include <intr_shared_ptr.h>

template <typename T> class atomic_forward_list
{
    struct link;
    struct node;
    typedef intr_shared_ptr<node> link_iptr_t;
    typedef typename link_iptr_t::shared_ptr link_sptr_t;
    struct link {
        std::atomic<unsigned long> ref_cnt;
        link_iptr_t next;
        link() : ref_cnt(0), next(nullptr) {}
        explicit link(const link_sptr_t& p) : ref_cnt(0), next(p) {}
        ~link() {}
        link(const link& x) = delete;
        link& operator=(const link& x) = delete;
        void AddRef() { ref_cnt.fetch_add(1); }
        bool DelRef() { return ref_cnt.fetch_sub(1) == 1; }
    };
    struct node : public link {
        T data;
        explicit node(const T& x, const link_sptr_t& p) : link(p), data(x) {}
        ~node() {}
    };

    public:
    atomic_forward_list() : head_(), head_p_(static_cast<node*>(&head_)) {
        head_.AddRef(); // Make sure head is not deleted by any smart pointer
    }
    ~atomic_forward_list() {
        this->clear();
    }
    void clear() {
        while (bool(head_.next)) {
          head_.next = head_.next.get()->next;
        }
    }
    bool empty() const {
        return !bool(head_.next);
    }
    bool push_front(const T& x) {
        // Capture current head.
        link_sptr_t h(head_.next.get());
        // Create new node, link it after the current head.
        node* n = new node(x, h);
        // Change the head to point to the new node.
        // If the head changed by another thread, CAS sets h to the new head,
        // so replace "next" pointer in the new node to point to that.
        while (!head_.next.compare_exchange_strong(h, n)) {
            n->next.reset(h);
        }
        return true;
    }

    bool pop_front(T& x) {
        // Capture current head.
        link_sptr_t h(head_.next.get());
        if (!bool(h)) return false;
        // Change the head to point to the new head (next node).
        // If the head changed by another thread, CAS sets h to the new head,
        // so replace "next" pointer in the new node to point to that.
        while (!head_.next.compare_exchange_strong(h, h->next.get())) {
            if (!bool(h)) return false;
        }
        x = h->data;
        return true;
    }

    class iterator {
        public:
        ~iterator() {}
        explicit operator bool() const { return bool(p_); }
        T* operator->() const { return &(p_->data); }
        T& operator*() const { return p_->data; }
        iterator operator++() {
            p_ = p_->next.get();
            return *this;
        }
        iterator operator++(int) {
            iterator tmp(*this);
            p_ = p_->next.get();
            return tmp;
        }
        bool operator==(const iterator& rhs) const {
            return p_ == rhs.p_;
        }
        bool operator!=(const iterator& rhs) const {
            return p_ != rhs.p_;
        }
        private:
        link_sptr_t p_;
        friend class atomic_forward_list;
        explicit iterator(const link_sptr_t& p) : p_(p) {}
        iterator() : p_() {}
    };

    iterator before_begin() {
        return iterator(head_p_.get());
    }

    iterator begin() {
        return iterator(head_.next.get());
    }

    iterator end() {
        return iterator();
    }

    iterator insert_after(const iterator& pos, const T& x) {
        // Capture current position.
        link_sptr_t p(pos.p_->next.get());
        // Create new node, link it after the current position.
        node* n = new node(x, p);
        // Change the current node to point to the new node.
        // If the node changed by another thread, CAS sets p to the new node,
        // so replace "next" pointer in the new node to point to that.
        while (!pos.p_->next.compare_exchange_strong(p, n)) {
            n->next.reset(p);
        }
        return iterator(pos.p_->next.get());
    }

    iterator erase_after(const iterator& pos) {
        // Capture current position.
        link_sptr_t p(pos.p_->next.get());
        if (!bool(p)) return iterator();
        // Change the current node to point to the next node.
        // If current node changed by another thread, CAS sets p to the new head,
        // so replace "next" pointer in the new node to point to that.
        while (!pos.p_->next.compare_exchange_strong(p, p->next.get())) {
            if (!bool(p)) return iterator();
        }
        return iterator(pos.p_->next.get());
    }

    iterator find(const T& x) const {
        link_sptr_t p(head_.next.get());
        while (p && !(p->data == x)) {
            p = p->next.get();
        }
        return iterator(p);
    }

    private:
    link head_;
    link_iptr_t head_p_;
};

#endif // ATOMIC_FORWARD_LIST_H_
+344 −0
Changes for High-Performance Computing and Concurrency - Working Files/Chapter 10/atomic-forward-list_test.C: 344 added lines, 0 removed lines.
Original line number Diff line number Diff line
#include <atomic-forward-list.h>

#include <gtest/gtest.h>

#include <iomanip>
#include <iostream>
using namespace std;

static long C_count = 0;
struct C {
  size_t x;
  C() : x() { ++C_count; }
  C(size_t x) : x(x) { ++C_count; }
  C(const C& c) : x(c.x) { ++C_count; }
  ~C() { --C_count; }
};
bool operator==(const C& a, const C& b) { return a.x == b.x; }
typedef atomic_forward_list<C> list_t;
typedef list_t::iterator iterator_t;

TEST(AtomicForwardListTest, Construct) {
  ASSERT_EQ(0, C_count);
  list_t l;
  EXPECT_TRUE(l.empty());
  EXPECT_EQ(0, C_count);
}

TEST(AtomicForwardListTest, PushFront) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  EXPECT_FALSE(l.empty());
  EXPECT_EQ(1, C_count);
}

TEST(AtomicForwardListTest, DtorCleans) {
  ASSERT_EQ(0, C_count);
  {
    list_t l;
    l.push_front(C(1));
  }
  EXPECT_EQ(0, C_count);
}

TEST(AtomicForwardListTest, PushFront2) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  l.push_front(C(2));
  EXPECT_FALSE(l.empty());
  EXPECT_EQ(2, C_count);
}

TEST(AtomicForwardListTest, DtorCleans2) {
  ASSERT_EQ(0, C_count);
  {
    list_t l;
    l.push_front(C(1));
    l.push_front(C(2));
  }
  EXPECT_EQ(0, C_count);
}

TEST(AtomicForwardListTest, PopFront) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  {
    C c(0);
    EXPECT_TRUE(l.pop_front(c));
    EXPECT_EQ(1u, c.x);
  }
  EXPECT_TRUE(l.empty());
  EXPECT_EQ(0, C_count);
}

TEST(AtomicForwardListTest, Clear) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  l.push_front(C(2));
  l.clear();
  EXPECT_TRUE(l.empty());
  EXPECT_EQ(0, C_count);
}

TEST(AtomicForwardListTest, ClearPushClear) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  l.push_front(C(2));
  l.clear();
  l.push_front(C(1));
  l.push_front(C(2));
  l.push_front(C(3));
  EXPECT_FALSE(l.empty());
  EXPECT_EQ(3, C_count);
  l.clear();
  EXPECT_TRUE(l.empty());
  EXPECT_EQ(0, C_count);
}

TEST(AtomicForwardListTest, BeforeBegin) {
  ASSERT_EQ(0, C_count);
  list_t l;
  iterator_t it = l.before_begin();
  EXPECT_TRUE(bool(it));
}

TEST(AtomicForwardListTest, Begin) {
  ASSERT_EQ(0, C_count);
  list_t l;
  iterator_t it = l.begin();
  EXPECT_FALSE(bool(it));
}

TEST(AtomicForwardListTest, Begin1) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  iterator_t it = l.begin();
  EXPECT_TRUE(bool(it));
  EXPECT_EQ(1u, it->x);
  EXPECT_EQ(1u, (*it).x);
}

TEST(AtomicForwardListTest, End) {
  ASSERT_EQ(0, C_count);
  list_t l;
  iterator_t it = l.end();
  EXPECT_FALSE(bool(it));
}

TEST(AtomicForwardListTest, End1) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  iterator_t it = l.end();
  EXPECT_FALSE(bool(it));
}

TEST(AtomicForwardListTest, IteratorIncr) {
  ASSERT_EQ(0, C_count);
  list_t l;
  iterator_t it = l.before_begin();
  ++it;
  EXPECT_FALSE(bool(it));
}

TEST(AtomicForwardListTest, IteratorIncr1) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  iterator_t it = l.before_begin();
  ++it;
  EXPECT_TRUE(bool(it));
  EXPECT_EQ(1u, it->x);
  EXPECT_EQ(1u, (*it).x);
  ++it;
  EXPECT_FALSE(bool(it));
}

TEST(AtomicForwardListTest, IteratorCompare) {
  ASSERT_EQ(0, C_count);
  list_t l;
  iterator_t it1 = l.before_begin();
  EXPECT_EQ(it1, l.before_begin());
  iterator_t it2 = l.begin();
  EXPECT_NE(it1, it2);
  iterator_t it3 = l.end();
  EXPECT_NE(it1, it3);
  EXPECT_EQ(it2, it3);
  ++it1;
  EXPECT_EQ(it1, l.begin());
  EXPECT_EQ(it1, it2);
  EXPECT_EQ(it1, it3);
}

TEST(AtomicForwardListTest, IteratorCompare1) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  iterator_t it1 = l.before_begin();
  EXPECT_EQ(it1, l.before_begin());
  iterator_t it2 = l.begin();
  EXPECT_NE(it1, it2);
  iterator_t it3 = l.end();
  EXPECT_NE(it1, it3);
  EXPECT_NE(it2, it3);
  ++it1;
  EXPECT_EQ(it1, l.begin());
  EXPECT_EQ(it1, it2);
  EXPECT_NE(it1, it3);
  ++it1;
  EXPECT_EQ(it1, l.end());
  EXPECT_EQ(it1, it3);
  EXPECT_NE(it1, it2);
}

TEST(AtomicForwardListTest, IteratorIncr2) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  l.push_front(C(2));
  iterator_t it = l.before_begin();
  EXPECT_NE(it, l.end());

  EXPECT_EQ(l.begin(), ++it);
  EXPECT_TRUE(bool(it));
  EXPECT_EQ(2u, it->x);
  EXPECT_EQ(2u, (*it).x);
  EXPECT_EQ(it, l.begin());
  EXPECT_NE(it, l.end());

  EXPECT_NE(l.begin(), ++it);
  EXPECT_TRUE(bool(it));
  EXPECT_EQ(1u, it->x);
  EXPECT_EQ(1u, (*it).x);
  EXPECT_NE(it, l.end());

  EXPECT_EQ(l.end(), ++it);
  EXPECT_FALSE(bool(it));
  EXPECT_EQ(it, l.end());
}

TEST(AtomicForwardListTest, IteratorPreIncr2) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  l.push_front(C(2));
  iterator_t it = l.before_begin();
  EXPECT_NE(it, l.end());

  EXPECT_EQ(l.before_begin(), it++);
  EXPECT_TRUE(bool(it));
  EXPECT_EQ(2u, it->x);
  EXPECT_EQ(2u, (*it).x);
  EXPECT_EQ(it, l.begin());
  EXPECT_NE(it, l.end());

  EXPECT_EQ(l.begin(), it++);
  EXPECT_TRUE(bool(it));
  EXPECT_EQ(1u, it->x);
  EXPECT_EQ(1u, (*it).x);
  EXPECT_NE(it, l.end());

  EXPECT_NE(l.end(), it++);
  EXPECT_FALSE(bool(it));
  EXPECT_EQ(it, l.end());
}

TEST(AtomicForwardListTest, InsertAfterHead) {
  ASSERT_EQ(0, C_count);
  {
    list_t l;
    iterator_t it1 = l.before_begin();
    iterator_t it2 = l.insert_after(it1, C(1));
    EXPECT_TRUE(it2);
    EXPECT_FALSE(l.empty());
    EXPECT_EQ(1, C_count);
    EXPECT_EQ(1u, l.begin()->x);
    EXPECT_EQ(l.begin(), it2);
  }
  EXPECT_EQ(0, C_count);
}

TEST(AtomicForwardListTest, InsertAfterBegin) {
  ASSERT_EQ(0, C_count);
  {
    list_t l;
    l.push_front(C(1));
    iterator_t it1 = l.begin();
    iterator_t it2 = l.insert_after(it1, C(2));
    EXPECT_TRUE(it2);
    EXPECT_EQ(2, C_count);
    EXPECT_EQ(1u, l.begin()->x);
    EXPECT_EQ(2u, it2->x);
    ++it1;
    EXPECT_EQ(it1, it2);
  }
  EXPECT_EQ(0, C_count);
}

TEST(AtomicForwardListTest, EraseAfterBeforeBegin) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  l.push_front(C(2));

  iterator_t it = l.erase_after(l.before_begin());
  EXPECT_EQ(l.begin(), it);
  EXPECT_EQ(1u, it->x);
  EXPECT_FALSE(l.empty());
  EXPECT_EQ(1, C_count);

  it = l.erase_after(l.before_begin());
  EXPECT_EQ(l.begin(), it);
  EXPECT_EQ(l.end(), it);
  EXPECT_TRUE(l.empty());
  EXPECT_EQ(0, C_count);
}

TEST(AtomicForwardListTest, EraseAfterBegin) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  l.push_front(C(2));

  iterator_t it = l.erase_after(l.begin());
  EXPECT_EQ(l.end(), it);
  EXPECT_FALSE(l.empty());
  EXPECT_EQ(1, C_count);

  it = l.erase_after(l.begin());
  EXPECT_NE(l.begin(), it);
  EXPECT_EQ(l.end(), it);
  EXPECT_FALSE(l.empty());
  EXPECT_EQ(1, C_count);
}

TEST(AtomicForwardListTest, Find) {
  ASSERT_EQ(0, C_count);
  list_t l;
  l.push_front(C(1));
  l.push_front(C(2));
  l.push_front(C(3));
  iterator_t it1 = l.begin();

  iterator_t it = l.find(3);
  EXPECT_EQ(3u, it->x);
  EXPECT_EQ(l.begin(), it);

  it = l.find(2);
  EXPECT_EQ(2u, it->x);
  EXPECT_EQ(++it1, it);

  it = l.find(1);
  EXPECT_EQ(1u, it->x);
  EXPECT_EQ(++it1, it);

  it = l.find(0);
  EXPECT_EQ(++it1, it);
  EXPECT_EQ(l.end(), it);
}
Loading