add original source
This commit is contained in:
BIN
source/CCiA_SourceCode.zip
Normal file
BIN
source/CCiA_SourceCode.zip
Normal file
Binary file not shown.
23
source/LICENSE_1_0.txt
Normal file
23
source/LICENSE_1_0.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
13
source/listing_1.1.cpp
Normal file
13
source/listing_1.1.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
void hello()
|
||||
{
|
||||
std::cout<<"Hello Concurrent World\n";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::thread t(hello);
|
||||
t.join();
|
||||
}
|
||||
42
source/listing_10.1.cpp
Normal file
42
source/listing_10.1.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
void test_concurrent_push_and_pop_on_empty_queue()
|
||||
{
|
||||
threadsafe_queue<int> q;
|
||||
|
||||
std::promise<void> go,push_ready,pop_ready;
|
||||
std::shared_future<void> ready(go.get_future());
|
||||
|
||||
std::future<void> push_done;
|
||||
std::future<int> pop_done;
|
||||
|
||||
try
|
||||
{
|
||||
push_done=std::async(std::launch::async,
|
||||
[&q,ready,&push_ready]()
|
||||
{
|
||||
push_ready.set_value();
|
||||
ready.wait();
|
||||
q.push(42);
|
||||
}
|
||||
);
|
||||
pop_done=std::async(std::launch::async,
|
||||
[&q,ready,&pop_ready]()
|
||||
{
|
||||
pop_ready.set_value();
|
||||
ready.wait();
|
||||
return q.pop();
|
||||
}
|
||||
);
|
||||
push_ready.get_future().wait();
|
||||
pop_ready.get_future().wait();
|
||||
go.set_value();
|
||||
|
||||
push_done.get();
|
||||
assert(pop_done.get()==42);
|
||||
assert(q.empty());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
go.set_value();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
35
source/listing_2.1.cpp
Normal file
35
source/listing_2.1.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <thread>
|
||||
|
||||
void do_something(int& i)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
struct func
|
||||
{
|
||||
int& i;
|
||||
|
||||
func(int& i_):i(i_){}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
for(unsigned j=0;j<1000000;++j)
|
||||
{
|
||||
do_something(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void oops()
|
||||
{
|
||||
int some_local_state=0;
|
||||
func my_func(some_local_state);
|
||||
std::thread my_thread(my_func);
|
||||
my_thread.detach();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
oops();
|
||||
}
|
||||
46
source/listing_2.2.cpp
Normal file
46
source/listing_2.2.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <thread>
|
||||
|
||||
void do_something(int& i)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
struct func
|
||||
{
|
||||
int& i;
|
||||
|
||||
func(int& i_):i(i_){}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
for(unsigned j=0;j<1000000;++j)
|
||||
{
|
||||
do_something(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void do_something_in_current_thread()
|
||||
{}
|
||||
|
||||
void f()
|
||||
{
|
||||
int some_local_state=0;
|
||||
func my_func(some_local_state);
|
||||
std::thread t(my_func);
|
||||
try
|
||||
{
|
||||
do_something_in_current_thread();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
t.join();
|
||||
throw;
|
||||
}
|
||||
t.join();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f();
|
||||
}
|
||||
58
source/listing_2.3.cpp
Normal file
58
source/listing_2.3.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <thread>
|
||||
|
||||
class thread_guard
|
||||
{
|
||||
std::thread& t;
|
||||
public:
|
||||
explicit thread_guard(std::thread& t_):
|
||||
t(t_)
|
||||
{}
|
||||
~thread_guard()
|
||||
{
|
||||
if(t.joinable())
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
thread_guard(thread_guard const&)=delete;
|
||||
thread_guard& operator=(thread_guard const&)=delete;
|
||||
};
|
||||
|
||||
void do_something(int& i)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
struct func
|
||||
{
|
||||
int& i;
|
||||
|
||||
func(int& i_):i(i_){}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
for(unsigned j=0;j<1000000;++j)
|
||||
{
|
||||
do_something(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void do_something_in_current_thread()
|
||||
{}
|
||||
|
||||
|
||||
void f()
|
||||
{
|
||||
int some_local_state;
|
||||
func my_func(some_local_state);
|
||||
std::thread t(my_func);
|
||||
thread_guard g(t);
|
||||
|
||||
do_something_in_current_thread();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f();
|
||||
}
|
||||
61
source/listing_2.4.cpp
Normal file
61
source/listing_2.4.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include <thread>
|
||||
#include <string>
|
||||
|
||||
void open_document_and_display_gui(std::string const& filename)
|
||||
{}
|
||||
|
||||
bool done_editing()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
enum command_type{
|
||||
open_new_document
|
||||
};
|
||||
|
||||
|
||||
struct user_command
|
||||
{
|
||||
command_type type;
|
||||
|
||||
user_command():
|
||||
type(open_new_document)
|
||||
{}
|
||||
};
|
||||
|
||||
user_command get_user_input()
|
||||
{
|
||||
return user_command();
|
||||
}
|
||||
|
||||
std::string get_filename_from_user()
|
||||
{
|
||||
return "foo.doc";
|
||||
}
|
||||
|
||||
void process_user_input(user_command const& cmd)
|
||||
{}
|
||||
|
||||
void edit_document(std::string const& filename)
|
||||
{
|
||||
open_document_and_display_gui(filename);
|
||||
while(!done_editing())
|
||||
{
|
||||
user_command cmd=get_user_input();
|
||||
if(cmd.type==open_new_document)
|
||||
{
|
||||
std::string const new_name=get_filename_from_user();
|
||||
std::thread t(edit_document,new_name);
|
||||
t.detach();
|
||||
}
|
||||
else
|
||||
{
|
||||
process_user_input(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
edit_document("bar.doc");
|
||||
}
|
||||
27
source/listing_2.5.cpp
Normal file
27
source/listing_2.5.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <thread>
|
||||
|
||||
void some_function()
|
||||
{}
|
||||
|
||||
void some_other_function(int)
|
||||
{}
|
||||
|
||||
std::thread f()
|
||||
{
|
||||
void some_function();
|
||||
return std::thread(some_function);
|
||||
}
|
||||
std::thread g()
|
||||
{
|
||||
void some_other_function(int);
|
||||
std::thread t(some_other_function,42);
|
||||
return t;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::thread t1=f();
|
||||
t1.join();
|
||||
std::thread t2=g();
|
||||
t2.join();
|
||||
}
|
||||
56
source/listing_2.6.cpp
Normal file
56
source/listing_2.6.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
class scoped_thread
|
||||
{
|
||||
std::thread t;
|
||||
public:
|
||||
explicit scoped_thread(std::thread t_):
|
||||
t(std::move(t_))
|
||||
{
|
||||
if(!t.joinable())
|
||||
throw std::logic_error("No thread");
|
||||
}
|
||||
~scoped_thread()
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
scoped_thread(scoped_thread const&)=delete;
|
||||
scoped_thread& operator=(scoped_thread const&)=delete;
|
||||
};
|
||||
|
||||
void do_something(int& i)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
struct func
|
||||
{
|
||||
int& i;
|
||||
|
||||
func(int& i_):i(i_){}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
for(unsigned j=0;j<1000000;++j)
|
||||
{
|
||||
do_something(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void do_something_in_current_thread()
|
||||
{}
|
||||
|
||||
void f()
|
||||
{
|
||||
int some_local_state;
|
||||
scoped_thread t(std::thread(func(some_local_state)));
|
||||
|
||||
do_something_in_current_thread();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f();
|
||||
}
|
||||
23
source/listing_2.7.cpp
Normal file
23
source/listing_2.7.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
void do_work(unsigned id)
|
||||
{}
|
||||
|
||||
void f()
|
||||
{
|
||||
std::vector<std::thread> threads;
|
||||
for(unsigned i=0;i<20;++i)
|
||||
{
|
||||
threads.push_back(std::thread(do_work,i));
|
||||
}
|
||||
std::for_each(threads.begin(),threads.end(),
|
||||
std::mem_fn(&std::thread::join));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f();
|
||||
}
|
||||
67
source/listing_2.8.cpp
Normal file
67
source/listing_2.8.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <thread>
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
template<typename Iterator,typename T>
|
||||
struct accumulate_block
|
||||
{
|
||||
void operator()(Iterator first,Iterator last,T& result)
|
||||
{
|
||||
result=std::accumulate(first,last,result);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Iterator,typename T>
|
||||
T parallel_accumulate(Iterator first,Iterator last,T init)
|
||||
{
|
||||
unsigned long const length=std::distance(first,last);
|
||||
|
||||
if(!length)
|
||||
return init;
|
||||
|
||||
unsigned long const min_per_thread=25;
|
||||
unsigned long const max_threads=
|
||||
(length+min_per_thread-1)/min_per_thread;
|
||||
|
||||
unsigned long const hardware_threads=
|
||||
std::thread::hardware_concurrency();
|
||||
|
||||
unsigned long const num_threads=
|
||||
std::min(hardware_threads!=0?hardware_threads:2,max_threads);
|
||||
|
||||
unsigned long const block_size=length/num_threads;
|
||||
|
||||
std::vector<T> results(num_threads);
|
||||
std::vector<std::thread> threads(num_threads-1);
|
||||
|
||||
Iterator block_start=first;
|
||||
for(unsigned long i=0;i<(num_threads-1);++i)
|
||||
{
|
||||
Iterator block_end=block_start;
|
||||
std::advance(block_end,block_size);
|
||||
threads[i]=std::thread(
|
||||
accumulate_block<Iterator,T>(),
|
||||
block_start,block_end,std::ref(results[i]));
|
||||
block_start=block_end;
|
||||
}
|
||||
accumulate_block<Iterator,T>()(block_start,last,results[num_threads-1]);
|
||||
|
||||
std::for_each(threads.begin(),threads.end(),
|
||||
std::mem_fn(&std::thread::join));
|
||||
|
||||
return std::accumulate(results.begin(),results.end(),init);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::vector<int> vi;
|
||||
for(int i=0;i<10;++i)
|
||||
{
|
||||
vi.push_back(10);
|
||||
}
|
||||
int sum=parallel_accumulate(vi.begin(),vi.end(),5);
|
||||
std::cout<<"sum="<<sum<<std::endl;
|
||||
}
|
||||
26
source/listing_3.1.cpp
Normal file
26
source/listing_3.1.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
|
||||
std::list<int> some_list;
|
||||
std::mutex some_mutex;
|
||||
|
||||
void add_to_list(int new_value)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(some_mutex);
|
||||
some_list.push_back(new_value);
|
||||
}
|
||||
bool list_contains(int value_to_find)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(some_mutex);
|
||||
return std::find(some_list.begin(),some_list.end(),value_to_find)
|
||||
!= some_list.end();
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
add_to_list(42);
|
||||
std::cout<<"contains(1)="<<list_contains(1)<<", contains(42)="<<list_contains(42)<<std::endl;
|
||||
}
|
||||
27
source/listing_3.10.cpp
Normal file
27
source/listing_3.10.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <mutex>
|
||||
class Y
|
||||
{
|
||||
private:
|
||||
int some_detail;
|
||||
mutable std::mutex m;
|
||||
|
||||
int get_detail() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock_a(m);
|
||||
return some_detail;
|
||||
}
|
||||
public:
|
||||
Y(int sd):some_detail(sd){}
|
||||
|
||||
friend bool operator==(Y const& lhs, Y const& rhs)
|
||||
{
|
||||
if(&lhs==&rhs)
|
||||
return true;
|
||||
int const lhs_value=lhs.get_detail();
|
||||
int const rhs_value=rhs.get_detail();
|
||||
return lhs_value==rhs_value;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{}
|
||||
28
source/listing_3.11.cpp
Normal file
28
source/listing_3.11.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
struct some_resource
|
||||
{
|
||||
void do_something()
|
||||
{}
|
||||
|
||||
};
|
||||
|
||||
|
||||
std::shared_ptr<some_resource> resource_ptr;
|
||||
std::mutex resource_mutex;
|
||||
void foo()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(resource_mutex);
|
||||
if(!resource_ptr)
|
||||
{
|
||||
resource_ptr.reset(new some_resource);
|
||||
}
|
||||
lk.unlock();
|
||||
resource_ptr->do_something();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
foo();
|
||||
}
|
||||
56
source/listing_3.12.cpp
Normal file
56
source/listing_3.12.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <mutex>
|
||||
|
||||
struct connection_info
|
||||
{};
|
||||
|
||||
struct data_packet
|
||||
{};
|
||||
|
||||
struct connection_handle
|
||||
{
|
||||
void send_data(data_packet const&)
|
||||
{}
|
||||
data_packet receive_data()
|
||||
{
|
||||
return data_packet();
|
||||
}
|
||||
};
|
||||
|
||||
struct remote_connection_manager
|
||||
{
|
||||
connection_handle open(connection_info const&)
|
||||
{
|
||||
return connection_handle();
|
||||
}
|
||||
} connection_manager;
|
||||
|
||||
|
||||
class X
|
||||
{
|
||||
private:
|
||||
connection_info connection_details;
|
||||
connection_handle connection;
|
||||
std::once_flag connection_init_flag;
|
||||
|
||||
void open_connection()
|
||||
{
|
||||
connection=connection_manager.open(connection_details);
|
||||
}
|
||||
public:
|
||||
X(connection_info const& connection_details_):
|
||||
connection_details(connection_details_)
|
||||
{}
|
||||
void send_data(data_packet const& data)
|
||||
{
|
||||
std::call_once(connection_init_flag,&X::open_connection,this);
|
||||
connection.send_data(data);
|
||||
}
|
||||
data_packet receive_data()
|
||||
{
|
||||
std::call_once(connection_init_flag,&X::open_connection,this);
|
||||
return connection.receive_data();
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{}
|
||||
30
source/listing_3.13.cpp
Normal file
30
source/listing_3.13.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <boost/thread/shared_mutex.hpp>
|
||||
|
||||
class dns_entry
|
||||
{};
|
||||
|
||||
class dns_cache
|
||||
{
|
||||
std::map<std::string,dns_entry> entries;
|
||||
boost::shared_mutex entry_mutex;
|
||||
public:
|
||||
dns_entry find_entry(std::string const& domain)
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lk(entry_mutex);
|
||||
std::map<std::string,dns_entry>::const_iterator const it=
|
||||
entries.find(domain);
|
||||
return (it==entries.end())?dns_entry():it->second;
|
||||
}
|
||||
void update_or_add_entry(std::string const& domain,
|
||||
dns_entry const& dns_details)
|
||||
{
|
||||
std::lock_guard<boost::shared_mutex> lk(entry_mutex);
|
||||
entries[domain]=dns_details;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{}
|
||||
44
source/listing_3.2.cpp
Normal file
44
source/listing_3.2.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <mutex>
|
||||
|
||||
class some_data
|
||||
{
|
||||
int a;
|
||||
std::string b;
|
||||
public:
|
||||
void do_something()
|
||||
{}
|
||||
};
|
||||
|
||||
class data_wrapper
|
||||
{
|
||||
private:
|
||||
some_data data;
|
||||
std::mutex m;
|
||||
public:
|
||||
template<typename Function>
|
||||
void process_data(Function func)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m);
|
||||
func(data);
|
||||
}
|
||||
};
|
||||
|
||||
some_data* unprotected;
|
||||
|
||||
void malicious_function(some_data& protected_data)
|
||||
{
|
||||
unprotected=&protected_data;
|
||||
}
|
||||
|
||||
data_wrapper x;
|
||||
|
||||
void foo()
|
||||
{
|
||||
x.process_data(malicious_function);
|
||||
unprotected->do_something();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
foo();
|
||||
}
|
||||
24
source/listing_3.3.cpp
Normal file
24
source/listing_3.3.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include <deque>
|
||||
template<typename T,typename Container=std::deque<T> >
|
||||
class stack
|
||||
{
|
||||
public:
|
||||
explicit stack(const Container&);
|
||||
explicit stack(Container&& = Container());
|
||||
template <class Alloc> explicit stack(const Alloc&);
|
||||
template <class Alloc> stack(const Container&, const Alloc&);
|
||||
template <class Alloc> stack(Container&&, const Alloc&);
|
||||
template <class Alloc> stack(stack&&, const Alloc&);
|
||||
|
||||
bool empty() const;
|
||||
size_t size() const;
|
||||
T& top();
|
||||
T const& top() const;
|
||||
void push(T const&);
|
||||
void push(T&&);
|
||||
void pop();
|
||||
void swap(stack&&);
|
||||
};
|
||||
|
||||
int main()
|
||||
{}
|
||||
24
source/listing_3.4.cpp
Normal file
24
source/listing_3.4.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
|
||||
struct empty_stack: std::exception
|
||||
{
|
||||
const char* what() const throw();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class threadsafe_stack
|
||||
{
|
||||
public:
|
||||
threadsafe_stack();
|
||||
threadsafe_stack(const threadsafe_stack&);
|
||||
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
|
||||
|
||||
void push(T new_value);
|
||||
std::shared_ptr<T> pop();
|
||||
void pop(T& value);
|
||||
bool empty() const;
|
||||
};
|
||||
|
||||
int main()
|
||||
{}
|
||||
68
source/listing_3.5.cpp
Normal file
68
source/listing_3.5.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <exception>
|
||||
#include <stack>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
struct empty_stack: std::exception
|
||||
{
|
||||
const char* what() const throw()
|
||||
{
|
||||
return "empty stack";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class threadsafe_stack
|
||||
{
|
||||
private:
|
||||
std::stack<T> data;
|
||||
mutable std::mutex m;
|
||||
public:
|
||||
threadsafe_stack(){}
|
||||
threadsafe_stack(const threadsafe_stack& other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(other.m);
|
||||
data=other.data;
|
||||
}
|
||||
threadsafe_stack& operator=(const threadsafe_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 empty_stack();
|
||||
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
|
||||
data.pop();
|
||||
return res;
|
||||
}
|
||||
void pop(T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
if(data.empty()) throw empty_stack();
|
||||
value=data.top();
|
||||
data.pop();
|
||||
}
|
||||
bool empty() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
return data.empty();
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
threadsafe_stack<int> si;
|
||||
si.push(5);
|
||||
si.pop();
|
||||
if(!si.empty())
|
||||
{
|
||||
int x;
|
||||
si.pop(x);
|
||||
}
|
||||
|
||||
}
|
||||
29
source/listing_3.6.cpp
Normal file
29
source/listing_3.6.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <mutex>
|
||||
|
||||
class some_big_object
|
||||
{};
|
||||
|
||||
void swap(some_big_object& lhs,some_big_object& rhs)
|
||||
{}
|
||||
|
||||
class X
|
||||
{
|
||||
private:
|
||||
some_big_object some_detail;
|
||||
mutable std::mutex m;
|
||||
public:
|
||||
X(some_big_object const& sd):some_detail(sd){}
|
||||
|
||||
friend void swap(X& lhs, X& rhs)
|
||||
{
|
||||
if(&lhs==&rhs)
|
||||
return;
|
||||
std::lock(lhs.m,rhs.m);
|
||||
std::lock_guard<std::mutex> lock_a(lhs.m,std::adopt_lock);
|
||||
std::lock_guard<std::mutex> lock_b(rhs.m,std::adopt_lock);
|
||||
swap(lhs.some_detail,rhs.some_detail);
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{}
|
||||
64
source/listing_3.7.cpp
Normal file
64
source/listing_3.7.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <mutex>
|
||||
|
||||
class hierarchical_mutex
|
||||
{
|
||||
public:
|
||||
explicit hierarchical_mutex(unsigned level)
|
||||
{}
|
||||
|
||||
void lock()
|
||||
{}
|
||||
void unlock()
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
hierarchical_mutex high_level_mutex(10000);
|
||||
hierarchical_mutex low_level_mutex(5000);
|
||||
|
||||
int do_low_level_stuff()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
|
||||
int low_level_func()
|
||||
{
|
||||
std::lock_guard<hierarchical_mutex> lk(low_level_mutex);
|
||||
return do_low_level_stuff();
|
||||
}
|
||||
|
||||
void high_level_stuff(int some_param)
|
||||
{}
|
||||
|
||||
|
||||
void high_level_func()
|
||||
{
|
||||
std::lock_guard<hierarchical_mutex> lk(high_level_mutex);
|
||||
high_level_stuff(low_level_func());
|
||||
}
|
||||
|
||||
void thread_a()
|
||||
{
|
||||
high_level_func();
|
||||
}
|
||||
|
||||
hierarchical_mutex other_mutex(100);
|
||||
void do_other_stuff()
|
||||
{}
|
||||
|
||||
|
||||
void other_stuff()
|
||||
{
|
||||
high_level_func();
|
||||
do_other_stuff();
|
||||
}
|
||||
|
||||
void thread_b()
|
||||
{
|
||||
std::lock_guard<hierarchical_mutex> lk(other_mutex);
|
||||
other_stuff();
|
||||
}
|
||||
|
||||
int main()
|
||||
{}
|
||||
56
source/listing_3.8.cpp
Normal file
56
source/listing_3.8.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
|
||||
class hierarchical_mutex
|
||||
{
|
||||
std::mutex internal_mutex;
|
||||
unsigned long const hierarchy_value;
|
||||
unsigned long previous_hierarchy_value;
|
||||
static thread_local unsigned long this_thread_hierarchy_value;
|
||||
|
||||
void check_for_hierarchy_violation()
|
||||
{
|
||||
if(this_thread_hierarchy_value <= hierarchy_value)
|
||||
{
|
||||
throw std::logic_error("mutex hierarchy violated");
|
||||
}
|
||||
}
|
||||
void update_hierarchy_value()
|
||||
{
|
||||
previous_hierarchy_value=this_thread_hierarchy_value;
|
||||
this_thread_hierarchy_value=hierarchy_value;
|
||||
}
|
||||
public:
|
||||
explicit hierarchical_mutex(unsigned long value):
|
||||
hierarchy_value(value),
|
||||
previous_hierarchy_value(0)
|
||||
{}
|
||||
void lock()
|
||||
{
|
||||
check_for_hierarchy_violation();
|
||||
internal_mutex.lock();
|
||||
update_hierarchy_value();
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
this_thread_hierarchy_value=previous_hierarchy_value;
|
||||
internal_mutex.unlock();
|
||||
}
|
||||
bool try_lock()
|
||||
{
|
||||
check_for_hierarchy_violation();
|
||||
if(!internal_mutex.try_lock())
|
||||
return false;
|
||||
update_hierarchy_value();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
thread_local unsigned long
|
||||
hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX);
|
||||
|
||||
int main()
|
||||
{
|
||||
hierarchical_mutex m1(42);
|
||||
hierarchical_mutex m2(2000);
|
||||
|
||||
}
|
||||
29
source/listing_3.9.cpp
Normal file
29
source/listing_3.9.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <mutex>
|
||||
|
||||
class some_big_object
|
||||
{};
|
||||
|
||||
void swap(some_big_object& lhs,some_big_object& rhs)
|
||||
{}
|
||||
|
||||
class X
|
||||
{
|
||||
private:
|
||||
some_big_object some_detail;
|
||||
mutable std::mutex m;
|
||||
public:
|
||||
X(some_big_object const& sd):some_detail(sd){}
|
||||
|
||||
friend void swap(X& lhs, X& rhs)
|
||||
{
|
||||
if(&lhs==&rhs)
|
||||
return;
|
||||
std::unique_lock<std::mutex> lock_a(lhs.m,std::defer_lock);
|
||||
std::unique_lock<std::mutex> lock_b(rhs.m,std::defer_lock);
|
||||
std::lock(lock_a,lock_b);
|
||||
swap(lhs.some_detail,rhs.some_detail);
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{}
|
||||
64
source/listing_4.1.cpp
Normal file
64
source/listing_4.1.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include <queue>
|
||||
|
||||
bool more_data_to_prepare()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
struct data_chunk
|
||||
{};
|
||||
|
||||
data_chunk prepare_data()
|
||||
{
|
||||
return data_chunk();
|
||||
}
|
||||
|
||||
void process(data_chunk&)
|
||||
{}
|
||||
|
||||
bool is_last_chunk(data_chunk&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::mutex mut;
|
||||
std::queue<data_chunk> data_queue;
|
||||
std::condition_variable data_cond;
|
||||
|
||||
void data_preparation_thread()
|
||||
{
|
||||
while(more_data_to_prepare())
|
||||
{
|
||||
data_chunk const data=prepare_data();
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
data_queue.push(data);
|
||||
data_cond.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void data_processing_thread()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mut);
|
||||
data_cond.wait(lk,[]{return !data_queue.empty();});
|
||||
data_chunk data=data_queue.front();
|
||||
data_queue.pop();
|
||||
lk.unlock();
|
||||
process(data);
|
||||
if(is_last_chunk(data))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::thread t1(data_preparation_thread);
|
||||
std::thread t2(data_processing_thread);
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
}
|
||||
27
source/listing_4.10.cpp
Normal file
27
source/listing_4.10.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <future>
|
||||
void process_connections(connection_set& connections)
|
||||
{
|
||||
while(!done(connections))
|
||||
{
|
||||
for(connection_iterator
|
||||
connection=connections.begin(),end=connections.end();
|
||||
connection!=end;
|
||||
++connection)
|
||||
{
|
||||
if(connection->has_incoming_data())
|
||||
{
|
||||
data_packet data=connection->incoming();
|
||||
std::promise<payload_type>& p=
|
||||
connection->get_promise(data.id);
|
||||
p.set_value(data.payload);
|
||||
}
|
||||
if(connection->has_outgoing_data())
|
||||
{
|
||||
outgoing_packet data=
|
||||
connection->top_of_outgoing_queue();
|
||||
connection->send(data.payload);
|
||||
data.promise.set_value(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
source/listing_4.11.cpp
Normal file
18
source/listing_4.11.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
std::condition_variable cv;
|
||||
bool done;
|
||||
std::mutex m;
|
||||
bool wait_loop()
|
||||
{
|
||||
auto const timeout= std::chrono::steady_clock::now()+
|
||||
std::chrono::milliseconds(500);
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
while(!done)
|
||||
{
|
||||
if(cv.wait_until(lk,timeout)==std::cv_status::timeout)
|
||||
break;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
24
source/listing_4.12.cpp
Normal file
24
source/listing_4.12.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
template<typename T>
|
||||
std::list<T> sequential_quick_sort(std::list<T> input)
|
||||
{
|
||||
if(input.empty())
|
||||
{
|
||||
return input;
|
||||
}
|
||||
std::list<T> result;
|
||||
result.splice(result.begin(),input,input.begin());
|
||||
T const& pivot=*result.begin();
|
||||
auto divide_point=std::partition(input.begin(),input.end(),
|
||||
[&](T const& t){return t<pivot;});
|
||||
std::list<T> lower_part;
|
||||
lower_part.splice(lower_part.end(),input,input.begin(),
|
||||
divide_point);
|
||||
auto new_lower(
|
||||
sequential_quick_sort(std::move(lower_part)));
|
||||
auto new_higher(
|
||||
sequential_quick_sort(std::move(input)));
|
||||
result.splice(result.end(),new_higher);
|
||||
Using synchronization of operations to simplify code
|
||||
result.splice(result.begin(),new_lower);
|
||||
return result;
|
||||
}
|
||||
23
source/listing_4.13.cpp
Normal file
23
source/listing_4.13.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
template<typename T>
|
||||
std::list<T> parallel_quick_sort(std::list<T> input)
|
||||
{
|
||||
if(input.empty())
|
||||
{
|
||||
return input;
|
||||
}
|
||||
std::list<T> result;
|
||||
result.splice(result.begin(),input,input.begin());
|
||||
T const& pivot=*result.begin();
|
||||
auto divide_point=std::partition(input.begin(),input.end(),
|
||||
[&](T const& t){return t<pivot;});
|
||||
std::list<T> lower_part;
|
||||
lower_part.splice(lower_part.end(),input,input.begin(),
|
||||
divide_point);
|
||||
std::future<std::list<T> > new_lower(
|
||||
std::async(¶llel_quick_sort<T>,std::move(lower_part)));
|
||||
auto new_higher(
|
||||
parallel_quick_sort(std::move(input)));
|
||||
result.splice(result.end(),new_higher);
|
||||
result.splice(result.begin(),new_lower.get());
|
||||
return result;
|
||||
}
|
||||
12
source/listing_4.14.cpp
Normal file
12
source/listing_4.14.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
template<typename F,typename A>
|
||||
std::future<std::result_of<F(A&&)>::type>
|
||||
spawn_task(F&& f,A&& a)
|
||||
{
|
||||
typedef std::result_of<F(A&&)>::type result_type;
|
||||
std::packaged_task<result_type(A&&)>
|
||||
task(std::move(f));
|
||||
std::future<result_type> res(task.get_future());
|
||||
std::thread t(std::move(task),std::move(a));
|
||||
t.detach();
|
||||
return res;
|
||||
}
|
||||
43
source/listing_4.15.cpp
Normal file
43
source/listing_4.15.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
struct card_inserted
|
||||
{
|
||||
std::string account;
|
||||
};
|
||||
class atm
|
||||
{
|
||||
messaging::receiver incoming;
|
||||
messaging::sender bank;
|
||||
messaging::sender interface_hardware;
|
||||
void (atm::*state)();
|
||||
std::string account;
|
||||
std::string pin;
|
||||
void waiting_for_card()
|
||||
{
|
||||
interface_hardware.send(display_enter_card());
|
||||
incoming.wait()
|
||||
.handle<card_inserted>(
|
||||
[&](card_inserted const& msg)
|
||||
{
|
||||
account=msg.account;
|
||||
pin="";
|
||||
interface_hardware.send(display_enter_pin());
|
||||
state=&atm::getting_pin;
|
||||
}
|
||||
);
|
||||
}
|
||||
void getting_pin();
|
||||
public:
|
||||
void run()
|
||||
{
|
||||
state=&atm::waiting_for_card;
|
||||
try
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
(this->*state)();
|
||||
}
|
||||
}
|
||||
catch(messaging::close_queue const&)
|
||||
{
|
||||
}
|
||||
}
|
||||
};
|
||||
31
source/listing_4.16.cpp
Normal file
31
source/listing_4.16.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
void atm::getting_pin()
|
||||
{
|
||||
incoming.wait()
|
||||
.handle<digit_pressed>(
|
||||
[&](digit_pressed const& msg)
|
||||
{
|
||||
unsigned const pin_length=4;
|
||||
pin+=msg.digit;
|
||||
if(pin.length()==pin_length)
|
||||
{
|
||||
bank.send(verify_pin(account,pin,incoming));
|
||||
state=&atm::verifying_pin;
|
||||
}
|
||||
}
|
||||
)
|
||||
.handle<clear_last_pressed>(
|
||||
[&](clear_last_pressed const& msg)
|
||||
{
|
||||
if(!pin.empty())
|
||||
{
|
||||
pin.resize(pin.length()-1);
|
||||
}
|
||||
}
|
||||
)
|
||||
.handle<cancel_pressed>(
|
||||
[&](cancel_pressed const& msg)
|
||||
{
|
||||
state=&atm::done_processing;
|
||||
}
|
||||
);
|
||||
}
|
||||
30
source/listing_4.2.cpp
Normal file
30
source/listing_4.2.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
template <class T, class Container = std::deque<T> >
|
||||
class queue {
|
||||
public:
|
||||
explicit queue(const Container&);
|
||||
explicit queue(Container&& = Container());
|
||||
queue(queue&& q);
|
||||
|
||||
template <class Alloc> explicit queue(const Alloc&);
|
||||
template <class Alloc> queue(const Container&, const Alloc&);
|
||||
template <class Alloc> queue(Container&&, const Alloc&);
|
||||
template <class Alloc> queue(queue&&, const Alloc&);
|
||||
|
||||
queue& operator=(queue&& q);
|
||||
void swap(queue&& q);
|
||||
|
||||
bool empty() const;
|
||||
size_type size() const;
|
||||
|
||||
T& front();
|
||||
const T& front() const;
|
||||
T& back();
|
||||
const T& back() const;
|
||||
|
||||
void push(const T& x);
|
||||
void push(T&& x);
|
||||
void pop();
|
||||
};
|
||||
|
||||
int main()
|
||||
{}
|
||||
22
source/listing_4.3.cpp
Normal file
22
source/listing_4.3.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <memory>
|
||||
template<typename T>
|
||||
class threadsafe_queue
|
||||
{
|
||||
public:
|
||||
threadsafe_queue();
|
||||
threadsafe_queue(const threadsafe_queue&);
|
||||
threadsafe_queue& operator=(const threadsafe_queue&) = delete;
|
||||
|
||||
void push(T new_value);
|
||||
|
||||
bool try_pop(T& value);
|
||||
std::shared_ptr<T> try_pop();
|
||||
|
||||
void wait_and_pop(T& value);
|
||||
std::shared_ptr<T> wait_and_pop();
|
||||
|
||||
bool empty() const;
|
||||
};
|
||||
|
||||
int main()
|
||||
{}
|
||||
58
source/listing_4.4.cpp
Normal file
58
source/listing_4.4.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
template<typename T>
|
||||
class threadsafe_queue
|
||||
{
|
||||
private:
|
||||
std::mutex mut;
|
||||
std::queue<T> data_queue;
|
||||
std::condition_variable data_cond;
|
||||
public:
|
||||
void push(T new_value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
data_queue.push(new_value);
|
||||
data_cond.notify_one();
|
||||
}
|
||||
|
||||
void wait_and_pop(T& value)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mut);
|
||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
||||
value=data_queue.front();
|
||||
data_queue.pop();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct data_chunk
|
||||
{};
|
||||
|
||||
data_chunk prepare_data();
|
||||
bool more_data_to_prepare();
|
||||
void process(data_chunk);
|
||||
bool is_last_chunk(data_chunk);
|
||||
|
||||
threadsafe_queue<data_chunk> data_queue;
|
||||
|
||||
void data_preparation_thread()
|
||||
{
|
||||
while(more_data_to_prepare())
|
||||
{
|
||||
data_chunk const data=prepare_data();
|
||||
data_queue.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
void data_processing_thread()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
data_chunk data;
|
||||
data_queue.wait_and_pop(data);
|
||||
process(data);
|
||||
if(is_last_chunk(data))
|
||||
break;
|
||||
}
|
||||
}
|
||||
73
source/listing_4.5.cpp
Normal file
73
source/listing_4.5.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
class threadsafe_queue
|
||||
{
|
||||
private:
|
||||
mutable std::mutex mut;
|
||||
std::queue<T> data_queue;
|
||||
std::condition_variable data_cond;
|
||||
public:
|
||||
threadsafe_queue()
|
||||
{}
|
||||
threadsafe_queue(threadsafe_queue const& other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(other.mut);
|
||||
data_queue=other.data_queue;
|
||||
}
|
||||
|
||||
void push(T new_value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
data_queue.push(new_value);
|
||||
data_cond.notify_one();
|
||||
}
|
||||
|
||||
void wait_and_pop(T& value)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mut);
|
||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
||||
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,[this]{return !data_queue.empty();});
|
||||
std::shared_ptr<T> res(std::make_shared<T>(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(std::make_shared<T>(data_queue.front()));
|
||||
data_queue.pop();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
return data_queue.empty();
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{}
|
||||
16
source/listing_4.6.cpp
Normal file
16
source/listing_4.6.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
int find_the_answer_to_ltuae()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
void do_other_stuff()
|
||||
{}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::future<int> the_answer=std::async(find_the_answer_to_ltuae);
|
||||
do_other_stuff();
|
||||
std::cout<<"The answer is "<<the_answer.get()<<std::endl;
|
||||
}
|
||||
34
source/listing_4.7.cpp
Normal file
34
source/listing_4.7.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <string>
|
||||
#include <future>
|
||||
|
||||
struct X
|
||||
{
|
||||
void foo(int,std::string const&);
|
||||
std::string bar(std::string const&);
|
||||
};
|
||||
|
||||
|
||||
X x;
|
||||
auto f1=std::async(&X::foo,&x,42,"hello");
|
||||
auto f2=std::async(&X::bar,x,"goodbye");
|
||||
|
||||
struct Y
|
||||
{
|
||||
double operator()(double);
|
||||
};
|
||||
Y y;
|
||||
auto f3=std::async(Y(),3.141);
|
||||
auto f4=std::async(std::ref(y),2.718);
|
||||
X baz(X&);
|
||||
auto f6=std::async(baz,std::ref(x));
|
||||
class move_only
|
||||
{
|
||||
public:
|
||||
move_only();
|
||||
move_only(move_only&&);
|
||||
move_only(move_only const&) = delete;
|
||||
move_only& operator=(move_only&&);
|
||||
move_only& operator=(move_only const&) = delete;
|
||||
void operator()();
|
||||
};
|
||||
auto f5=std::async(move_only());
|
||||
9
source/listing_4.8.cpp
Normal file
9
source/listing_4.8.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
template<>
|
||||
class packaged_task<std::string(std::vector<char>*,int)>
|
||||
{
|
||||
public:
|
||||
template<typename Callable>
|
||||
explicit packaged_task(Callable&& f);
|
||||
std::future<std::string> get_future();
|
||||
void operator()(std::vector<char>*,int);
|
||||
};
|
||||
40
source/listing_4.9.cpp
Normal file
40
source/listing_4.9.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
std::mutex m;
|
||||
std::deque<std::packaged_task<void()> > tasks;
|
||||
|
||||
bool gui_shutdown_message_received();
|
||||
void get_and_process_gui_message();
|
||||
|
||||
void gui_thread()
|
||||
{
|
||||
while(!gui_shutdown_message_received())
|
||||
{
|
||||
get_and_process_gui_message();
|
||||
std::packaged_task<void()> task;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m);
|
||||
if(tasks.empty())
|
||||
continue;
|
||||
task=std::move(tasks.front());
|
||||
tasks.pop_front();
|
||||
}
|
||||
task();
|
||||
}
|
||||
}
|
||||
|
||||
std::thread gui_bg_thread(gui_thread);
|
||||
|
||||
template<typename Func>
|
||||
std::future<void> post_task_for_gui_thread(Func f)
|
||||
{
|
||||
std::packaged_task<void()> task(f);
|
||||
std::future<void> res=task.get_future();
|
||||
std::lock_guard<std::mutex> lk(m);
|
||||
tasks.push_back(std::move(task));
|
||||
return res;
|
||||
}
|
||||
16
source/listing_5.1.cpp
Normal file
16
source/listing_5.1.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
class spinlock_mutex
|
||||
{
|
||||
std::atomic_flag flag;
|
||||
public:
|
||||
spinlock_mutex():
|
||||
flag(ATOMIC_FLAG_INIT)
|
||||
{}
|
||||
void lock()
|
||||
{
|
||||
while(flag.test_and_set(std::memory_order_acquire));
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
flag.clear(std::memory_order_release);
|
||||
}
|
||||
};
|
||||
39
source/listing_5.10.cpp
Normal file
39
source/listing_5.10.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <assert.h>
|
||||
struct X
|
||||
{
|
||||
int i;
|
||||
std::string s;
|
||||
};
|
||||
|
||||
std::atomic<X*> p;
|
||||
std::atomic<int> a;
|
||||
|
||||
void create_x()
|
||||
{
|
||||
X* x=new X;
|
||||
x->i=42;
|
||||
x->s="hello";
|
||||
a.store(99,std::memory_order_relaxed);
|
||||
p.store(x,std::memory_order_release);
|
||||
}
|
||||
|
||||
void use_x()
|
||||
{
|
||||
X* x;
|
||||
while(!(x=p.load(std::memory_order_consume)))
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
assert(x->i==42);
|
||||
assert(x->s=="hello");
|
||||
assert(a.load(std::memory_order_relaxed)==99);
|
||||
}
|
||||
int main()
|
||||
{
|
||||
std::thread t1(create_x);
|
||||
std::thread t2(use_x);
|
||||
t1.join();
|
||||
t2.join();
|
||||
}
|
||||
|
||||
41
source/listing_5.11.cpp
Normal file
41
source/listing_5.11.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
std::vector<int> queue_data;
|
||||
std::atomic<int> count;
|
||||
|
||||
void populate_queue()
|
||||
{
|
||||
unsigned const number_of_items=20;
|
||||
queue_data.clear();
|
||||
for(unsigned i=0;i<number_of_items;++i)
|
||||
{
|
||||
queue_data.push_back(i);
|
||||
}
|
||||
|
||||
count.store(number_of_items,std::memory_order_release);
|
||||
}
|
||||
|
||||
void consume_queue_items()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
int item_index;
|
||||
if((item_index=count.fetch_sub(1,std::memory_order_acquire))<=0)
|
||||
{
|
||||
wait_for_more_items();
|
||||
continue;
|
||||
}
|
||||
process(queue_data[item_index-1]);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::thread a(populate_queue);
|
||||
std::thread b(consume_queue_items);
|
||||
std::thread c(consume_queue_items);
|
||||
a.join();
|
||||
b.join();
|
||||
c.join();
|
||||
}
|
||||
33
source/listing_5.12.cpp
Normal file
33
source/listing_5.12.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <assert.h>
|
||||
|
||||
std::atomic<bool> x,y;
|
||||
std::atomic<int> z;
|
||||
|
||||
void write_x_then_y()
|
||||
{
|
||||
x.store(true,std::memory_order_relaxed);
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
y.store(true,std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void read_y_then_x()
|
||||
{
|
||||
while(!y.load(std::memory_order_relaxed));
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
if(x.load(std::memory_order_relaxed))
|
||||
++z;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
x=false;
|
||||
y=false;
|
||||
z=0;
|
||||
std::thread a(write_x_then_y);
|
||||
std::thread b(read_y_then_x);
|
||||
a.join();
|
||||
b.join();
|
||||
assert(z.load()!=0);
|
||||
}
|
||||
34
source/listing_5.13.cpp
Normal file
34
source/listing_5.13.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <assert.h>
|
||||
|
||||
bool x=false;
|
||||
std::atomic<bool> y;
|
||||
std::atomic<int> z;
|
||||
|
||||
void write_x_then_y()
|
||||
{
|
||||
x=true;
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
y.store(true,std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void read_y_then_x()
|
||||
{
|
||||
while(!y.load(std::memory_order_relaxed));
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
if(x)
|
||||
++z;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
x=false;
|
||||
y=false;
|
||||
z=0;
|
||||
std::thread a(write_x_then_y);
|
||||
std::thread b(read_y_then_x);
|
||||
a.join();
|
||||
b.join();
|
||||
assert(z.load()!=0);
|
||||
}
|
||||
22
source/listing_5.2.cpp
Normal file
22
source/listing_5.2.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
std::vector<int> data;
|
||||
std::atomic_bool data_ready(false);
|
||||
|
||||
void reader_thread()
|
||||
{
|
||||
while(!data_ready.load())
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
std::cout<<"The answer="<<data[0]<<"\n";
|
||||
}
|
||||
void writer_thread()
|
||||
{
|
||||
data.push_back(42);
|
||||
data_ready=true;
|
||||
}
|
||||
17
source/listing_5.3.cpp
Normal file
17
source/listing_5.3.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <iostream>
|
||||
|
||||
void foo(int a,int b)
|
||||
{
|
||||
std::cout<<a<<”,”<<b<<std::endl;
|
||||
}
|
||||
|
||||
int get_num()
|
||||
{
|
||||
static int i=0;
|
||||
return ++i;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
foo(get_num(),get_num());
|
||||
}
|
||||
46
source/listing_5.4.cpp
Normal file
46
source/listing_5.4.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <assert.h>
|
||||
|
||||
std::atomic<bool> x,y;
|
||||
std::atomic<int> z;
|
||||
|
||||
void write_x()
|
||||
{
|
||||
x.store(true,std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
void write_y()
|
||||
{
|
||||
y.store(true,std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
void read_x_then_y()
|
||||
{
|
||||
while(!x.load(std::memory_order_seq_cst));
|
||||
if(y.load(std::memory_order_seq_cst))
|
||||
++z;
|
||||
}
|
||||
|
||||
void read_y_then_x()
|
||||
{
|
||||
while(!y.load(std::memory_order_seq_cst));
|
||||
if(x.load(std::memory_order_seq_cst))
|
||||
++z;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
x=false;
|
||||
y=false;
|
||||
z=0;
|
||||
std::thread a(write_x);
|
||||
std::thread b(write_y);
|
||||
std::thread c(read_x_then_y);
|
||||
std::thread d(read_y_then_x);
|
||||
a.join();
|
||||
b.join();
|
||||
c.join();
|
||||
d.join();
|
||||
assert(z.load()!=0);
|
||||
}
|
||||
31
source/listing_5.5.cpp
Normal file
31
source/listing_5.5.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <assert.h>
|
||||
|
||||
std::atomic<bool> x,y;
|
||||
std::atomic<int> z;
|
||||
|
||||
void write_x_then_y()
|
||||
{
|
||||
x.store(true,std::memory_order_relaxed);
|
||||
y.store(true,std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void read_y_then_x()
|
||||
{
|
||||
while(!y.load(std::memory_order_relaxed));
|
||||
if(x.load(std::memory_order_relaxed))
|
||||
++z;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
x=false;
|
||||
y=false;
|
||||
z=0;
|
||||
std::thread a(write_x_then_y);
|
||||
std::thread b(read_y_then_x);
|
||||
a.join();
|
||||
b.join();
|
||||
assert(z.load()!=0);
|
||||
}
|
||||
74
source/listing_5.6.cpp
Normal file
74
source/listing_5.6.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
|
||||
std::atomic<int> x(0),y(0),z(0);
|
||||
std::atomic<bool> go(false);
|
||||
unsigned const loop_count=10;
|
||||
|
||||
struct read_values
|
||||
{
|
||||
int x,y,z;
|
||||
};
|
||||
read_values values1[loop_count];
|
||||
read_values values2[loop_count];
|
||||
read_values values3[loop_count];
|
||||
read_values values4[loop_count];
|
||||
read_values values5[loop_count];
|
||||
void increment(std::atomic<int>* var_to_inc,read_values* values)
|
||||
{
|
||||
while(!go)
|
||||
std::this_thread::yield();
|
||||
for(unsigned i=0;i<loop_count;++i)
|
||||
{
|
||||
values[i].x=x.load(std::memory_order_relaxed);
|
||||
values[i].y=y.load(std::memory_order_relaxed);
|
||||
values[i].z=z.load(std::memory_order_relaxed);
|
||||
var_to_inc->store(i+1,std::memory_order_relaxed);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
void read_vals(read_values* values)
|
||||
{
|
||||
while(!go)
|
||||
std::this_thread::yield();
|
||||
for(unsigned i=0;i<loop_count;++i)
|
||||
{
|
||||
values[i].x=x.load(std::memory_order_relaxed);
|
||||
values[i].y=y.load(std::memory_order_relaxed);
|
||||
values[i].z=z.load(std::memory_order_relaxed);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
void print(read_values* v)
|
||||
{
|
||||
for(unsigned i=0;i<loop_count;++i)
|
||||
{
|
||||
if(i)
|
||||
std::cout<<",";
|
||||
std::cout<<"("<<v[i].x<<","<<v[i].y<<","<<v[i].z<<")";
|
||||
}
|
||||
std::cout<<std::endl;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::thread t1(increment,&x,values1);
|
||||
std::thread t2(increment,&y,values2);
|
||||
std::thread t3(increment,&z,values3);
|
||||
std::thread t4(read_vals,values4);
|
||||
std::thread t5(read_vals,values5);
|
||||
go=true;
|
||||
t5.join();
|
||||
t4.join();
|
||||
t3.join();
|
||||
t2.join();
|
||||
t1.join();
|
||||
print(values1);
|
||||
print(values2);
|
||||
print(values3);
|
||||
print(values4);
|
||||
print(values5);
|
||||
}
|
||||
46
source/listing_5.7.cpp
Normal file
46
source/listing_5.7.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <assert.h>
|
||||
|
||||
std::atomic<bool> x,y;
|
||||
std::atomic<int> z;
|
||||
|
||||
void write_x()
|
||||
{
|
||||
x.store(true,std::memory_order_release);
|
||||
}
|
||||
|
||||
void write_y()
|
||||
{
|
||||
y.store(true,std::memory_order_release);
|
||||
}
|
||||
|
||||
void read_x_then_y()
|
||||
{
|
||||
while(!x.load(std::memory_order_acquire));
|
||||
if(y.load(std::memory_order_acquire))
|
||||
++z;
|
||||
}
|
||||
|
||||
void read_y_then_x()
|
||||
{
|
||||
while(!y.load(std::memory_order_acquire));
|
||||
if(x.load(std::memory_order_acquire))
|
||||
++z;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
x=false;
|
||||
y=false;
|
||||
z=0;
|
||||
std::thread a(write_x);
|
||||
std::thread b(write_y);
|
||||
std::thread c(read_x_then_y);
|
||||
std::thread d(read_y_then_x);
|
||||
a.join();
|
||||
b.join();
|
||||
c.join();
|
||||
d.join();
|
||||
assert(z.load()!=0);
|
||||
}
|
||||
31
source/listing_5.8.cpp
Normal file
31
source/listing_5.8.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <assert.h>
|
||||
|
||||
std::atomic<bool> x,y;
|
||||
std::atomic<int> z;
|
||||
|
||||
void write_x_then_y()
|
||||
{
|
||||
x.store(true,std::memory_order_relaxed);
|
||||
y.store(true,std::memory_order_release);
|
||||
}
|
||||
|
||||
void read_y_then_x()
|
||||
{
|
||||
while(!y.load(std::memory_order_acquire));
|
||||
if(x.load(std::memory_order_relaxed))
|
||||
++z;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
x=false;
|
||||
y=false;
|
||||
z=0;
|
||||
std::thread a(write_x_then_y);
|
||||
std::thread b(read_y_then_x);
|
||||
a.join();
|
||||
b.join();
|
||||
assert(z.load()!=0);
|
||||
}
|
||||
42
source/listing_5.9.cpp
Normal file
42
source/listing_5.9.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <assert.h>
|
||||
|
||||
std::atomic<int> data[5];
|
||||
std::atomic<bool> sync1(false),sync2(false);
|
||||
|
||||
void thread_1()
|
||||
{
|
||||
data[0].store(42,std::memory_order_relaxed);
|
||||
data[1].store(97,std::memory_order_relaxed);
|
||||
data[2].store(17,std::memory_order_relaxed);
|
||||
data[3].store(-141,std::memory_order_relaxed);
|
||||
data[4].store(2003,std::memory_order_relaxed);
|
||||
sync1.store(true,std::memory_order_release);
|
||||
}
|
||||
|
||||
void thread_2()
|
||||
{
|
||||
while(!sync1.load(std::memory_order_acquire));
|
||||
sync2.store(std::memory_order_release);
|
||||
}
|
||||
|
||||
void thread_3()
|
||||
{
|
||||
while(!sync2.load(std::memory_order_acquire));
|
||||
assert(data[0].load(std::memory_order_relaxed)==42);
|
||||
assert(data[1].load(std::memory_order_relaxed)==97);
|
||||
assert(data[2].load(std::memory_order_relaxed)==17);
|
||||
assert(data[3].load(std::memory_order_relaxed)==-141);
|
||||
assert(data[4].load(std::memory_order_relaxed)==2003);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::thread t1(thread_1);
|
||||
std::thread t2(thread_2);
|
||||
std::thread t3(thread_3);
|
||||
t1.join();
|
||||
t2.join();
|
||||
t3.join();
|
||||
}
|
||||
56
source/listing_6.1.cpp
Normal file
56
source/listing_6.1.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <exception>
|
||||
#include <stack>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
struct empty_stack: std::exception
|
||||
{
|
||||
const char* what() const throw()
|
||||
{
|
||||
return "empty stack";
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class threadsafe_stack
|
||||
{
|
||||
private:
|
||||
std::stack<T> data;
|
||||
mutable std::mutex m;
|
||||
public:
|
||||
threadsafe_stack(){}
|
||||
threadsafe_stack(const threadsafe_stack& other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(other.m);
|
||||
data=other.data;
|
||||
}
|
||||
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
|
||||
|
||||
void push(T new_value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
data.push(std::move(new_value));
|
||||
}
|
||||
std::shared_ptr<T> pop()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
if(data.empty()) throw empty_stack();
|
||||
std::shared_ptr<T> const res(
|
||||
std::make_shared<T>(std::move(data.top())));
|
||||
data.pop();
|
||||
return res;
|
||||
}
|
||||
void pop(T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
if(data.empty()) throw empty_stack();
|
||||
value=std::move(data.top());
|
||||
data.pop();
|
||||
}
|
||||
bool empty() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
return data.empty();
|
||||
}
|
||||
};
|
||||
|
||||
44
source/listing_6.10.cpp
Normal file
44
source/listing_6.10.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
template<typename T>
|
||||
class threadsafe_queue
|
||||
{
|
||||
private:
|
||||
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();
|
||||
}
|
||||
|
||||
public:
|
||||
std::shared_ptr<T> try_pop()
|
||||
{
|
||||
std::unique_ptr<node> const 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;
|
||||
}
|
||||
|
||||
void empty()
|
||||
{
|
||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||
return (head==get_tail());
|
||||
}
|
||||
};
|
||||
108
source/listing_6.11.cpp
Normal file
108
source/listing_6.11.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
#include <boost/thread/shared_mutex.hpp>
|
||||
|
||||
template<typename Key,typename Value,typename Hash=std::hash<Key> >
|
||||
class threadsafe_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
|
||||
{
|
||||
std::size_t const bucket_index=hasher(key)%buckets.size();
|
||||
return *buckets[bucket_index];
|
||||
}
|
||||
|
||||
public:
|
||||
typedef Key key_type;
|
||||
typedef Value mapped_type;
|
||||
typedef Hash hash_type;
|
||||
|
||||
threadsafe_lookup_table(
|
||||
unsigned num_buckets=19, Hash const& hasher_=Hash()):
|
||||
buckets(num_buckets),hasher(hasher_)
|
||||
{
|
||||
for(unsigned i=0;i<num_buckets;++i)
|
||||
{
|
||||
buckets[i].reset(new bucket_type);
|
||||
}
|
||||
}
|
||||
|
||||
threadsafe_lookup_table(threadsafe_lookup_table const& other)=delete;
|
||||
threadsafe_lookup_table& operator=(
|
||||
threadsafe_lookup_table const& 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(Key const& key,Value const& value)
|
||||
{
|
||||
get_bucket(key).add_or_update_mapping(key,value);
|
||||
}
|
||||
|
||||
void remove_mapping(Key const& key)
|
||||
{
|
||||
get_bucket(key).remove_mapping(key);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
20
source/listing_6.12.cpp
Normal file
20
source/listing_6.12.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
std::map<Key,Value> threadsafe_lookup_table::get_map() const
|
||||
{
|
||||
std::vector<std::unique_lock<boost::shared_mutex> > locks;
|
||||
for(unsigned i=0;i<buckets.size();++i)
|
||||
{
|
||||
locks.push_back(
|
||||
std::unique_lock<boost::shared_mutex>(buckets[i].mutex));
|
||||
}
|
||||
std::map<Key,Value> res;
|
||||
for(unsigned i=0;i<buckets.size();++i)
|
||||
{
|
||||
for(bucket_iterator it=buckets[i].data.begin();
|
||||
it!=buckets[i].data.end();
|
||||
++it)
|
||||
{
|
||||
res.insert(*it);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
101
source/listing_6.13.cpp
Normal file
101
source/listing_6.13.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
template<typename T>
|
||||
class threadsafe_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:
|
||||
threadsafe_list()
|
||||
{}
|
||||
|
||||
~threadsafe_list()
|
||||
{
|
||||
remove_if([](T const&){return true;});
|
||||
}
|
||||
|
||||
threadsafe_list(threadsafe_list const& other)=delete;
|
||||
threadsafe_list& operator=(threadsafe_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_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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
72
source/listing_6.2.cpp
Normal file
72
source/listing_6.2.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
class threadsafe_queue
|
||||
{
|
||||
private:
|
||||
mutable std::mutex mut;
|
||||
std::queue<T> data_queue;
|
||||
std::condition_variable data_cond;
|
||||
public:
|
||||
threadsafe_queue()
|
||||
{}
|
||||
|
||||
void push(T new_value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
data_queue.push(std::move(new_value));
|
||||
data_cond.notify_one();
|
||||
}
|
||||
|
||||
void wait_and_pop(T& value)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mut);
|
||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
||||
value=std::move(data_queue.front());
|
||||
data_queue.pop();
|
||||
}
|
||||
|
||||
std::shared_ptr<T> wait_and_pop()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mut);
|
||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
||||
std::shared_ptr<T> res(
|
||||
std::make_shared<T>(std::move(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=std::move(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(
|
||||
std::make_shared<T>(std::move(data_queue.front())));
|
||||
data_queue.pop();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
return data_queue.empty();
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
threadsafe_queue<int> rq;
|
||||
}
|
||||
68
source/listing_6.3.cpp
Normal file
68
source/listing_6.3.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
class threadsafe_queue
|
||||
{
|
||||
private:
|
||||
mutable std::mutex mut;
|
||||
std::queue<std::shared_ptr<T> > data_queue;
|
||||
std::condition_variable data_cond;
|
||||
public:
|
||||
threadsafe_queue()
|
||||
{}
|
||||
|
||||
void wait_and_pop(T& value)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mut);
|
||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
||||
value=std::move(*data_queue.front());
|
||||
data_queue.pop();
|
||||
}
|
||||
|
||||
bool try_pop(T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
if(data_queue.empty())
|
||||
return false;
|
||||
value=std::move(*data_queue.front());
|
||||
data_queue.pop();
|
||||
}
|
||||
|
||||
std::shared_ptr<T> wait_and_pop()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mut);
|
||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
||||
std::shared_ptr<T> res=data_queue.front();
|
||||
data_queue.pop();
|
||||
return res;
|
||||
}
|
||||
|
||||
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(T new_value)
|
||||
{
|
||||
std::shared_ptr<T> data(
|
||||
std::make_shared<T>(std::move(new_value)));
|
||||
std::lock_guard<std::mutex> lk(mut);
|
||||
data_queue.push(data);
|
||||
data_cond.notify_one();
|
||||
}
|
||||
|
||||
};
|
||||
56
source/listing_6.4.cpp
Normal file
56
source/listing_6.4.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
class queue
|
||||
{
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
T data;
|
||||
std::unique_ptr<node> next;
|
||||
|
||||
node(T data_):
|
||||
data(std::move(data_))
|
||||
{}
|
||||
};
|
||||
|
||||
std::unique_ptr<node> head;
|
||||
node* tail;
|
||||
|
||||
public:
|
||||
queue():
|
||||
tail(nullptr)
|
||||
{}
|
||||
|
||||
queue(const queue& other)=delete;
|
||||
queue& operator=(const queue& other)=delete;
|
||||
|
||||
std::shared_ptr<T> try_pop()
|
||||
{
|
||||
if(!head)
|
||||
{
|
||||
return std::shared_ptr<T>();
|
||||
}
|
||||
std::shared_ptr<T> const res(
|
||||
std::make_shared<T>(std::move(head->data)));
|
||||
std::unique_ptr<node> const old_head=std::move(head);
|
||||
head=std::move(old_head->next);
|
||||
return res;
|
||||
}
|
||||
|
||||
void push(T new_value)
|
||||
{
|
||||
std::unique_ptr<node> p(new node(std::move(new_value)));
|
||||
node* const new_tail=p.get();
|
||||
if(tail)
|
||||
{
|
||||
tail->next=std::move(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
head=std::move(p);
|
||||
}
|
||||
tail=new_tail;
|
||||
}
|
||||
};
|
||||
|
||||
46
source/listing_6.5.cpp
Normal file
46
source/listing_6.5.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <memory>
|
||||
template<typename T>
|
||||
class queue
|
||||
{
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
std::shared_ptr<T> data;
|
||||
std::unique_ptr<node> next;
|
||||
};
|
||||
|
||||
std::unique_ptr<node> head;
|
||||
node* tail;
|
||||
|
||||
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()
|
||||
{
|
||||
if(head.get()==tail)
|
||||
{
|
||||
return std::shared_ptr<T>();
|
||||
}
|
||||
std::shared_ptr<T> const res(head->data);
|
||||
std::unique_ptr<node> const old_head=std::move(head);
|
||||
head=std::move(old_head->next);
|
||||
return res;
|
||||
}
|
||||
|
||||
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);
|
||||
tail->data=new_data;
|
||||
node* const new_tail=p.get();
|
||||
tail->next=std::move(p);
|
||||
tail=new_tail;
|
||||
}
|
||||
};
|
||||
|
||||
64
source/listing_6.6.cpp
Normal file
64
source/listing_6.6.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
template<typename T>
|
||||
class threadsafe_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> const old_head=std::move(head);
|
||||
head=std::move(old_head->next);
|
||||
return old_head;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
threadsafe_queue():
|
||||
head(new node),tail(head.get())
|
||||
{}
|
||||
|
||||
threadsafe_queue(const threadsafe_queue& other)=delete;
|
||||
threadsafe_queue& operator=(const threadsafe_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;
|
||||
}
|
||||
};
|
||||
|
||||
29
source/listing_6.7.cpp
Normal file
29
source/listing_6.7.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
template<typename T>
|
||||
class threadsafe_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;
|
||||
public:
|
||||
threadsafe_queue():
|
||||
head(new node),tail(head.get())
|
||||
{}
|
||||
threadsafe_queue(const threadsafe_queue& other)=delete;
|
||||
threadsafe_queue& operator=(const threadsafe_queue& other)=delete;
|
||||
|
||||
std::shared_ptr<T> try_pop();
|
||||
bool try_pop(T& value);
|
||||
std::shared_ptr<T> wait_and_pop();
|
||||
void wait_and_pop(T& value);
|
||||
void push(T new_value);
|
||||
void empty();
|
||||
};
|
||||
15
source/listing_6.8.cpp
Normal file
15
source/listing_6.8.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
template<typename T>
|
||||
void threadsafe_queue<T>::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();
|
||||
}
|
||||
49
source/listing_6.9.cpp
Normal file
49
source/listing_6.9.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
template<typename T>
|
||||
class threadsafe_queue
|
||||
{
|
||||
private:
|
||||
node* get_tail()
|
||||
{
|
||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||
return tail;
|
||||
}
|
||||
|
||||
std::unique_ptr<node> pop_head()
|
||||
{
|
||||
std::unique_ptr<node> const old_head=std::move(head);
|
||||
head=std::move(old_head->next);
|
||||
return old_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_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:
|
||||
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_head(value);
|
||||
}
|
||||
};
|
||||
18
source/listing_7.1.cpp
Normal file
18
source/listing_7.1.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <atomic>
|
||||
|
||||
class spinlock_mutex
|
||||
{
|
||||
std::atomic_flag flag;
|
||||
public:
|
||||
spinlock_mutex():
|
||||
flag(ATOMIC_FLAG_INIT)
|
||||
{}
|
||||
void lock()
|
||||
{
|
||||
while(flag.test_and_set(std::memory_order_acquire));
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
flag.clear(std::memory_order_release);
|
||||
}
|
||||
};
|
||||
38
source/listing_7.10.cpp
Normal file
38
source/listing_7.10.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
class lock_free_stack
|
||||
{
|
||||
private:
|
||||
struct node;
|
||||
struct counted_node_ptr
|
||||
{
|
||||
int external_count;
|
||||
node* ptr;
|
||||
};
|
||||
struct node
|
||||
{
|
||||
std::shared_ptr<T> data;
|
||||
std::atomic<int> internal_count;
|
||||
counted_node_ptr next;
|
||||
node(T const& data_):
|
||||
data(std::make_shared<T>(data_)),
|
||||
internal_count(0)
|
||||
{}
|
||||
};
|
||||
std::atomic<counted_node_ptr> head;
|
||||
public:
|
||||
~lock_free_stack()
|
||||
{
|
||||
while(pop());
|
||||
}
|
||||
void push(T const& data)
|
||||
{
|
||||
counted_node_ptr new_node;
|
||||
new_node.ptr=new node(data);
|
||||
new_node.external_count=1;
|
||||
new_node.ptr->next=head.load();
|
||||
while(!head.compare_exchange_weak(new_node.ptr->next,new_node));
|
||||
}
|
||||
};
|
||||
46
source/listing_7.11.cpp
Normal file
46
source/listing_7.11.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
template<typename T>
|
||||
class lock_free_stack
|
||||
{
|
||||
private:
|
||||
void increase_head_count(counted_node_ptr& old_counter)
|
||||
{
|
||||
counted_node_ptr new_counter;
|
||||
do
|
||||
{
|
||||
new_counter=old_counter;
|
||||
++new_counter.external_count;
|
||||
}
|
||||
while(!head.compare_exchange_strong(old_counter,new_counter));
|
||||
old_counter.external_count=new_counter.external_count;
|
||||
}
|
||||
public:
|
||||
std::shared_ptr<T> pop()
|
||||
{
|
||||
counted_node_ptr old_head=head.load();
|
||||
for(;;)
|
||||
{
|
||||
increase_head_count(old_head);
|
||||
node* const ptr=old_head.ptr;
|
||||
if(!ptr)
|
||||
{
|
||||
return std::shared_ptr<T>();
|
||||
}
|
||||
if(head.compare_exchange_strong(old_head,ptr->next))
|
||||
{
|
||||
std::shared_ptr<T> res;
|
||||
res.swap(ptr->data);
|
||||
int const count_increase=old_head.external_count-2;
|
||||
if(ptr->internal_count.fetch_add(count_increase)==
|
||||
-count_increase)
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
else if(ptr->internal_count.fetch_sub(1)==1)
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
88
source/listing_7.12.cpp
Normal file
88
source/listing_7.12.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
class lock_free_stack
|
||||
{
|
||||
private:
|
||||
struct node;
|
||||
struct counted_node_ptr
|
||||
{
|
||||
int external_count;
|
||||
node* ptr;
|
||||
};
|
||||
struct node
|
||||
{
|
||||
std::shared_ptr<T> data;
|
||||
std::atomic<int> internal_count;
|
||||
counted_node_ptr next;
|
||||
node(T const& data_):
|
||||
data(std::make_shared<T>(data_)),
|
||||
internal_count(0)
|
||||
{}
|
||||
};
|
||||
std::atomic<counted_node_ptr> head;
|
||||
void increase_head_count(counted_node_ptr& old_counter)
|
||||
{
|
||||
counted_node_ptr new_counter;
|
||||
do
|
||||
{
|
||||
new_counter=old_counter;
|
||||
++new_counter.external_count;
|
||||
}
|
||||
while(!head.compare_exchange_strong(
|
||||
old_counter,new_counter,
|
||||
std::memory_order_acquire,
|
||||
std::memory_order_relaxed));
|
||||
old_counter.external_count=new_counter.external_count;
|
||||
}
|
||||
public:
|
||||
~lock_free_stack()
|
||||
{
|
||||
while(pop());
|
||||
}
|
||||
void push(T const& data)
|
||||
{
|
||||
counted_node_ptr new_node;
|
||||
new_node.ptr=new node(data);
|
||||
new_node.external_count=1;
|
||||
new_node.ptr->next=head.load(std::memory_order_relaxed)
|
||||
while(!head.compare_exchange_weak(
|
||||
new_node.ptr->next,new_node,
|
||||
std::memory_order_release,
|
||||
std::memory_order_relaxed));
|
||||
}
|
||||
std::shared_ptr<T> pop()
|
||||
{
|
||||
counted_node_ptr old_head=
|
||||
head.load(std::memory_order_relaxed);
|
||||
for(;;)
|
||||
{
|
||||
increase_head_count(old_head);
|
||||
node* const ptr=old_head.ptr;
|
||||
if(!ptr)
|
||||
{
|
||||
return std::shared_ptr<T>();
|
||||
}
|
||||
if(head.compare_exchange_strong(
|
||||
old_head,ptr->next,std::memory_order_relaxed))
|
||||
{
|
||||
std::shared_ptr<T> res;
|
||||
res.swap(ptr->data);
|
||||
int const count_increase=old_head.external_count-2;
|
||||
if(ptr->internal_count.fetch_add(
|
||||
count_increase,std::memory_order_release)==-count_increase)
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
else if(ptr->internal_count.fetch_add(
|
||||
-1,std::memory_order_relaxed)==1)
|
||||
{
|
||||
ptr->internal_count.load(std::memory_order_acquire);
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
62
source/listing_7.13.cpp
Normal file
62
source/listing_7.13.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
template<typename T>
|
||||
class lock_free_queue
|
||||
{
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
std::shared_ptr<T> data;
|
||||
node* next;
|
||||
node():
|
||||
next(nullptr)
|
||||
{}
|
||||
};
|
||||
std::atomic<node*> head;
|
||||
std::atomic<node*> tail;
|
||||
node* pop_head()
|
||||
{
|
||||
node* const old_head=head.load();
|
||||
if(old_head==tail.load())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
head.store(old_head->next);
|
||||
return old_head;
|
||||
}
|
||||
public:
|
||||
lock_free_queue():
|
||||
head(new node),tail(head.load())
|
||||
{}
|
||||
lock_free_queue(const lock_free_queue& other)=delete;
|
||||
lock_free_queue& operator=(const lock_free_queue& other)=delete;
|
||||
~lock_free_queue()
|
||||
{
|
||||
while(node* const old_head=head.load())
|
||||
{
|
||||
head.store(old_head->next);
|
||||
delete old_head;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<T> pop()
|
||||
{
|
||||
node* old_head=pop_head();
|
||||
if(!old_head)
|
||||
{
|
||||
return std::shared_ptr<T>();
|
||||
}
|
||||
std::shared_ptr<T> const res(old_head->data);
|
||||
delete old_head;
|
||||
return res;
|
||||
}
|
||||
void push(T new_value)
|
||||
{
|
||||
std::shared_ptr<T> new_data(std::make_shared<T>(new_value));
|
||||
node* p=new node;
|
||||
node* const old_tail=tail.load();
|
||||
old_tail->data.swap(new_data);
|
||||
old_tail->next=p;
|
||||
tail.store(p);
|
||||
}
|
||||
};
|
||||
20
source/listing_7.14.cpp
Normal file
20
source/listing_7.14.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
void push(T new_value)
|
||||
{
|
||||
std::unique_ptr<T> new_data(new T(new_value));
|
||||
counted_node_ptr new_next;
|
||||
new_next.ptr=new node;
|
||||
new_next.external_count=1;
|
||||
for(;;)
|
||||
{
|
||||
node* const old_tail=tail.load();
|
||||
T* old_data=nullptr;
|
||||
if(old_tail->data.compare_exchange_strong(
|
||||
old_data,new_data.get()))
|
||||
{
|
||||
old_tail->next=new_next;
|
||||
tail.store(new_next.ptr);
|
||||
new_data.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
source/listing_7.15.cpp
Normal file
59
source/listing_7.15.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include <atomic>
|
||||
|
||||
template<typename T>
|
||||
class lock_free_queue
|
||||
{
|
||||
private:
|
||||
struct node;
|
||||
struct counted_node_ptr
|
||||
{
|
||||
int external_count;
|
||||
node* ptr;
|
||||
};
|
||||
std::atomic<counted_node_ptr> head;
|
||||
std::atomic<counted_node_ptr> tail;
|
||||
struct node_counter
|
||||
{
|
||||
unsigned internal_count:30;
|
||||
unsigned external_counters:2;
|
||||
};
|
||||
struct node
|
||||
{
|
||||
std::atomic<T*> data;
|
||||
std::atomic<node_counter> count;
|
||||
counted_node_ptr next;
|
||||
node()
|
||||
{
|
||||
node_counter new_count;
|
||||
new_count.internal_count=0;
|
||||
new_count.external_counters=2;
|
||||
count.store(new_count);
|
||||
next.ptr=nullptr;
|
||||
next.external_count=0;
|
||||
}
|
||||
};
|
||||
public:
|
||||
void push(T new_value)
|
||||
{
|
||||
std::unique_ptr<T> new_data(new T(new_value));
|
||||
counted_node_ptr new_next;
|
||||
new_next.ptr=new node;
|
||||
new_next.external_count=1;
|
||||
counted_node_ptr old_tail=tail.load();
|
||||
for(;;)
|
||||
{
|
||||
increase_external_count(tail,old_tail);
|
||||
T* old_data=nullptr;
|
||||
if(old_tail.ptr->data.compare_exchange_strong(
|
||||
old_data,new_data.get()))
|
||||
{
|
||||
old_tail.ptr->next=new_next;
|
||||
old_tail=tail.exchange(new_next);
|
||||
free_external_counter(old_tail);
|
||||
new_data.release();
|
||||
break;
|
||||
}
|
||||
old_tail.ptr->release_ref();
|
||||
}
|
||||
}
|
||||
};
|
||||
31
source/listing_7.16.cpp
Normal file
31
source/listing_7.16.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
template<typename T>
|
||||
class lock_free_queue
|
||||
{
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
void release_ref();
|
||||
};
|
||||
public:
|
||||
std::unique_ptr<T> pop()
|
||||
{
|
||||
counted_node_ptr old_head=head.load(std::memory_order_relaxed);
|
||||
for(;;)
|
||||
{
|
||||
increase_external_count(head,old_head);
|
||||
node* const ptr=old_head.ptr;
|
||||
if(ptr==tail.load().ptr)
|
||||
{
|
||||
ptr->release_ref();
|
||||
return std::unique_ptr<T>();
|
||||
}
|
||||
if(head.compare_exchange_strong(old_head,ptr->next))
|
||||
{
|
||||
T* const res=ptr->data.exchange(nullptr);
|
||||
free_external_counter(old_head);
|
||||
return std::unique_ptr<T>(res);
|
||||
}
|
||||
ptr->release_ref();
|
||||
}
|
||||
}
|
||||
};
|
||||
27
source/listing_7.17.cpp
Normal file
27
source/listing_7.17.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
template<typename T>
|
||||
class lock_free_queue
|
||||
{
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
void release_ref()
|
||||
{
|
||||
node_counter old_counter=
|
||||
count.load(std::memory_order_relaxed);
|
||||
node_counter new_counter;
|
||||
do
|
||||
{
|
||||
new_counter=old_counter;
|
||||
--new_counter.internal_count;
|
||||
}
|
||||
while(!count.compare_exchange_strong(
|
||||
old_counter,new_counter,
|
||||
std::memory_order_acquire,std::memory_order_relaxed));
|
||||
if(!new_counter.internal_count &&
|
||||
!new_counter.external_counters)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
20
source/listing_7.18.cpp
Normal file
20
source/listing_7.18.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
template<typename T>
|
||||
class lock_free_queue
|
||||
{
|
||||
private:
|
||||
static void increase_external_count(
|
||||
std::atomic<counted_node_ptr>& counter,
|
||||
counted_node_ptr& old_counter)
|
||||
{
|
||||
counted_node_ptr new_counter;
|
||||
do
|
||||
{
|
||||
new_counter=old_counter;
|
||||
++new_counter.external_count;
|
||||
}
|
||||
while(!counter.compare_exchange_strong(
|
||||
old_counter,new_counter,
|
||||
std::memory_order_acquire,std::memory_order_relaxed));
|
||||
old_counter.external_count=new_counter.external_count;
|
||||
}
|
||||
};
|
||||
27
source/listing_7.19.cpp
Normal file
27
source/listing_7.19.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
template<typename T>
|
||||
class lock_free_queue
|
||||
{
|
||||
private:
|
||||
static void free_external_counter(counted_node_ptr &old_node_ptr)
|
||||
{
|
||||
node* const ptr=old_node_ptr.ptr;
|
||||
int const count_increase=old_node_ptr.external_count-2;
|
||||
node_counter old_counter=
|
||||
ptr->count.load(std::memory_order_relaxed);
|
||||
node_counter new_counter;
|
||||
do
|
||||
{
|
||||
new_counter=old_counter;
|
||||
--new_counter.external_counters;
|
||||
new_counter.internal_count+=count_increase;
|
||||
}
|
||||
while(!ptr->count.compare_exchange_strong(
|
||||
old_counter,new_counter,
|
||||
std::memory_order_acquire,std::memory_order_relaxed));
|
||||
if(!new_counter.internal_count &&
|
||||
!new_counter.external_counters)
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
23
source/listing_7.2.cpp
Normal file
23
source/listing_7.2.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <atomic>
|
||||
|
||||
template<typename T>
|
||||
class lock_free_stack
|
||||
{
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
T data;
|
||||
node* next;
|
||||
node(T const& data_):
|
||||
data(data_)
|
||||
{}
|
||||
};
|
||||
std::atomic<node*> head;
|
||||
public:
|
||||
void push(T const& data)
|
||||
{
|
||||
node* const new_node=new node(data);
|
||||
new_node->next=head.load();
|
||||
while(!head.compare_exchange_weak(new_node->next,new_node));
|
||||
}
|
||||
};
|
||||
33
source/listing_7.20.cpp
Normal file
33
source/listing_7.20.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
template<typename T>
|
||||
class lock_free_queue
|
||||
{
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
std::atomic<T*> data;
|
||||
std::atomic<node_counter> count;
|
||||
std::atomic<counted_node_ptr> next;
|
||||
};
|
||||
public:
|
||||
std::unique_ptr<T> pop()
|
||||
{
|
||||
counted_node_ptr old_head=head.load(std::memory_order_relaxed);
|
||||
for(;;)
|
||||
{
|
||||
increase_external_count(head,old_head);
|
||||
node* const ptr=old_head.ptr;
|
||||
if(ptr==tail.load().ptr)
|
||||
{
|
||||
return std::unique_ptr<T>();
|
||||
}
|
||||
counted_node_ptr next=ptr->next.load();
|
||||
if(head.compare_exchange_strong(old_head,next))
|
||||
{
|
||||
T* const res=ptr->data.exchange(nullptr);
|
||||
free_external_counter(old_head);
|
||||
return std::unique_ptr<T>(res);
|
||||
}
|
||||
ptr->release_ref();
|
||||
}
|
||||
}
|
||||
};
|
||||
55
source/listing_7.21.cpp
Normal file
55
source/listing_7.21.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
template<typename T>
|
||||
class lock_free_queue
|
||||
{
|
||||
private:
|
||||
void set_new_tail(counted_node_ptr &old_tail,
|
||||
counted_node_ptr const &new_tail)
|
||||
{
|
||||
node* const current_tail_ptr=old_tail.ptr;
|
||||
while(!tail.compare_exchange_weak(old_tail,new_tail) &&
|
||||
old_tail.ptr==current_tail_ptr);
|
||||
if(old_tail.ptr==current_tail_ptr)
|
||||
free_external_counter(old_tail);
|
||||
else
|
||||
current_tail_ptr->release_ref();
|
||||
}
|
||||
public:
|
||||
void push(T new_value)
|
||||
{
|
||||
std::unique_ptr<T> new_data(new T(new_value));
|
||||
counted_node_ptr new_next;
|
||||
new_next.ptr=new node;
|
||||
new_next.external_count=1;
|
||||
counted_node_ptr old_tail=tail.load();
|
||||
for(;;)
|
||||
{
|
||||
increase_external_count(tail,old_tail);
|
||||
T* old_data=nullptr;
|
||||
if(old_tail.ptr->data.compare_exchange_strong(
|
||||
old_data,new_data.get()))
|
||||
{
|
||||
counted_node_ptr old_next={0};
|
||||
if(!old_tail.ptr->next.compare_exchange_strong(
|
||||
old_next,new_next))
|
||||
{
|
||||
delete new_next.ptr;
|
||||
new_next=old_next;
|
||||
}
|
||||
set_new_tail(old_tail, new_next);
|
||||
new_data.release();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
counted_node_ptr old_next={0};
|
||||
if(old_tail.ptr->next.compare_exchange_strong(
|
||||
old_next,new_next))
|
||||
{
|
||||
old_next=new_next;
|
||||
new_next.ptr=new node;
|
||||
}
|
||||
set_new_tail(old_tail, old_next);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
31
source/listing_7.3.cpp
Normal file
31
source/listing_7.3.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
class lock_free_stack
|
||||
{
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
std::shared_ptr<T> data;
|
||||
node* next;
|
||||
node(T const& data_):
|
||||
data(std::make_shared<T>(data_))
|
||||
{}
|
||||
};
|
||||
std::atomic<node*> head;
|
||||
public:
|
||||
void push(T const& data)
|
||||
{
|
||||
node* const new_node=new node(data);
|
||||
new_node->next=head.load();
|
||||
while(!head.compare_exchange_weak(new_node->next,new_node));
|
||||
}
|
||||
std::shared_ptr<T> pop()
|
||||
{
|
||||
node* old_head=head.load();
|
||||
while(old_head &&
|
||||
!head.compare_exchange_weak(old_head,old_head->next));
|
||||
return old_head ? old_head->data : std::shared_ptr<T>();
|
||||
}
|
||||
};
|
||||
25
source/listing_7.4.cpp
Normal file
25
source/listing_7.4.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
class lock_free_stack
|
||||
{
|
||||
private:
|
||||
std::atomic<unsigned> threads_in_pop;
|
||||
void try_reclaim(node* old_head);
|
||||
public:
|
||||
std::shared_ptr<T> pop()
|
||||
{
|
||||
++threads_in_pop;
|
||||
node* old_head=head.load();
|
||||
while(old_head &&
|
||||
!head.compare_exchange_weak(old_head,old_head->next));
|
||||
std::shared_ptr<T> res;
|
||||
if(old_head)
|
||||
{
|
||||
res.swap(old_head->data);
|
||||
}
|
||||
try_reclaim(old_head);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
57
source/listing_7.5.cpp
Normal file
57
source/listing_7.5.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include <atomic>
|
||||
|
||||
template<typename T>
|
||||
class lock_free_stack
|
||||
{
|
||||
private:
|
||||
std::atomic<node*> to_be_deleted;
|
||||
static void delete_nodes(node* nodes)
|
||||
{
|
||||
while(nodes)
|
||||
{
|
||||
node* next=nodes->next;
|
||||
delete nodes;
|
||||
nodes=next;
|
||||
}
|
||||
}
|
||||
void try_reclaim(node* old_head)
|
||||
{
|
||||
if(threads_in_pop==1)
|
||||
{
|
||||
node* nodes_to_delete=to_be_deleted.exchange(nullptr);
|
||||
if(!--threads_in_pop)
|
||||
{
|
||||
delete_nodes(nodes_to_delete);
|
||||
}
|
||||
else if(nodes_to_delete)
|
||||
{
|
||||
chain_pending_nodes(nodes_to_delete);
|
||||
}
|
||||
delete old_head;
|
||||
}
|
||||
else
|
||||
{
|
||||
chain_pending_node(old_head);
|
||||
--threads_in_pop;
|
||||
}
|
||||
}
|
||||
void chain_pending_nodes(node* nodes)
|
||||
{
|
||||
node* last=nodes;
|
||||
while(node* const next=last->next)
|
||||
{
|
||||
last=next;
|
||||
}
|
||||
chain_pending_nodes(nodes,last);
|
||||
}
|
||||
void chain_pending_nodes(node* first,node* last)
|
||||
{
|
||||
last->next=to_be_deleted;
|
||||
while(!to_be_deleted.compare_exchange_weak(
|
||||
last->next,first));
|
||||
}
|
||||
void chain_pending_node(node* n)
|
||||
{
|
||||
chain_pending_nodes(n,n);
|
||||
}
|
||||
};
|
||||
36
source/listing_7.6.cpp
Normal file
36
source/listing_7.6.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
std::shared_ptr<T> pop()
|
||||
{
|
||||
std::atomic<void*>& hp=get_hazard_pointer_for_current_thread();
|
||||
node* old_head=head.load();
|
||||
do
|
||||
{
|
||||
node* temp;
|
||||
do
|
||||
{
|
||||
temp=old_head;
|
||||
hp.store(old_head);
|
||||
old_head=head.load();
|
||||
} while(old_head!=temp);
|
||||
}
|
||||
while(old_head &&
|
||||
!head.compare_exchange_strong(old_head,old_head->next));
|
||||
hp.store(nullptr);
|
||||
std::shared_ptr<T> res;
|
||||
if(old_head)
|
||||
{
|
||||
res.swap(old_head->data);
|
||||
if(outstanding_hazard_pointers_for(old_head))
|
||||
{
|
||||
reclaim_later(old_head);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete old_head;
|
||||
}
|
||||
delete_nodes_with_no_hazards();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
49
source/listing_7.7.cpp
Normal file
49
source/listing_7.7.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
unsigned const max_hazard_pointers=100;
|
||||
struct hazard_pointer
|
||||
{
|
||||
std::atomic<std::thread::id> id;
|
||||
std::atomic<void*> pointer;
|
||||
};
|
||||
hazard_pointer hazard_pointers[max_hazard_pointers];
|
||||
class hp_owner
|
||||
{
|
||||
hazard_pointer* hp;
|
||||
public:
|
||||
hp_owner(hp_owner const&)=delete;
|
||||
hp_owner operator=(hp_owner const&)=delete;
|
||||
hp_owner():
|
||||
hp(nullptr)
|
||||
{
|
||||
for(unsigned i=0;i<max_hazard_pointers;++i)
|
||||
{
|
||||
std::thread::id old_id;
|
||||
if(hazard_pointers[i].id.compare_exchange_strong(
|
||||
old_id,std::this_thread::get_id()))
|
||||
{
|
||||
hp=&hazard_pointers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!hp)
|
||||
{
|
||||
throw std::runtime_error("No hazard pointers available");
|
||||
}
|
||||
}
|
||||
std::atomic<void*>& get_pointer()
|
||||
{
|
||||
return hp->pointer;
|
||||
}
|
||||
~hp_owner()
|
||||
{
|
||||
hp->pointer.store(nullptr);
|
||||
hp->id.store(std::thread::id());
|
||||
}
|
||||
};
|
||||
std::atomic<void*>& get_hazard_pointer_for_current_thread()
|
||||
{
|
||||
thread_local static hp_owner hazard;
|
||||
return hazard.get_pointer();
|
||||
}
|
||||
51
source/listing_7.8.cpp
Normal file
51
source/listing_7.8.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <atomic>
|
||||
|
||||
template<typename T>
|
||||
void do_delete(void* p)
|
||||
{
|
||||
delete static_cast<T*>(p);
|
||||
}
|
||||
struct data_to_reclaim
|
||||
{
|
||||
void* data;
|
||||
std::function<void(void*)> deleter;
|
||||
data_to_reclaim* next;
|
||||
template<typename T>
|
||||
data_to_reclaim(T* p):
|
||||
data(p),
|
||||
deleter(&do_delete<T>),
|
||||
next(0)
|
||||
{}
|
||||
~data_to_reclaim()
|
||||
{
|
||||
deleter(data);
|
||||
}
|
||||
};
|
||||
std::atomic<data_to_reclaim*> nodes_to_reclaim;
|
||||
void add_to_reclaim_list(data_to_reclaim* node)
|
||||
{
|
||||
node->next=nodes_to_reclaim.load();
|
||||
while(!nodes_to_reclaim.compare_exchange_weak(node->next,node));
|
||||
}
|
||||
template<typename T>
|
||||
void reclaim_later(T* data)
|
||||
{
|
||||
add_to_reclaim_list(new data_to_reclaim(data));
|
||||
}
|
||||
void delete_nodes_with_no_hazards()
|
||||
{
|
||||
data_to_reclaim* current=nodes_to_reclaim.exchange(nullptr);
|
||||
while(current)
|
||||
{
|
||||
data_to_reclaim* const next=current->next;
|
||||
if(!outstanding_hazard_pointers_for(current->data))
|
||||
{
|
||||
delete current;
|
||||
}
|
||||
else
|
||||
{
|
||||
add_to_reclaim_list(current);
|
||||
}
|
||||
current=next;
|
||||
}
|
||||
}
|
||||
32
source/listing_7.9.cpp
Normal file
32
source/listing_7.9.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
class lock_free_stack
|
||||
{
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
std::shared_ptr<T> data;
|
||||
std::shared_ptr<node> next;
|
||||
node(T const& data_):
|
||||
data(std::make_shared<T>(data_))
|
||||
{}
|
||||
};
|
||||
std::shared_ptr<node> head;
|
||||
public:
|
||||
void push(T const& data)
|
||||
{
|
||||
std::shared_ptr<node> const new_node=std::make_shared<node>(data);
|
||||
new_node->next=head.load();
|
||||
while(!std::atomic_compare_exchange_weak(
|
||||
&head,&new_node->next,new_node));
|
||||
}
|
||||
std::shared_ptr<T> pop()
|
||||
{
|
||||
std::shared_ptr<node> old_head=std::atomic_load(&head);
|
||||
while(old_head && !std::atomic_compare_exchange_weak(
|
||||
&head,&old_head,old_head->next));
|
||||
return old_head ? old_head->data : std::shared_ptr<T>();
|
||||
}
|
||||
};
|
||||
102
source/listing_8.1.cpp
Normal file
102
source/listing_8.1.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
template<typename T>
|
||||
struct sorter
|
||||
{
|
||||
struct chunk_to_sort
|
||||
{
|
||||
std::list<T> data;
|
||||
std::promise<std::list<T> > promise;
|
||||
};
|
||||
|
||||
thread_safe_stack<chunk_to_sort> chunks;
|
||||
std::vector<std::thread> threads;
|
||||
unsigned const max_thread_count;
|
||||
std::atomic<bool> end_of_data;
|
||||
|
||||
sorter():
|
||||
max_thread_count(std::thread::hardware_concurrency()-1),
|
||||
end_of_data(false)
|
||||
{}
|
||||
|
||||
~sorter()
|
||||
{
|
||||
end_of_data=true;
|
||||
for(unsigned i=0;i<threads.size();++i)
|
||||
{
|
||||
threads[i].join();
|
||||
}
|
||||
}
|
||||
|
||||
void try_sort_chunk()
|
||||
{
|
||||
boost::shared_ptr<chunk_to_sort > chunk=chunks.pop();
|
||||
if(chunk)
|
||||
{
|
||||
sort_chunk(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
std::list<T> do_sort(std::list<T>& chunk_data)
|
||||
{
|
||||
if(chunk_data.empty())
|
||||
{
|
||||
return chunk_data;
|
||||
}
|
||||
|
||||
std::list<T> result;
|
||||
result.splice(result.begin(),chunk_data,chunk_data.begin());
|
||||
T const& partition_val=*result.begin();
|
||||
|
||||
typename std::list<T>::iterator divide_point=
|
||||
std::partition(chunk_data.begin(),chunk_data.end(),
|
||||
[&](T const& val){return val<partition_val;});
|
||||
chunk_to_sort new_lower_chunk;
|
||||
new_lower_chunk.data.splice(new_lower_chunk.data.end(),
|
||||
chunk_data,chunk_data.begin(),
|
||||
divide_point);
|
||||
|
||||
std::future<std::list<T> > new_lower=
|
||||
new_lower_chunk.promise.get_future();
|
||||
chunks.push(std::move(new_lower_chunk));
|
||||
if(threads.size()<max_thread_count)
|
||||
{
|
||||
threads.push_back(std::thread(&sorter<T>::sort_thread,this));
|
||||
}
|
||||
|
||||
std::list<T> new_higher(do_sort(chunk_data));
|
||||
|
||||
result.splice(result.end(),new_higher);
|
||||
while(new_lower.wait_for(std::chrono::seconds(0)) !=
|
||||
std::future_status::ready)
|
||||
{
|
||||
try_sort_chunk();
|
||||
}
|
||||
|
||||
result.splice(result.begin(),new_lower.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
void sort_chunk(boost::shared_ptr<chunk_to_sort > const& chunk)
|
||||
{
|
||||
chunk->promise.set_value(do_sort(chunk->data));
|
||||
}
|
||||
|
||||
void sort_thread()
|
||||
{
|
||||
while(!end_of_data)
|
||||
{
|
||||
try_sort_chunk();
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
std::list<T> parallel_quick_sort(std::list<T> input)
|
||||
{
|
||||
if(input.empty())
|
||||
{
|
||||
return input;
|
||||
}
|
||||
sorter<T> s;
|
||||
return s.do_sort(input);
|
||||
}
|
||||
45
source/listing_8.10.cpp
Normal file
45
source/listing_8.10.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
template<typename Iterator,typename MatchType>
|
||||
Iterator parallel_find_impl(Iterator first,Iterator last,MatchType match,
|
||||
std::atomic<bool>& done)
|
||||
{
|
||||
try
|
||||
{
|
||||
unsigned long const length=std::distance(first,last);
|
||||
unsigned long const min_per_thread=25;
|
||||
if(length<(2*min_per_thread))
|
||||
{
|
||||
for(;(first!=last) && !done.load();++first)
|
||||
{
|
||||
if(*first==match)
|
||||
{
|
||||
done=true;
|
||||
return first;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator const mid_point=first+(length/2);
|
||||
std::future<Iterator> async_result=
|
||||
std::async(¶llel_find_impl<Iterator,MatchType>,
|
||||
mid_point,last,match,std::ref(done));
|
||||
Iterator const direct_result=
|
||||
parallel_find_impl(first,mid_point,match,done);
|
||||
return (direct_result==mid_point)?
|
||||
async_result.get():direct_result;
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
done=true;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Iterator,typename MatchType>
|
||||
Iterator parallel_find(Iterator first,Iterator last,MatchType match)
|
||||
{
|
||||
std::atomic<bool> done(false);
|
||||
return parallel_find_impl(first,last,match,done);
|
||||
}
|
||||
93
source/listing_8.11.cpp
Normal file
93
source/listing_8.11.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
template<typename Iterator>
|
||||
void parallel_partial_sum(Iterator first,Iterator last)
|
||||
{
|
||||
typedef typename Iterator::value_type value_type;
|
||||
struct process_chunk
|
||||
{
|
||||
void operator()(Iterator begin,Iterator last,
|
||||
std::future<value_type>* previous_end_value,
|
||||
std::promise<value_type>* end_value)
|
||||
{
|
||||
try
|
||||
{
|
||||
Iterator end=last;
|
||||
++end;
|
||||
std::partial_sum(begin,end,begin);
|
||||
if(previous_end_value)
|
||||
{
|
||||
value_type& addend=previous_end_value->get();
|
||||
*last+=addend;
|
||||
if(end_value)
|
||||
{
|
||||
end_value->set_value(*last);
|
||||
}
|
||||
std::for_each(begin,last,[addend](value_type& item)
|
||||
{
|
||||
item+=addend;
|
||||
});
|
||||
}
|
||||
else if(end_value)
|
||||
{
|
||||
end_value->set_value(*last);
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
if(end_value)
|
||||
{
|
||||
end_value->set_exception(std::current_exception());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
unsigned long const length=std::distance(first,last);
|
||||
|
||||
if(!length)
|
||||
return last;
|
||||
|
||||
unsigned long const min_per_thread=25;
|
||||
unsigned long const max_threads=
|
||||
(length+min_per_thread-1)/min_per_thread;
|
||||
|
||||
unsigned long const hardware_threads=
|
||||
std::thread::hardware_concurrency();
|
||||
|
||||
unsigned long const num_threads=
|
||||
std::min(hardware_threads!=0?hardware_threads:2,max_threads);
|
||||
|
||||
unsigned long const block_size=length/num_threads;
|
||||
|
||||
typedef typename Iterator::value_type value_type;
|
||||
|
||||
std::vector<std::thread> threads(num_threads-1);
|
||||
std::vector<std::promise<value_type> >
|
||||
end_values(num_threads-1);
|
||||
std::vector<std::future<value_type> >
|
||||
previous_end_values;
|
||||
previous_end_values.reserve(num_threads-1);
|
||||
join_threads joiner(threads);
|
||||
|
||||
Iterator block_start=first;
|
||||
for(unsigned long i=0;i<(num_threads-1);++i)
|
||||
{
|
||||
Iterator block_last=block_start;
|
||||
std::advance(block_last,block_size-1);
|
||||
threads[i]=std::thread(process_chunk(),
|
||||
block_start,block_last,
|
||||
(i!=0)?&previous_end_values[i-1]:0,
|
||||
&end_values[i]);
|
||||
block_start=block_last;
|
||||
++block_start;
|
||||
previous_end_values.push_back(end_values[i].get_future());
|
||||
}
|
||||
Iterator final_element=block_start;
|
||||
std::advance(final_element,std::distance(block_start,last)-1);
|
||||
process_chunk()(block_start,final_element,
|
||||
(num_threads>1)?&previous_end_values.back():0,
|
||||
0);
|
||||
}
|
||||
24
source/listing_8.12.cpp
Normal file
24
source/listing_8.12.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
class barrier
|
||||
{
|
||||
unsigned const count;
|
||||
std::atomic<unsigned> spaces;
|
||||
std::atomic<unsigned> generation;
|
||||
public:
|
||||
explicit barrier(unsigned count_):
|
||||
count(count_),spaces(count),generation(0)
|
||||
{}
|
||||
void wait()
|
||||
{
|
||||
unsigned const my_generation=generation;
|
||||
if(!--spaces)
|
||||
{
|
||||
spaces=count;
|
||||
++generation;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(generation==my_generation)
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
};
|
||||
98
source/listing_8.13.cpp
Normal file
98
source/listing_8.13.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
struct join_threads
|
||||
{
|
||||
join_threads(std::vector<std::thread>&)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
struct barrier
|
||||
{
|
||||
std::atomic<unsigned> count;
|
||||
std::atomic<unsigned> spaces;
|
||||
std::atomic<unsigned> generation;
|
||||
barrier(unsigned count_):
|
||||
count(count_),spaces(count_),generation(0)
|
||||
{}
|
||||
void wait()
|
||||
{
|
||||
unsigned const gen=generation.load();
|
||||
if(!--spaces)
|
||||
{
|
||||
spaces=count.load();
|
||||
++generation;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(generation.load()==gen)
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void done_waiting()
|
||||
{
|
||||
--count;
|
||||
if(!--spaces)
|
||||
{
|
||||
spaces=count.load();
|
||||
++generation;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
void parallel_partial_sum(Iterator first,Iterator last)
|
||||
{
|
||||
typedef typename Iterator::value_type value_type;
|
||||
|
||||
struct process_element
|
||||
{
|
||||
void operator()(Iterator first,Iterator last,
|
||||
std::vector<value_type>& buffer,
|
||||
unsigned i,barrier& b)
|
||||
{
|
||||
value_type& ith_element=*(first+i);
|
||||
bool update_source=false;
|
||||
for(unsigned step=0,stride=1;stride<=i;++step,stride*=2)
|
||||
{
|
||||
value_type const& source=(step%2)?
|
||||
buffer[i]:ith_element;
|
||||
value_type& dest=(step%2)?
|
||||
ith_element:buffer[i];
|
||||
value_type const& addend=(step%2)?
|
||||
buffer[i-stride]:*(first+i-stride);
|
||||
dest=source+addend;
|
||||
update_source=!(step%2);
|
||||
b.wait();
|
||||
}
|
||||
if(update_source)
|
||||
{
|
||||
ith_element=buffer[i];
|
||||
}
|
||||
b.done_waiting();
|
||||
}
|
||||
};
|
||||
|
||||
unsigned long const length=std::distance(first,last);
|
||||
|
||||
if(length<=1)
|
||||
return;
|
||||
|
||||
std::vector<value_type> buffer(length);
|
||||
barrier b(length);
|
||||
std::vector<std::thread> threads(length-1);
|
||||
join_threads joiner(threads);
|
||||
|
||||
Iterator block_start=first;
|
||||
for(unsigned long i=0;i<(length-1);++i)
|
||||
{
|
||||
threads[i]=std::thread(process_element(),first,last,
|
||||
std::ref(buffer),i,std::ref(b));
|
||||
}
|
||||
process_element()(first,last,buffer,length-1,b);
|
||||
}
|
||||
49
source/listing_8.2.cpp
Normal file
49
source/listing_8.2.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
template<typename Iterator,typename T>
|
||||
struct accumulate_block
|
||||
{
|
||||
void operator()(Iterator first,Iterator last,T& result)
|
||||
{
|
||||
result=std::accumulate(first,last,result);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Iterator,typename T>
|
||||
T parallel_accumulate(Iterator first,Iterator last,T init)
|
||||
{
|
||||
unsigned long const length=std::distance(first,last);
|
||||
|
||||
if(!length)
|
||||
return init;
|
||||
|
||||
unsigned long const min_per_thread=25;
|
||||
unsigned long const max_threads=
|
||||
(length+min_per_thread-1)/min_per_thread;
|
||||
|
||||
unsigned long const hardware_threads=
|
||||
std::thread::hardware_concurrency();
|
||||
|
||||
unsigned long const num_threads=
|
||||
std::min(hardware_threads!=0?hardware_threads:2,max_threads);
|
||||
|
||||
unsigned long const block_size=length/num_threads;
|
||||
|
||||
std::vector<T> results(num_threads);
|
||||
std::vector<std::thread> threads(num_threads-1);
|
||||
|
||||
Iterator block_start=first;
|
||||
for(unsigned long i=0;i<(num_threads-1);++i)
|
||||
{
|
||||
Iterator block_end=block_start;
|
||||
std::advance(block_end,block_size);
|
||||
threads[i]=std::thread(
|
||||
accumulate_block<Iterator,T>(),
|
||||
block_start,block_end,std::ref(results[i]));
|
||||
block_start=block_end;
|
||||
}
|
||||
accumulate_block()(block_start,last,results[num_threads-1]);
|
||||
|
||||
std::for_each(threads.begin(),threads.end(),
|
||||
std::mem_fn(&std::thread::join));
|
||||
|
||||
return std::accumulate(results.begin(),results.end(),init);
|
||||
}
|
||||
56
source/listing_8.3.cpp
Normal file
56
source/listing_8.3.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
template<typename Iterator,typename T>
|
||||
struct accumulate_block
|
||||
{
|
||||
T operator()(Iterator first,Iterator last)
|
||||
{
|
||||
return std::accumulate(first,last,T());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Iterator,typename T>
|
||||
T parallel_accumulate(Iterator first,Iterator last,T init)
|
||||
{
|
||||
unsigned long const length=std::distance(first,last);
|
||||
|
||||
if(!length)
|
||||
return init;
|
||||
|
||||
unsigned long const min_per_thread=25;
|
||||
unsigned long const max_threads=
|
||||
(length+min_per_thread-1)/min_per_thread;
|
||||
|
||||
unsigned long const hardware_threads=
|
||||
std::thread::hardware_concurrency();
|
||||
|
||||
unsigned long const num_threads=
|
||||
std::min(hardware_threads!=0?hardware_threads:2,max_threads);
|
||||
|
||||
unsigned long const block_size=length/num_threads;
|
||||
|
||||
std::vector<std::future<T> > futures(num_threads-1);
|
||||
std::vector<std::thread> threads(num_threads-1);
|
||||
|
||||
Iterator block_start=first;
|
||||
for(unsigned long i=0;i<(num_threads-1);++i)
|
||||
{
|
||||
Iterator block_end=block_start;
|
||||
std::advance(block_end,block_size);
|
||||
std::packaged_task<T(Iterator,Iterator)> task(
|
||||
accumulate_block<Iterator,T>());
|
||||
futures[i]=task.get_future();
|
||||
threads[i]=std::thread(std::move(task),block_start,block_end);
|
||||
block_start=block_end;
|
||||
}
|
||||
T last_result=accumulate_block()(block_start,last);
|
||||
|
||||
std::for_each(threads.begin(),threads.end(),
|
||||
std::mem_fn(&std::thread::join));
|
||||
|
||||
T result=init;
|
||||
for(unsigned long i=0;i<(num_threads-1);++i)
|
||||
{
|
||||
result+=futures[i].get();
|
||||
}
|
||||
result += last_result;
|
||||
return result;
|
||||
}
|
||||
44
source/listing_8.4.cpp
Normal file
44
source/listing_8.4.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
template<typename Iterator,typename T>
|
||||
T parallel_accumulate(Iterator first,Iterator last,T init)
|
||||
{
|
||||
unsigned long const length=std::distance(first,last);
|
||||
|
||||
if(!length)
|
||||
return init;
|
||||
|
||||
unsigned long const min_per_thread=25;
|
||||
unsigned long const max_threads=
|
||||
(length+min_per_thread-1)/min_per_thread;
|
||||
|
||||
unsigned long const hardware_threads=
|
||||
std::thread::hardware_concurrency();
|
||||
|
||||
unsigned long const num_threads=
|
||||
std::min(hardware_threads!=0?hardware_threads:2,max_threads);
|
||||
|
||||
unsigned long const block_size=length/num_threads;
|
||||
|
||||
std::vector<std::future<T> > futures(num_threads-1);
|
||||
std::vector<std::thread> threads(num_threads-1);
|
||||
join_threads joiner(threads);
|
||||
|
||||
Iterator block_start=first;
|
||||
for(unsigned long i=0;i<(num_threads-1);++i)
|
||||
{
|
||||
Iterator block_end=block_start;
|
||||
std::advance(block_end,block_size);
|
||||
std::packaged_task<T(Iterator,Iterator)> task(
|
||||
accumulate_block<Iterator,T>());
|
||||
futures[i]=task.get_future();
|
||||
threads[i]=std::thread(std::move(task),block_start,block_end);
|
||||
block_start=block_end;
|
||||
}
|
||||
T last_result=accumulate_block()(block_start,last);
|
||||
T result=init;
|
||||
for(unsigned long i=0;i<(num_threads-1);++i)
|
||||
{
|
||||
result+=futures[i].get();
|
||||
}
|
||||
result += last_result;
|
||||
return result;
|
||||
}
|
||||
20
source/listing_8.5.cpp
Normal file
20
source/listing_8.5.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
template<typename Iterator,typename T>
|
||||
T parallel_accumulate(Iterator first,Iterator last,T init)
|
||||
{
|
||||
unsigned long const length=std::distance(first,last);
|
||||
unsigned long const max_chunk_size=25;
|
||||
if(length<=max_chunk_size)
|
||||
{
|
||||
return std::accumulate(first,last,init);
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator mid_point=first;
|
||||
std::advance(mid_point,length/2);
|
||||
std::future<T> first_half_result=
|
||||
std::async(parallel_accumulate<Iterator,T>,
|
||||
first,mid_point,init);
|
||||
T second_half_result=parallel_accumulate(mid_point,last,T());
|
||||
return first_half_result.get()+second_half_result;
|
||||
}
|
||||
}
|
||||
50
source/listing_8.6.cpp
Normal file
50
source/listing_8.6.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
std::thread task_thread;
|
||||
std::atomic<bool> task_cancelled(false);
|
||||
|
||||
void gui_thread()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
event_data event=get_event();
|
||||
if(event.type==quit)
|
||||
break;
|
||||
process(event);
|
||||
}
|
||||
}
|
||||
|
||||
void task()
|
||||
{
|
||||
while(!task_complete() && !task_cancelled)
|
||||
{
|
||||
do_next_operation();
|
||||
}
|
||||
if(task_cancelled)
|
||||
{
|
||||
perform_cleanup();
|
||||
}
|
||||
else
|
||||
{
|
||||
post_gui_event(task_complete);
|
||||
}
|
||||
}
|
||||
|
||||
void process(event_data const& event)
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case start_task:
|
||||
task_cancelled=false;
|
||||
task_thread=std::thread(task);
|
||||
break;
|
||||
case stop_task:
|
||||
task_cancelled=true;
|
||||
task_thread.join();
|
||||
break;
|
||||
case task_complete:
|
||||
task_thread.join();
|
||||
display_results();
|
||||
break;
|
||||
default:
|
||||
//...
|
||||
}
|
||||
}
|
||||
44
source/listing_8.7.cpp
Normal file
44
source/listing_8.7.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
template<typename Iterator,typename Func>
|
||||
void parallel_for_each(Iterator first,Iterator last,Func f)
|
||||
{
|
||||
unsigned long const length=std::distance(first,last);
|
||||
|
||||
if(!length)
|
||||
return;
|
||||
|
||||
unsigned long const min_per_thread=25;
|
||||
unsigned long const max_threads=
|
||||
(length+min_per_thread-1)/min_per_thread;
|
||||
|
||||
unsigned long const hardware_threads=
|
||||
std::thread::hardware_concurrency();
|
||||
|
||||
unsigned long const num_threads=
|
||||
std::min(hardware_threads!=0?hardware_threads:2,max_threads);
|
||||
|
||||
unsigned long const block_size=length/num_threads;
|
||||
|
||||
std::vector<std::future<void> > futures(num_threads-1);
|
||||
std::vector<std::thread> threads(num_threads-1);
|
||||
join_threads joiner(threads);
|
||||
|
||||
Iterator block_start=first;
|
||||
for(unsigned long i=0;i<(num_threads-1);++i)
|
||||
{
|
||||
Iterator block_end=block_start;
|
||||
std::advance(block_end,block_size);
|
||||
std::packaged_task<void(void)> task(
|
||||
[=]()
|
||||
{
|
||||
std::for_each(block_start,block_end,f);
|
||||
});
|
||||
futures[i]=task.get_future();
|
||||
threads[i]=std::thread(std::move(task));
|
||||
block_start=block_end;
|
||||
}
|
||||
std::for_each(block_start,last,f);
|
||||
for(unsigned long i=0;i<(num_threads-1);++i)
|
||||
{
|
||||
futures[i].get();
|
||||
}
|
||||
}
|
||||
24
source/listing_8.8.cpp
Normal file
24
source/listing_8.8.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
template<typename Iterator,typename Func>
|
||||
void parallel_for_each(Iterator first,Iterator last,Func f)
|
||||
{
|
||||
unsigned long const length=std::distance(first,last);
|
||||
|
||||
if(!length)
|
||||
return;
|
||||
|
||||
unsigned long const min_per_thread=25;
|
||||
|
||||
if(length<(2*min_per_thread))
|
||||
{
|
||||
std::for_each(first,last,f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator const mid_point=first+length/2;
|
||||
std::future<void> first_half=
|
||||
std::async(¶llel_for_each<Iterator,Func>,
|
||||
first,mid_point,f);
|
||||
parallel_for_each(mid_point,last,f);
|
||||
first_half.get();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user