chapter 6 code
This commit is contained in:
34
ch6/Makefile
Normal file
34
ch6/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
CPP = g++
|
||||
CPPFLAGS = -g -std=c++0x
|
||||
OFLAG = -o
|
||||
.SUFFIXES : .o .cpp .c
|
||||
.cpp.o :
|
||||
$(CPP) $(CPPFLAGS) -c $<
|
||||
.c.o :
|
||||
$(CPP) $(CPPFLAGS) -c $<
|
||||
|
||||
PROGS = stack queue queuelist queuelist2 list
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
stack: stack.o
|
||||
$(CPP) $(OFLAG) stack stack.o -lpthread
|
||||
|
||||
queue: queue.o
|
||||
$(CPP) $(OFLAG) queue queue.o -lpthread
|
||||
|
||||
queuelist: queuelist.o
|
||||
$(CPP) $(OFLAG) queuelist queuelist.o -lpthread
|
||||
|
||||
queuelist2: queuelist2.o
|
||||
$(CPP) $(OFLAG) queuelist2 queuelist2.o -lpthread
|
||||
|
||||
lookuptable: lookuptable.o
|
||||
$(CPP) $(OFLAG) lookuptable lookuptable.o -lpthread
|
||||
|
||||
list: list.o
|
||||
$(CPP) $(OFLAG) list list.o -lpthread
|
||||
|
||||
clean:
|
||||
rm -f ${PROGS} *.o
|
||||
|
||||
126
ch6/list.cpp
Normal file
126
ch6/list.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <cstdio>
|
||||
|
||||
template <typename T>
|
||||
class list
|
||||
{
|
||||
struct node
|
||||
{
|
||||
std::mutex m;
|
||||
std::shared_ptr<T> data;
|
||||
std::unique_ptr<node> next;
|
||||
|
||||
node()
|
||||
: next()
|
||||
{}
|
||||
|
||||
node(T const& value)
|
||||
: data(std::make_shared<T>(value))
|
||||
{}
|
||||
};
|
||||
|
||||
node head;
|
||||
|
||||
public:
|
||||
list()
|
||||
{}
|
||||
|
||||
~list()
|
||||
{
|
||||
remove_if([](T const&){return true;});
|
||||
}
|
||||
|
||||
list(list const& other) = delete;
|
||||
list& operator=(list const& other) = delete;
|
||||
|
||||
void push_front(T const& value)
|
||||
{
|
||||
std::unique_ptr<node> new_node(new node(value));
|
||||
std::lock_guard<std::mutex> lk(head.m);
|
||||
new_node->next = std::move(head.next);
|
||||
head.next = std::move(new_node);
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
void for_each(Function f)
|
||||
{
|
||||
node* current = &head;
|
||||
std::unique_lock<std::mutex> lk(head.m);
|
||||
while (node* const next = current->next.get())
|
||||
{
|
||||
std::unique_lock<std::mutex> next_lk(next->m);
|
||||
lk.unlock();
|
||||
f(*next->data);
|
||||
current = next;
|
||||
lk = std::move(next_lk);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Predicate>
|
||||
std::shared_ptr<T> find_first_of(Predicate p)
|
||||
{
|
||||
node *current = &head;
|
||||
std::unique_lock<std::mutex> lk(head.m);
|
||||
while (node* const next = current->next.get())
|
||||
{
|
||||
std::unique_lock<std::mutex> next_lk(next->m);
|
||||
lk.unlock();
|
||||
if (p(*next->data))
|
||||
{
|
||||
return next->data;
|
||||
}
|
||||
current = next;
|
||||
lk = std::move(next_lk);
|
||||
}
|
||||
return std::shared_ptr<T>();
|
||||
}
|
||||
|
||||
template <typename Predicate>
|
||||
void remove_if(Predicate p)
|
||||
{
|
||||
node *current = &head;
|
||||
std::unique_lock<std::mutex> lk(head.m);
|
||||
while (node* const next = current->next.get())
|
||||
{
|
||||
std::unique_lock<std::mutex> next_lk(next->m);
|
||||
if (p(*next->data))
|
||||
{
|
||||
std::unique_ptr<node> old_next = std::move(current->next);
|
||||
current->next = std::move(next->next);
|
||||
next_lk.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
lk.unlock();
|
||||
current = next;
|
||||
lk = std::move(next_lk);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void push(list<int>* l)
|
||||
{
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
printf("pushing %d\n", i);
|
||||
l->push_front(i);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(int i)
|
||||
{
|
||||
printf("visit %d\n", i);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
list<int> l;
|
||||
std::thread t(push,&l);
|
||||
sleep(1);
|
||||
l.for_each(visit);
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
|
||||
133
ch6/lookuptable.cpp
Normal file
133
ch6/lookuptable.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <boost/thread/shared_mutex.hpp>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
template <typename Key, typename Value, typename Hash=std::hash<Key> >
|
||||
class lookup_table
|
||||
{
|
||||
private:
|
||||
class bucket_type
|
||||
{
|
||||
private:
|
||||
typedef std::pair<Key, Value> bucket_value;
|
||||
typedef std::list<bucket_value> bucket_data;
|
||||
typedef typename bucket_data::iterator bucket_iterator;
|
||||
|
||||
bucket_data data;
|
||||
mutable boost::shared_mutex mutex;
|
||||
|
||||
bucket_iterator find_entry_for(Key const& key) const
|
||||
{
|
||||
return std::find_if(data.begin(), data.end(),
|
||||
[&](bucket_value const& item)
|
||||
{return item.first == key;});
|
||||
}
|
||||
|
||||
public:
|
||||
Value value_for(Key const& key, Value const& default_value) const
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock(mutex);
|
||||
bucket_iterator const found_entry = find_entry_for(key);
|
||||
return (found_entry == data.end()) ?
|
||||
default_value : found_entry->second;
|
||||
}
|
||||
|
||||
void add_or_update_mapping(Key const& key, Value const& value)
|
||||
{
|
||||
std::unique_lock<boost::shared_mutex> lock(mutex);
|
||||
bucket_iterator const found_entry = find_entry_for(key);
|
||||
if (found_entry == data.end()) {
|
||||
data.push_back(bucket_value(key,value));
|
||||
}
|
||||
else {
|
||||
found_entry->second = value;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_mapping(Key const& key)
|
||||
{
|
||||
std::unique_lock<boost::shared_mutex> lock(mutex);
|
||||
bucket_iterator const found_entry = find_entry_for(key);
|
||||
if (found_entry != data.end()) {
|
||||
data.erase(found_entry);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<bucket_type> > buckets;
|
||||
Hash hasher;
|
||||
|
||||
bucket_type& get_bucket(Key const& key) const
|
||||
{
|
||||
const std::size_t bucket_index = hasher(key) % buckets.size();
|
||||
return *buckets[bucket_index];
|
||||
}
|
||||
|
||||
public:
|
||||
typedef Key key_type;
|
||||
typedef Value mapped_type;
|
||||
typedef Hash hash_type;
|
||||
|
||||
lookup_table(unsigned num_buckets = 19, const Hash& hasher_ = Hash())
|
||||
: buckets(num_buckets)
|
||||
, hasher(hasher_)
|
||||
{
|
||||
for (unsigned i = 0; i < num_buckets; ++i)
|
||||
{
|
||||
buckets[i].reset(new bucket_type);
|
||||
}
|
||||
}
|
||||
|
||||
lookup_table(const lookup_table& other) = delete;
|
||||
lookup_table& operator=(const lookup_table& other) = delete;
|
||||
|
||||
Value value_for(Key const& key,
|
||||
Value const& default_value = Value()) const
|
||||
{
|
||||
return get_bucket(key).value_for(key, default_value);
|
||||
}
|
||||
|
||||
void add_or_update_mapping(const Key& key, const Value& value)
|
||||
{
|
||||
get_bucket(key).add_or_update_mapping(key, value);
|
||||
}
|
||||
|
||||
void remove_mapping(const Key& key)
|
||||
{
|
||||
get_bucket(key).remove_mapping(key);
|
||||
}
|
||||
|
||||
std::map<Key,Value> get_map() const
|
||||
{
|
||||
std::vector<boost::unique_lock<boost::shared_mutex> > locks;
|
||||
for (unsigned i = 0; i < buckets.size(); ++i)
|
||||
{
|
||||
locks.push_back(boost::unique_lock<boost::shared_mutex>(
|
||||
buckets[i].mutex));
|
||||
}
|
||||
std::map<Key,Value> res;
|
||||
for (unsigned i = 0; i < buckets.size(); ++i)
|
||||
{
|
||||
for (typename bucket_type::bucket_iterator it = buckets[i].data.begin();
|
||||
it != buckets[i].data.end();
|
||||
++it)
|
||||
{
|
||||
res.insert(*it);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
lookup_table<int,std::string> t;
|
||||
t.add_or_update_mapping(1,"hello");
|
||||
std::cout << "value for 1 is " << t.value_for(1) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
112
ch6/queue.cpp
Normal file
112
ch6/queue.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
template <typename T>
|
||||
class queue
|
||||
{
|
||||
private:
|
||||
mutable std::mutex mut;
|
||||
std::queue<std::shared_ptr<T> > data_queue;
|
||||
std::condition_variable data_cond;
|
||||
|
||||
public:
|
||||
queue(){}
|
||||
|
||||
queue(const queue& other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(other.mut);
|
||||
data_queue = other.data_queue;
|
||||
}
|
||||
|
||||
void push(T new_value)
|
||||
{
|
||||
std::shared_ptr<T> data(new T(new_value));
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
data_queue.push(data);
|
||||
data_cond.notify_one();
|
||||
}
|
||||
|
||||
void wait_and_pop(T& value)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mut);
|
||||
data_cond.wait(lk, [&]{return !data_queue.empty();});
|
||||
/*
|
||||
while (data_queue.empty()) {
|
||||
data_cond.wait(lk);
|
||||
}
|
||||
*/
|
||||
value = *data_queue.front();
|
||||
data_queue.pop();
|
||||
}
|
||||
|
||||
std::shared_ptr<T> wait_and_pop()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mut);
|
||||
data_cond.wait(lk, [&]{return !data_queue.empty();});
|
||||
/*
|
||||
while (data_queue.empty()) {
|
||||
data_cond.wait(lk);
|
||||
}
|
||||
*/
|
||||
std::shared_ptr<T> res = data_queue.front();
|
||||
data_queue.pop();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool try_pop(T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
if (data_queue.empty())
|
||||
return false;
|
||||
value = *data_queue.front();
|
||||
data_queue.pop();
|
||||
}
|
||||
|
||||
std::shared_ptr<T> try_pop()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
if (data_queue.empty())
|
||||
return std::shared_ptr<T>();
|
||||
std::shared_ptr<T> res = data_queue.front();
|
||||
data_queue.pop();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
return data_queue.empty();
|
||||
}
|
||||
};
|
||||
|
||||
void push(queue<int>* q)
|
||||
{
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
//std::cout << "pushing " << i << std::endl;
|
||||
printf("pushing %d\n", i);
|
||||
q->push(i);
|
||||
}
|
||||
}
|
||||
|
||||
void pop(queue<int>* q)
|
||||
{
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
printf("poping %d\n", *q->wait_and_pop());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
queue<int> q;
|
||||
std::thread t1(push,&q);
|
||||
std::thread t2(pop,&q);
|
||||
t1.join();
|
||||
t2.join();
|
||||
return 0;
|
||||
}
|
||||
|
||||
94
ch6/queuelist.cpp
Normal file
94
ch6/queuelist.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
template <typename T>
|
||||
class queue
|
||||
{
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
std::shared_ptr<T> data;
|
||||
std::unique_ptr<node> next;
|
||||
};
|
||||
|
||||
std::mutex head_mutex;
|
||||
std::unique_ptr<node> head;
|
||||
std::mutex tail_mutex;
|
||||
node *tail;
|
||||
|
||||
node *get_tail()
|
||||
{
|
||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||
return tail;
|
||||
}
|
||||
|
||||
std::unique_ptr<node> pop_head()
|
||||
{
|
||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||
if (head.get() == get_tail()) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<node> old_head = std::move(head);
|
||||
head = std::move(old_head->next);
|
||||
return old_head;
|
||||
}
|
||||
|
||||
public:
|
||||
queue():head(new node),tail(head.get())
|
||||
{}
|
||||
|
||||
queue(const queue& other) = delete;
|
||||
queue& operator=(const queue& other) = delete;
|
||||
|
||||
std::shared_ptr<T> try_pop()
|
||||
{
|
||||
std::unique_ptr<node> old_head = pop_head();
|
||||
return old_head ? old_head->data : std::shared_ptr<T>();
|
||||
}
|
||||
|
||||
void push(T new_value)
|
||||
{
|
||||
std::shared_ptr<T> new_data(
|
||||
std::make_shared<T>(std::move(new_value)));
|
||||
std::unique_ptr<node> p(new node);
|
||||
node *const new_tail = p.get();
|
||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||
tail->data = new_data;
|
||||
tail->next = std::move(p);
|
||||
tail = new_tail;
|
||||
}
|
||||
};
|
||||
|
||||
void push(queue<int>* q)
|
||||
{
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
printf("pushing %d\n", i);
|
||||
q->push(i);
|
||||
}
|
||||
}
|
||||
|
||||
void pop(queue<int>* q)
|
||||
{
|
||||
int i = 0;
|
||||
while (true) {
|
||||
if (std::shared_ptr<int> p = q->try_pop()) {
|
||||
printf("poping %d\n", *p);
|
||||
++i;
|
||||
}
|
||||
if (i == 10) break;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
queue<int> q;
|
||||
std::thread th1(push,&q);
|
||||
std::thread th2(pop,&q);
|
||||
th1.join();
|
||||
th2.join();
|
||||
return 0;
|
||||
}
|
||||
|
||||
154
ch6/queuelist2.cpp
Normal file
154
ch6/queuelist2.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
template <typename T>
|
||||
class queue
|
||||
{
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
std::shared_ptr<T> data;
|
||||
std::unique_ptr<node> next;
|
||||
};
|
||||
|
||||
std::mutex head_mutex;
|
||||
std::unique_ptr<node> head;
|
||||
std::mutex tail_mutex;
|
||||
node *tail;
|
||||
std::condition_variable data_cond;
|
||||
|
||||
node *get_tail()
|
||||
{
|
||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||
return tail;
|
||||
}
|
||||
|
||||
std::unique_ptr<node> pop_head()
|
||||
{
|
||||
std::unique_ptr<node> old_head = std::move(head);
|
||||
head = std::move(old_head->next);
|
||||
return old_head;
|
||||
}
|
||||
|
||||
std::unique_ptr<node> try_pop_head()
|
||||
{
|
||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||
if (head.get() == get_tail()) {
|
||||
return std::unique_ptr<node>();
|
||||
}
|
||||
return pop_head();
|
||||
}
|
||||
|
||||
std::unique_ptr<node> try_pop_head(T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||
if (head.get() == get_tail()) {
|
||||
return std::unique_ptr<node>();
|
||||
}
|
||||
value = std::move(*head->data);
|
||||
return pop_head();
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> wait_for_data()
|
||||
{
|
||||
std::unique_lock<std::mutex> head_lock(head_mutex);
|
||||
data_cond.wait(head_lock, [&]{return head.get() != queue::get_tail();});
|
||||
return std::move(head_lock);
|
||||
}
|
||||
|
||||
std::unique_ptr<node> wait_pop_head()
|
||||
{
|
||||
std::unique_lock<std::mutex> head_lock(wait_for_data());
|
||||
return pop_head();
|
||||
}
|
||||
|
||||
std::unique_ptr<node> wait_pop_head(T& value)
|
||||
{
|
||||
std::unique_lock<std::mutex> head_lock(wait_for_data());
|
||||
value = std::move(*head->data);
|
||||
return pop_head();
|
||||
}
|
||||
|
||||
public:
|
||||
queue():head(new node),tail(head.get())
|
||||
{}
|
||||
|
||||
queue(const queue& other) = delete;
|
||||
queue& operator=(const queue& other) = delete;
|
||||
|
||||
std::shared_ptr<T> try_pop()
|
||||
{
|
||||
std::unique_ptr<node> old_head = try_pop_head();
|
||||
return old_head ? old_head->data : std::shared_ptr<T>();
|
||||
}
|
||||
|
||||
bool try_pop(T& value)
|
||||
{
|
||||
std::unique_ptr<node> const old_head = try_pop_head(value);
|
||||
return old_head;
|
||||
}
|
||||
|
||||
std::shared_ptr<T> wait_and_pop()
|
||||
{
|
||||
std::unique_ptr<node> const old_head = wait_pop_head();
|
||||
return old_head->data;
|
||||
}
|
||||
|
||||
void wait_and_pop(T& value)
|
||||
{
|
||||
std::unique_ptr<node> const old_head = wait_pop_value(value);
|
||||
}
|
||||
|
||||
void push(T new_value)
|
||||
{
|
||||
std::shared_ptr<T> new_data(
|
||||
std::make_shared<T>(std::move(new_value)));
|
||||
std::unique_ptr<node> p(new node);
|
||||
{
|
||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||
tail->data = new_data;
|
||||
node *const new_tail = p.get();
|
||||
tail->next = std::move(p);
|
||||
tail = new_tail;
|
||||
}
|
||||
data_cond.notify_one();
|
||||
}
|
||||
|
||||
void empty()
|
||||
{
|
||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||
return (head.get(0 == get_tail()));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void push(queue<int>* q)
|
||||
{
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
printf("pushing %d\n", i);
|
||||
q->push(i);
|
||||
}
|
||||
}
|
||||
|
||||
void pop(queue<int>* q)
|
||||
{
|
||||
int i = 0;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
printf("poping %d\n", *q->wait_and_pop());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
queue<int> q;
|
||||
std::thread th1(push,&q);
|
||||
std::thread th2(pop,&q);
|
||||
th1.join();
|
||||
th2.join();
|
||||
return 0;
|
||||
}
|
||||
|
||||
62
ch6/stack.cpp
Normal file
62
ch6/stack.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
template <typename T>
|
||||
class stack
|
||||
{
|
||||
private:
|
||||
std::stack<T> data;
|
||||
mutable std::mutex m;
|
||||
|
||||
public:
|
||||
stack(){}
|
||||
|
||||
stack(const stack& other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(other.m);
|
||||
data = other.data;
|
||||
}
|
||||
|
||||
stack& operator=(const stack&) = delete;
|
||||
|
||||
void push(T new_value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
data.push(new_value);
|
||||
}
|
||||
|
||||
std::shared_ptr<T> pop()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
if (data.empty()) throw std::underflow_error("empty stack");
|
||||
std::shared_ptr<T> const res(new T(data.top()));
|
||||
data.pop();
|
||||
return res;
|
||||
}
|
||||
|
||||
void pop(T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
if (data.empty()) throw std::underflow_error("empty stack");
|
||||
value = data.top();
|
||||
data.pop();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
return data.empty();
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
stack<int> s;
|
||||
s.push(1);
|
||||
std::cout << *s.pop() << std::endl;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user