├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE_1_0.txt ├── README.md ├── doc └── C++ Concurrency in Action by Anthony Williams - Errata.pdf └── src ├── CMakeLists.txt ├── appendixA ├── CMakeLists.txt ├── listing_a.1.cpp ├── listing_a.2.cpp ├── listing_a.3.cpp └── listing_a.4.cpp ├── appendixC ├── CMakeLists.txt ├── listing_c.1.cpp ├── listing_c.10.cpp ├── listing_c.2.cpp ├── listing_c.3.cpp ├── listing_c.4.cpp ├── listing_c.5.cpp ├── listing_c.6.cpp ├── listing_c.7.cpp ├── listing_c.8.cpp └── listing_c.9.cpp ├── ch01 ├── CMakeLists.txt └── listing_1.1.cpp ├── ch02 ├── CMakeLists.txt ├── listing_2.1.cpp ├── listing_2.2.cpp ├── listing_2.3.cpp ├── listing_2.4.cpp ├── listing_2.5.cpp ├── listing_2.6.cpp ├── listing_2.7.cpp └── listing_2.8.cpp ├── ch03 ├── CMakeLists.txt ├── listing_3.1.cpp ├── listing_3.10.cpp ├── listing_3.11.cpp ├── listing_3.12.cpp ├── listing_3.13.cpp ├── listing_3.2.cpp ├── listing_3.3.cpp ├── listing_3.4.cpp ├── listing_3.5.cpp ├── listing_3.6.cpp ├── listing_3.7.cpp ├── listing_3.8.cpp └── listing_3.9.cpp ├── ch04 ├── CMakeLists.txt ├── listing_4.1.cpp ├── listing_4.10.cpp ├── listing_4.11.cpp ├── listing_4.12.cpp ├── listing_4.13.cpp ├── listing_4.14.cpp ├── listing_4.15.cpp ├── listing_4.16.cpp ├── listing_4.2.cpp ├── listing_4.3.cpp ├── listing_4.4.cpp ├── listing_4.5.cpp ├── listing_4.6.cpp ├── listing_4.7.cpp ├── listing_4.8.cpp └── listing_4.9.cpp ├── ch05 ├── CMakeLists.txt ├── listing_5.01.cpp ├── listing_5.02.cpp ├── listing_5.03.cpp ├── listing_5.04.cpp ├── listing_5.05.cpp ├── listing_5.06.cpp ├── listing_5.07.cpp ├── listing_5.08.cpp ├── listing_5.09.cpp ├── listing_5.10.cpp ├── listing_5.11.cpp ├── listing_5.12.cpp └── listing_5.13.cpp ├── ch06 ├── CMakeLists.txt ├── listing_6.1.cpp ├── listing_6.10.cpp ├── listing_6.11.cpp ├── listing_6.12.cpp ├── listing_6.13.cpp ├── listing_6.2.cpp ├── listing_6.3.cpp ├── listing_6.4.cpp ├── listing_6.5.cpp ├── listing_6.6.cpp ├── listing_6.7.cpp ├── listing_6.8.cpp └── listing_6.9.cpp ├── ch07 ├── CMakeLists.txt ├── listing_7.1.cpp ├── listing_7.10.cpp ├── listing_7.11.cpp ├── listing_7.12.cpp ├── listing_7.13.cpp ├── listing_7.14.cpp ├── listing_7.15.cpp ├── listing_7.16.cpp ├── listing_7.17.cpp ├── listing_7.18.cpp ├── listing_7.19.cpp ├── listing_7.2.cpp ├── listing_7.20.cpp ├── listing_7.21.cpp ├── listing_7.3.cpp ├── listing_7.4.cpp ├── listing_7.5.cpp ├── listing_7.6.cpp ├── listing_7.7.cpp ├── listing_7.8.cpp └── listing_7.9.cpp ├── ch08 ├── CMakeLists.txt ├── listing_8.1.cpp ├── listing_8.10.cpp ├── listing_8.11.cpp ├── listing_8.12.cpp ├── listing_8.13.cpp ├── listing_8.2.cpp ├── listing_8.3.cpp ├── listing_8.4.cpp ├── listing_8.5.cpp ├── listing_8.6.cpp ├── listing_8.7.cpp ├── listing_8.8.cpp └── listing_8.9.cpp ├── ch09 ├── CMakeLists.txt ├── listing_9.1.cpp ├── listing_9.10.cpp ├── listing_9.11.cpp ├── listing_9.12.cpp ├── listing_9.13.cpp ├── listing_9.2.cpp ├── listing_9.3.cpp ├── listing_9.4.cpp ├── listing_9.5.cpp ├── listing_9.6.cpp ├── listing_9.7.cpp ├── listing_9.8.cpp └── listing_9.9.cpp └── ch10 ├── CMakeLists.txt └── listing_10.1.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | # Build directory 32 | build/* 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Ubuntu 14.04 Trusty support 2 | sudo: required 3 | dist: trusty 4 | 5 | language: cpp 6 | 7 | matrix: 8 | include: 9 | - compiler: gcc 10 | addons: 11 | apt: 12 | sources: 13 | - ubuntu-toolchain-r-test 14 | packages: 15 | - g++-6 16 | env: COMPILER=g++-6 17 | - compiler: clang 18 | addons: 19 | apt: 20 | sources: 21 | - ubuntu-toolchain-r-test 22 | - llvm-toolchain-trusty-4.0 23 | packages: 24 | - clang++-4.0 25 | env: COMPILER=clang++-4.0 26 | before_install: 27 | - sudo apt-get update -qq 28 | - wget --no-verbose --output-document=boost_1_56_0.tar.bz2 http://sourceforge.net/projects/boost/files/boost/1.56.0/boost_1_56_0.tar.bz2/download 29 | - export BOOST_ROOT="$TRAVIS_BUILD_DIR/../boost" 30 | - export DEBIAN_FRONTEND=noninteractive 31 | - mkdir -p $BOOST_ROOT 32 | - tar jxf boost_1_56_0.tar.bz2 --strip-components=1 -C $BOOST_ROOT 33 | 34 | install: 35 | - (cd $BOOST_ROOT; ./bootstrap.sh --with-libraries=atomic,chrono,context,coroutine,iostreams,system,test,thread) 36 | - (cd $BOOST_ROOT; ./b2 threading=multi --prefix=$HOME -d0 install) 37 | 38 | script: 39 | - mkdir build 40 | - cd build 41 | - cmake --version 42 | - cmake -DCMAKE_CXX_COMPILER=$COMPILER -DBOOST_ROOT=$HOME .. && make VERBOSE=1 43 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8) 2 | project(CCiA CXX) 3 | 4 | # check compile flag 5 | # include(CheckCXXCompilerFlag) 6 | # CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14) 7 | # CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) 8 | # if(COMPILER_SUPPORTS_CXX14) 9 | # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") 10 | # elseif(COMPILER_SUPPORTS_CXX1Y) 11 | # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 12 | # else() 13 | # message(FATAL_ERROR "${CMAKE_CXX_COMPILER} has no C++14 support") 14 | # endif() 15 | 16 | set(CMAKE_CXX_FLAGS "-std=c++11") 17 | 18 | set(CXX_FLAGS 19 | -g 20 | # -DVALGRIND 21 | # -DCHECK_PTHREAD_RETURN_VALUE 22 | # -D_FILE_OFFSET_BITS=64 23 | -Wall 24 | # -Wextra 25 | # -Werror 26 | # -Wconversion 27 | # -Wno-unused-parameter 28 | # -Wold-style-cast 29 | # -Woverloaded-virtual 30 | # -Wpointer-arith 31 | # -Wshadow 32 | # -Wwrite-strings 33 | # -march=native 34 | ) 35 | 36 | # list(APPEND CMAKE_CXX_FLAGS ${CXX_FLAGS}) 37 | # string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 38 | 39 | # set(CMAKE_CXX_COMPILER "clang++") 40 | set(CMAKE_CXX_FLAGS_DEBUG "-O0") 41 | set(CMAKE_CXX_FLAGS_RELEASE "-O2 -finline-limit=1000 -DNDEBUG") 42 | # set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) 43 | 44 | find_package(Boost COMPONENTS system thread REQUIRED) 45 | 46 | include_directories(${Boost_INCLUDE_DIRS}) 47 | include_directories("${PROJECT_SOURCE_DIR}/src/") 48 | 49 | add_subdirectory(src/) 50 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://cloud.githubusercontent.com/assets/6889915/19406598/848ebbc8-92bb-11e6-9769-c3e6fcf89731.jpg) 2 | 3 | C++ Concurrency in Action - Practical Multithreading 4 | ==================================================== 5 | 6 | [![Build Status](https://travis-ci.org/iZhangHui/CCiA.svg?branch=master)](https://travis-ci.org/iZhangHui/CCiA) ![Language](https://img.shields.io/badge/language-C%2B%2B11-orange.svg) 7 | 8 | ## Source code 9 | 10 | - The source code of the Book [C++ Concurrency in Action](http://www.manning.com/williams/) 11 | by Anthony Williams imported from http://www.manning.com/williams/CCiA_SourceCode.zip. 12 | 13 | ## eBook - Chinese version 14 | 15 | - gitbook :http://chenxiaowei.gitbooks.io/cpp_concurrency_in_action 16 | 17 | ## Contents 18 | 19 | - [Chapter 1. Hello, world of concurrency in C++!]() 20 | - [Chapter 2. Managing threads]() 21 | - [Chapter 3. Sharing data between threads]() 22 | - [Chapter 4. Synchronizing concurrent operations]() 23 | - [Chapter 5. The C++ memory model and operations on atomic types]() 24 | - [Chapter 6. Designing lock-based concurrent data structures]() 25 | - [Chapter 7. Designing lock-free concurrent data structures]() 26 | - [Chapter 8. Designing concurrent code]() 27 | - [Chapter 9. Advanced thread management]() 28 | - [Chapter 10. Testing and debugging multithreaded applications]() 29 | - [appendix A. Brief reference for some C++11 language features]() 30 | - [appendix B. Brief comparison of concurrency libraries]() 31 | - [appendix C. A message-passing framework and complete ATM example]() 32 | - [appendix D. C++ Thread Library reference]() 33 | -------------------------------------------------------------------------------- /doc/C++ Concurrency in Action by Anthony Williams - Errata.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iZhangHui/CCiA/e302a64abb3d10bb9eae15fe9d62e923831f03ff/doc/C++ Concurrency in Action by Anthony Williams - Errata.pdf -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(ch01) 2 | add_subdirectory(ch02) 3 | add_subdirectory(ch03) 4 | add_subdirectory(ch04) 5 | add_subdirectory(ch05) 6 | add_subdirectory(ch06) 7 | add_subdirectory(ch07) 8 | add_subdirectory(ch08) 9 | add_subdirectory(ch09) 10 | add_subdirectory(ch10) 11 | # add_subdirectory(appendixA) 12 | # add_subdirectory(appendixB) 13 | # add_subdirectory(appendixC) 14 | -------------------------------------------------------------------------------- /src/appendixA/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(listing_1.1 listing_1.1.cpp) 2 | target_link_libraries(listing_1.1 pthread) -------------------------------------------------------------------------------- /src/appendixA/listing_a.1.cpp: -------------------------------------------------------------------------------- 1 | class X 2 | { 3 | private: 4 | int* data; 5 | public: 6 | X(): 7 | data(new int[1000000]) 8 | {} 9 | ~X() 10 | { 11 | delete [] data; 12 | } 13 | X(const X& other): 14 | data(new int[1000000]) 15 | { 16 | std::copy(other.data,other.data+1000000,data); 17 | } 18 | X(X&& other): 19 | data(other.data) 20 | { 21 | other.data=nullptr; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/appendixA/listing_a.2.cpp: -------------------------------------------------------------------------------- 1 | class move_only 2 | { 3 | std::unique_ptr data; 4 | public: 5 | move_only(const move_only&) = delete; 6 | move_only(move_only&& other): 7 | data(std::move(other.data)) 8 | {} 9 | move_only& operator=(const move_only&) = delete; 10 | move_only& operator=(move_only&& other) 11 | { 12 | data=std::move(other.data); 13 | return *this; 14 | } 15 | }; 16 | move_only m1; 17 | move_only m2(m1); 18 | move_only m3(std::move(m1)); 19 | -------------------------------------------------------------------------------- /src/appendixA/listing_a.3.cpp: -------------------------------------------------------------------------------- 1 | class CX 2 | { 3 | private: 4 | int a; 5 | int b; 6 | public: 7 | CX() = default; 8 | CX(int a_, int b_): 9 | a(a_),b(b_) 10 | {} 11 | int get_a() const 12 | { 13 | return a; 14 | } 15 | int get_b() const 16 | { 17 | return b; 18 | } 19 | int foo() const 20 | { 21 | return a+b; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/appendixA/listing_a.4.cpp: -------------------------------------------------------------------------------- 1 | std::condition_variable cond; 2 | bool data_ready; 3 | std::mutex m; 4 | void wait_for_data() 5 | { 6 | std::unique_lock lk(m); 7 | cond.wait(lk,[]{return data_ready;}); 8 | } 9 | -------------------------------------------------------------------------------- /src/appendixC/CMakeLists.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iZhangHui/CCiA/e302a64abb3d10bb9eae15fe9d62e923831f03ff/src/appendixC/CMakeLists.txt -------------------------------------------------------------------------------- /src/appendixC/listing_c.1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | 8 | 9 | namespace messaging 10 | { 11 | struct message_base 12 | { 13 | virtual ~message_base() 14 | {} 15 | }; 16 | 17 | template 18 | struct wrapped_message: 19 | message_base 20 | { 21 | Msg contents; 22 | explicit wrapped_message(Msg const& contents_): 23 | contents(contents_) 24 | {} 25 | }; 26 | 27 | class queue 28 | { 29 | std::mutex m; 30 | std::condition_variable c; 31 | message_base 32 | std::queue > q; 33 | public: 34 | template 35 | void push(T const& msg) 36 | { 37 | std::lock_guard lk(m); 38 | q.push(std::make_shared >(msg)); 39 | c.notify_all(); 40 | } 41 | std::shared_ptr wait_and_pop() 42 | { 43 | std::unique_lock lk(m); 44 | c.wait(lk,[&]{return !q.empty();}); 45 | auto res=q.front(); 46 | q.pop(); 47 | return res; 48 | } 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /src/appendixC/listing_c.10.cpp: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | bank_machine bank; 4 | interface_machine interface_hardware; 5 | atm machine(bank.get_sender(),interface_hardware.get_sender()); 6 | std::thread bank_thread(&bank_machine::run,&bank); 7 | std::thread if_thread(&interface_machine::run,&interface_hardware); 8 | std::thread atm_thread(&atm::run,&machine); 9 | messaging::sender atmqueue(machine.get_sender()); 10 | bool quit_pressed=false; 11 | while(!quit_pressed) 12 | { 13 | char c=getchar(); 14 | switch(c) 15 | { 16 | case '0': 17 | case '1': 18 | case '2': 19 | case '3': 20 | case '4': 21 | case '5': 22 | case '6': 23 | case '7': 24 | case '8': 25 | case '9': 26 | atmqueue.send(digit_pressed(c)); 27 | break; 28 | case 'b': 29 | atmqueue.send(balance_pressed()); 30 | break; 31 | case 'w': 32 | atmqueue.send(withdraw_pressed(50)); 33 | break; 34 | case 'c': 35 | atmqueue.send(cancel_pressed()); 36 | break; 37 | case 'q': 38 | quit_pressed=true; 39 | break; 40 | case 'i': 41 | atmqueue.send(card_inserted("acc1234")); 42 | break; 43 | } 44 | } 45 | bank.done(); 46 | machine.done(); 47 | interface_hardware.done(); 48 | atm_thread.join(); 49 | bank_thread.join(); 50 | if_thread.join(); 51 | } 52 | -------------------------------------------------------------------------------- /src/appendixC/listing_c.2.cpp: -------------------------------------------------------------------------------- 1 | namespace messaging 2 | { 3 | class sender 4 | { 5 | queue*q; 6 | public: 7 | sender(): 8 | q(nullptr) 9 | {} 10 | explicit sender(queue*q_): 11 | q(q_) 12 | {} 13 | template 14 | void send(Message const& msg) 15 | { 16 | if(q) 17 | { 18 | q->push(msg); 19 | } 20 | } 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/appendixC/listing_c.3.cpp: -------------------------------------------------------------------------------- 1 | namespace messaging 2 | { 3 | class receiver 4 | { 5 | queue q; 6 | public: 7 | operator sender() 8 | { 9 | return sender(&q); 10 | } 11 | dispatcher wait() 12 | { 13 | return dispatcher(&q); 14 | } 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/appendixC/listing_c.4.cpp: -------------------------------------------------------------------------------- 1 | namespace messaging 2 | { 3 | class close_queue 4 | {}; 5 | 6 | class dispatcher 7 | { 8 | queue* q; 9 | bool chained; 10 | 11 | dispatcher(dispatcher const&)=delete; 12 | dispatcher& operator=(dispatcher const&)=delete; 13 | 14 | template< 15 | typename Dispatcher, 16 | typename Msg, 17 | typename Func> 18 | friend class TemplateDispatcher; 19 | 20 | void wait_and_dispatch() 21 | { 22 | for(;;) 23 | { 24 | auto msg=q->wait_and_pop(); 25 | dispatch(msg); 26 | } 27 | } 28 | 29 | bool dispatch( 30 | std::shared_ptr const& msg) 31 | { 32 | if(dynamic_cast*>(msg.get())) 33 | { 34 | throw close_queue(); 35 | } 36 | return false; 37 | } 38 | public: 39 | dispatcher(dispatcher&& other): 40 | q(other.q),chained(other.chained) 41 | { 42 | other.chained=true; 43 | } 44 | 45 | explicit dispatcher(queue* q_): 46 | q(q_),chained(false) 47 | {} 48 | 49 | template 50 | TemplateDispatcher 51 | handle(Func&& f) 52 | { 53 | return TemplateDispatcher( 54 | q,this,std::forward(f)); 55 | } 56 | 57 | ~dispatcher() noexcept(false) 58 | { 59 | if(!chained) 60 | { 61 | wait_and_dispatch(); 62 | } 63 | } 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /src/appendixC/listing_c.5.cpp: -------------------------------------------------------------------------------- 1 | namespace messaging 2 | { 3 | template 4 | class TemplateDispatcher 5 | { 6 | queue* q; 7 | PreviousDispatcher* prev; 8 | Func f; 9 | bool chained; 10 | 11 | TemplateDispatcher(TemplateDispatcher const&)=delete; 12 | TemplateDispatcher& operator=(TemplateDispatcher const&)=delete; 13 | 14 | template 15 | friend class TemplateDispatcher; 16 | 17 | void wait_and_dispatch() 18 | { 19 | for(;;) 20 | { 21 | auto msg=q->wait_and_pop(); 22 | if(dispatch(msg)) 23 | break; 24 | } 25 | } 26 | 27 | bool dispatch(std::shared_ptr const& msg) 28 | { 29 | if(wrapped_message* wrapper= 30 | dynamic_cast*>(msg.get())) 31 | { 32 | f(wrapper->contents); 33 | return true; 34 | } 35 | else 36 | { 37 | return prev->dispatch(msg); 38 | } 39 | } 40 | public: 41 | TemplateDispatcher(TemplateDispatcher&& other): 42 | q(other.q),prev(other.prev),f(std::move(other.f)), 43 | chained(other.chained) 44 | { 45 | other.chained=true; 46 | } 47 | 48 | TemplateDispatcher(queue* q_,PreviousDispatcher* prev_,Func&& f_): 49 | q(q_),prev(prev_),f(std::forward(f_)),chained(false) 50 | { 51 | prev_->chained=true; 52 | } 53 | 54 | template 55 | TemplateDispatcher 56 | handle(OtherFunc&& of) 57 | { 58 | return TemplateDispatcher< 59 | TemplateDispatcher,OtherMsg,OtherFunc>( 60 | q,this,std::forward(of)); 61 | } 62 | 63 | ~TemplateDispatcher() noexcept(false) 64 | { 65 | if(!chained) 66 | { 67 | wait_and_dispatch(); 68 | } 69 | } 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /src/appendixC/listing_c.6.cpp: -------------------------------------------------------------------------------- 1 | struct withdraw 2 | { 3 | std::string account; 4 | unsigned amount; 5 | mutable messaging::sender atm_queue; 6 | withdraw(std::string const& account_, 7 | unsigned amount_, 8 | messaging::sender atm_queue_): 9 | account(account_),amount(amount_), 10 | atm_queue(atm_queue_) 11 | {} 12 | }; 13 | struct withdraw_ok 14 | {}; 15 | struct withdraw_denied 16 | {}; 17 | struct cancel_withdrawal 18 | { 19 | std::string account; 20 | unsigned amount; 21 | cancel_withdrawal(std::string const& account_, 22 | unsigned amount_): 23 | account(account_),amount(amount_) 24 | {} 25 | }; 26 | struct withdrawal_processed 27 | { 28 | std::string account; 29 | unsigned amount; 30 | withdrawal_processed(std::string const& account_, 31 | unsigned amount_): 32 | account(account_),amount(amount_) 33 | {} 34 | }; 35 | struct card_inserted 36 | { 37 | std::string account; 38 | explicit card_inserted(std::string const& account_): 39 | account(account_) 40 | {} 41 | }; 42 | struct digit_pressed 43 | { 44 | char digit; 45 | explicit digit_pressed(char digit_): 46 | digit(digit_) 47 | {} 48 | }; 49 | struct clear_last_pressed 50 | {}; 51 | struct eject_card 52 | {}; 53 | struct withdraw_pressed 54 | { 55 | unsigned amount; 56 | explicit withdraw_pressed(unsigned amount_): 57 | amount(amount_) 58 | {} 59 | }; 60 | struct cancel_pressed 61 | {}; 62 | struct issue_money 63 | { 64 | unsigned amount; 65 | issue_money(unsigned amount_): 66 | amount(amount_) 67 | {} 68 | }; 69 | struct verify_pin 70 | { 71 | std::string account; 72 | std::string pin; 73 | mutable messaging::sender atm_queue; 74 | verify_pin(std::string const& account_,std::string const& pin_, 75 | messaging::sender atm_queue_): 76 | account(account_),pin(pin_),atm_queue(atm_queue_) 77 | {} 78 | }; 79 | struct pin_verified 80 | {}; 81 | struct pin_incorrect 82 | {}; 83 | struct display_enter_pin 84 | {}; 85 | struct display_enter_card 86 | {}; 87 | struct display_insufficient_funds 88 | {}; 89 | struct display_withdrawal_cancelled 90 | {}; 91 | struct display_pin_incorrect_message 92 | {}; 93 | struct display_withdrawal_options 94 | {}; 95 | struct get_balance 96 | { 97 | std::string account; 98 | mutable messaging::sender atm_queue; 99 | get_balance(std::string const& account_,messaging::sender atm_queue_): 100 | account(account_),atm_queue(atm_queue_) 101 | {} 102 | }; 103 | struct balance 104 | { 105 | unsigned amount; 106 | explicit balance(unsigned amount_): 107 | amount(amount_) 108 | {} 109 | }; 110 | struct display_balance 111 | { 112 | unsigned amount; 113 | explicit display_balance(unsigned amount_): 114 | amount(amount_) 115 | {} 116 | }; 117 | struct balance_pressed 118 | {}; 119 | -------------------------------------------------------------------------------- /src/appendixC/listing_c.7.cpp: -------------------------------------------------------------------------------- 1 | class atm 2 | { 3 | messaging::receiver incoming; 4 | messaging::sender bank; 5 | messaging::sender interface_hardware; 6 | void (atm::*state)(); 7 | std::string account; 8 | unsigned withdrawal_amount; 9 | std::string pin; 10 | void process_withdrawal() 11 | { 12 | incoming.wait() 13 | .handle( 14 | [&](withdraw_ok const& msg) 15 | { 16 | interface_hardware.send( 17 | issue_money(withdrawal_amount)); 18 | bank.send( 19 | withdrawal_processed(account,withdrawal_amount)); 20 | state=&atm::done_processing; 21 | } 22 | ) 23 | .handle( 24 | [&](withdraw_denied const& msg) 25 | { 26 | interface_hardware.send(display_insufficient_funds()); 27 | state=&atm::done_processing; 28 | } 29 | ) 30 | .handle( 31 | [&](cancel_pressed const& msg) 32 | { 33 | bank.send( 34 | cancel_withdrawal(account,withdrawal_amount)); 35 | interface_hardware.send( 36 | display_withdrawal_cancelled()); 37 | state=&atm::done_processing; 38 | } 39 | ); 40 | } 41 | void process_balance() 42 | { 43 | incoming.wait() 44 | .handle( 45 | [&](balance const& msg) 46 | { 47 | interface_hardware.send(display_balance(msg.amount)); 48 | state=&atm::wait_for_action; 49 | } 50 | ) 51 | .handle( 52 | [&](cancel_pressed const& msg) 53 | { 54 | state=&atm::done_processing; 55 | } 56 | ); 57 | } 58 | void wait_for_action() 59 | { 60 | interface_hardware.send(display_withdrawal_options()); 61 | incoming.wait() 62 | .handle( 63 | [&](withdraw_pressed const& msg) 64 | { 65 | withdrawal_amount=msg.amount; 66 | bank.send(withdraw(account,msg.amount,incoming)); 67 | state=&atm::process_withdrawal; 68 | } 69 | ) 70 | .handle( 71 | [&](balance_pressed const& msg) 72 | { 73 | bank.send(get_balance(account,incoming)); 74 | state=&atm::process_balance; 75 | } 76 | ) 77 | .handle( 78 | [&](cancel_pressed const& msg) 79 | { 80 | state=&atm::done_processing; 81 | } 82 | ); 83 | } 84 | void verifying_pin() 85 | { 86 | incoming.wait() 87 | .handle( 88 | [&](pin_verified const& msg) 89 | { 90 | state=&atm::wait_for_action; 91 | } 92 | ) 93 | .handle( 94 | [&](pin_incorrect const& msg) 95 | { 96 | interface_hardware.send( 97 | display_pin_incorrect_message()); 98 | state=&atm::done_processing; 99 | } 100 | ) 101 | .handle( 102 | [&](cancel_pressed const& msg) 103 | { 104 | state=&atm::done_processing; 105 | } 106 | ); 107 | } 108 | void getting_pin() 109 | { 110 | incoming.wait() 111 | .handle( 112 | [&](digit_pressed const& msg) 113 | { 114 | unsigned const pin_length=4; 115 | pin+=msg.digit; 116 | if(pin.length()==pin_length) 117 | { 118 | bank.send(verify_pin(account,pin,incoming)); 119 | state=&atm::verifying_pin; 120 | } 121 | } 122 | ) 123 | .handle( 124 | [&](clear_last_pressed const& msg) 125 | { 126 | if(!pin.empty()) 127 | { 128 | pin.pop_back(); 129 | } 130 | } 131 | ) 132 | .handle( 133 | [&](cancel_pressed const& msg) 134 | { 135 | state=&atm::done_processing; 136 | } 137 | ); 138 | } 139 | void waiting_for_card() 140 | { 141 | interface_hardware.send(display_enter_card()); 142 | incoming.wait() 143 | .handle( 144 | [&](card_inserted const& msg) 145 | { 146 | account=msg.account; 147 | pin=""; 148 | interface_hardware.send(display_enter_pin()); 149 | state=&atm::getting_pin; 150 | } 151 | ); 152 | } 153 | void done_processing() 154 | { 155 | interface_hardware.send(eject_card()); 156 | state=&atm::waiting_for_card; 157 | } 158 | atm(atm const&)=delete; 159 | atm& operator=(atm const&)=delete; 160 | public: 161 | atm(messaging::sender bank_, 162 | messaging::sender interface_hardware_): 163 | bank(bank_),interface_hardware(interface_hardware_) 164 | {} 165 | void done() 166 | { 167 | get_sender().send(messaging::close_queue()); 168 | } 169 | void run() 170 | { 171 | state=&atm::waiting_for_card; 172 | try 173 | { 174 | for(;;) 175 | { 176 | (this->*state)(); 177 | } 178 | } 179 | catch(messaging::close_queue const&) 180 | { 181 | } 182 | } 183 | messaging::sender get_sender() 184 | { 185 | return incoming; 186 | } 187 | }; 188 | -------------------------------------------------------------------------------- /src/appendixC/listing_c.8.cpp: -------------------------------------------------------------------------------- 1 | class bank_machine 2 | { 3 | messaging::receiver incoming; 4 | unsigned balance; 5 | public: 6 | bank_machine(): 7 | balance(199) 8 | {} 9 | void done() 10 | { 11 | get_sender().send(messaging::close_queue()); 12 | } 13 | void run() 14 | { 15 | try 16 | { 17 | for(;;) 18 | { 19 | incoming.wait() 20 | .handle( 21 | [&](verify_pin const& msg) 22 | { 23 | if(msg.pin=="1937") 24 | { 25 | msg.atm_queue.send(pin_verified()); 26 | } 27 | else 28 | { 29 | msg.atm_queue.send(pin_incorrect()); 30 | } 31 | } 32 | ) 33 | .handle( 34 | [&](withdraw const& msg) 35 | { 36 | if(balance>=msg.amount) 37 | { 38 | msg.atm_queue.send(withdraw_ok()); 39 | balance-=msg.amount; 40 | } 41 | else 42 | { 43 | msg.atm_queue.send(withdraw_denied()); 44 | } 45 | } 46 | ) 47 | .handle( 48 | [&](get_balance const& msg) 49 | { 50 | msg.atm_queue.send(::balance(balance)); 51 | } 52 | ) 53 | .handle( 54 | [&](withdrawal_processed const& msg) 55 | { 56 | } 57 | ) 58 | .handle( 59 | [&](cancel_withdrawal const& msg) 60 | { 61 | } 62 | ); 63 | } 64 | } 65 | catch(messaging::close_queue const&) 66 | { 67 | } 68 | } 69 | messaging::sender get_sender() 70 | { 71 | return incoming; 72 | } 73 | }; 74 | -------------------------------------------------------------------------------- /src/appendixC/listing_c.9.cpp: -------------------------------------------------------------------------------- 1 | class interface_machine 2 | { 3 | messaging::receiver incoming; 4 | public: 5 | void done() 6 | { 7 | get_sender().send(messaging::close_queue()); 8 | } 9 | void run() 10 | { 11 | try 12 | { 13 | for(;;) 14 | { 15 | incoming.wait() 16 | .handle( 17 | [&](issue_money const& msg) 18 | { 19 | { 20 | std::lock_guard lk(iom); 21 | std::cout<<"Issuing " 22 | <( 27 | [&](display_insufficient_funds const& msg) 28 | { 29 | { 30 | std::lock_guard lk(iom); 31 | std::cout<<"Insufficient funds"<( 36 | [&](display_enter_pin const& msg) 37 | { 38 | { 39 | std::lock_guard lk(iom); 40 | std::cout 41 | <<"Please enter your PIN (0-9)" 42 | <( 47 | [&](display_enter_card const& msg) 48 | { 49 | { 50 | std::lock_guard lk(iom); 51 | std::cout<<"Please enter your card (I)" 52 | <( 57 | [&](display_balance const& msg) 58 | { 59 | { 60 | std::lock_guard lk(iom); 61 | std::cout 62 | <<"The balance of your account is " 63 | <( 68 | [&](display_withdrawal_options const& msg) 69 | { 70 | { 71 | std::lock_guard lk(iom); 72 | std::cout<<"Withdraw 50? (w)"<( 80 | [&](display_withdrawal_cancelled const& msg) 81 | { 82 | { 83 | std::lock_guard lk(iom); 84 | std::cout<<"Withdrawal cancelled" 85 | <( 90 | [&](display_pin_incorrect_message const& msg) 91 | { 92 | { 93 | std::lock_guard lk(iom); 94 | std::cout<<"PIN incorrect"<( 99 | [&](eject_card const& msg) 100 | { 101 | { 102 | std::lock_guard lk(iom); 103 | std::cout<<"Ejecting card"< 2 | #include 3 | 4 | void hello() 5 | { 6 | std::cout<<"Hello Concurrent World\n"; 7 | } 8 | 9 | int main() 10 | { 11 | std::thread t(hello); 12 | t.join(); 13 | } 14 | -------------------------------------------------------------------------------- /src/ch02/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(listing_2.1 listing_2.1.cpp) 2 | target_link_libraries(listing_2.1 pthread) 3 | 4 | add_executable(listing_2.2 listing_2.2.cpp) 5 | target_link_libraries(listing_2.2 pthread) 6 | 7 | add_executable(listing_2.3 listing_2.3.cpp) 8 | target_link_libraries(listing_2.3 pthread) 9 | 10 | add_executable(listing_2.4 listing_2.4.cpp) 11 | target_link_libraries(listing_2.4 pthread) 12 | 13 | add_executable(listing_2.5 listing_2.5.cpp) 14 | target_link_libraries(listing_2.5 pthread) 15 | 16 | add_executable(listing_2.6 listing_2.6.cpp) 17 | target_link_libraries(listing_2.6 pthread) 18 | 19 | add_executable(listing_2.7 listing_2.7.cpp) 20 | target_link_libraries(listing_2.7 pthread) 21 | 22 | add_executable(listing_2.8 listing_2.8.cpp) 23 | target_link_libraries(listing_2.8 pthread) -------------------------------------------------------------------------------- /src/ch02/listing_2.1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void do_something(int& i) 4 | { 5 | ++i; 6 | } 7 | 8 | struct func 9 | { 10 | int& i; 11 | 12 | func(int& i_):i(i_){} 13 | 14 | void operator()() 15 | { 16 | for(unsigned j=0;j<1000000;++j) 17 | { 18 | do_something(i); 19 | } 20 | } 21 | }; 22 | 23 | 24 | void oops() 25 | { 26 | int some_local_state=0; 27 | func my_func(some_local_state); 28 | std::thread my_thread(my_func); 29 | my_thread.detach(); 30 | } 31 | 32 | int main() 33 | { 34 | oops(); 35 | } 36 | -------------------------------------------------------------------------------- /src/ch02/listing_2.2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void do_something(int& i) 4 | { 5 | ++i; 6 | } 7 | 8 | struct func 9 | { 10 | int& i; 11 | 12 | func(int& i_):i(i_){} 13 | 14 | void operator()() 15 | { 16 | for(unsigned j=0;j<1000000;++j) 17 | { 18 | do_something(i); 19 | } 20 | } 21 | }; 22 | 23 | void do_something_in_current_thread() 24 | {} 25 | 26 | void f() 27 | { 28 | int some_local_state=0; 29 | func my_func(some_local_state); 30 | std::thread t(my_func); 31 | try 32 | { 33 | do_something_in_current_thread(); 34 | } 35 | catch(...) 36 | { 37 | t.join(); 38 | throw; 39 | } 40 | t.join(); 41 | } 42 | 43 | int main() 44 | { 45 | f(); 46 | } 47 | -------------------------------------------------------------------------------- /src/ch02/listing_2.3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class thread_guard 4 | { 5 | std::thread& t; 6 | public: 7 | explicit thread_guard(std::thread& t_): 8 | t(t_) 9 | {} 10 | ~thread_guard() 11 | { 12 | if(t.joinable()) 13 | { 14 | t.join(); 15 | } 16 | } 17 | thread_guard(thread_guard const&)=delete; 18 | thread_guard& operator=(thread_guard const&)=delete; 19 | }; 20 | 21 | void do_something(int& i) 22 | { 23 | ++i; 24 | } 25 | 26 | struct func 27 | { 28 | int& i; 29 | 30 | func(int& i_):i(i_){} 31 | 32 | void operator()() 33 | { 34 | for(unsigned j=0;j<1000000;++j) 35 | { 36 | do_something(i); 37 | } 38 | } 39 | }; 40 | 41 | void do_something_in_current_thread() 42 | {} 43 | 44 | 45 | void f() 46 | { 47 | int some_local_state; 48 | func my_func(some_local_state); 49 | std::thread t(my_func); 50 | thread_guard g(t); 51 | 52 | do_something_in_current_thread(); 53 | } 54 | 55 | int main() 56 | { 57 | f(); 58 | } 59 | -------------------------------------------------------------------------------- /src/ch02/listing_2.4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void open_document_and_display_gui(std::string const& filename) 5 | {} 6 | 7 | bool done_editing() 8 | { 9 | return true; 10 | } 11 | 12 | enum command_type{ 13 | open_new_document 14 | }; 15 | 16 | 17 | struct user_command 18 | { 19 | command_type type; 20 | 21 | user_command(): 22 | type(open_new_document) 23 | {} 24 | }; 25 | 26 | user_command get_user_input() 27 | { 28 | return user_command(); 29 | } 30 | 31 | std::string get_filename_from_user() 32 | { 33 | return "foo.doc"; 34 | } 35 | 36 | void process_user_input(user_command const& cmd) 37 | {} 38 | 39 | void edit_document(std::string const& filename) 40 | { 41 | open_document_and_display_gui(filename); 42 | while(!done_editing()) 43 | { 44 | user_command cmd=get_user_input(); 45 | if(cmd.type==open_new_document) 46 | { 47 | std::string const new_name=get_filename_from_user(); 48 | std::thread t(edit_document,new_name); 49 | t.detach(); 50 | } 51 | else 52 | { 53 | process_user_input(cmd); 54 | } 55 | } 56 | } 57 | 58 | int main() 59 | { 60 | edit_document("bar.doc"); 61 | } 62 | -------------------------------------------------------------------------------- /src/ch02/listing_2.5.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void some_function() 4 | {} 5 | 6 | void some_other_function(int) 7 | {} 8 | 9 | std::thread f() 10 | { 11 | void some_function(); 12 | return std::thread(some_function); 13 | } 14 | std::thread g() 15 | { 16 | void some_other_function(int); 17 | std::thread t(some_other_function,42); 18 | return t; 19 | } 20 | 21 | int main() 22 | { 23 | std::thread t1=f(); 24 | t1.join(); 25 | std::thread t2=g(); 26 | t2.join(); 27 | } 28 | -------------------------------------------------------------------------------- /src/ch02/listing_2.6.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class scoped_thread 5 | { 6 | std::thread t; 7 | public: 8 | explicit scoped_thread(std::thread t_): 9 | t(std::move(t_)) 10 | { 11 | if(!t.joinable()) 12 | throw std::logic_error("No thread"); 13 | } 14 | ~scoped_thread() 15 | { 16 | t.join(); 17 | } 18 | scoped_thread(scoped_thread const&)=delete; 19 | scoped_thread& operator=(scoped_thread const&)=delete; 20 | }; 21 | 22 | void do_something(int& i) 23 | { 24 | ++i; 25 | } 26 | 27 | struct func 28 | { 29 | int& i; 30 | 31 | func(int& i_):i(i_){} 32 | 33 | void operator()() 34 | { 35 | for(unsigned j=0;j<1000000;++j) 36 | { 37 | do_something(i); 38 | } 39 | } 40 | }; 41 | 42 | void do_something_in_current_thread() 43 | {} 44 | 45 | void f() 46 | { 47 | int some_local_state; 48 | scoped_thread t(std::thread(func(some_local_state))); 49 | 50 | do_something_in_current_thread(); 51 | } 52 | 53 | int main() 54 | { 55 | f(); 56 | } 57 | -------------------------------------------------------------------------------- /src/ch02/listing_2.7.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void do_work(unsigned id) 7 | {} 8 | 9 | void f() 10 | { 11 | std::vector threads; 12 | for(unsigned i=0;i<20;++i) 13 | { 14 | threads.push_back(std::thread(do_work,i)); 15 | } 16 | std::for_each(threads.begin(),threads.end(), 17 | std::mem_fn(&std::thread::join)); 18 | } 19 | 20 | int main() 21 | { 22 | f(); 23 | } 24 | -------------------------------------------------------------------------------- /src/ch02/listing_2.8.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | template 9 | struct accumulate_block 10 | { 11 | void operator()(Iterator first,Iterator last,T& result) 12 | { 13 | result=std::accumulate(first,last,result); 14 | } 15 | }; 16 | 17 | template 18 | T parallel_accumulate(Iterator first,Iterator last,T init) 19 | { 20 | unsigned long const length=std::distance(first,last); 21 | 22 | if(!length) 23 | return init; 24 | 25 | unsigned long const min_per_thread=25; 26 | unsigned long const max_threads= 27 | (length+min_per_thread-1)/min_per_thread; 28 | 29 | unsigned long const hardware_threads= 30 | std::thread::hardware_concurrency(); 31 | 32 | unsigned long const num_threads= 33 | std::min(hardware_threads!=0?hardware_threads:2,max_threads); 34 | 35 | unsigned long const block_size=length/num_threads; 36 | 37 | std::vector results(num_threads); 38 | std::vector threads(num_threads-1); 39 | 40 | Iterator block_start=first; 41 | for(unsigned long i=0;i<(num_threads-1);++i) 42 | { 43 | Iterator block_end=block_start; 44 | std::advance(block_end,block_size); 45 | threads[i]=std::thread( 46 | accumulate_block(), 47 | block_start,block_end,std::ref(results[i])); 48 | block_start=block_end; 49 | } 50 | accumulate_block()(block_start,last,results[num_threads-1]); 51 | 52 | std::for_each(threads.begin(),threads.end(), 53 | std::mem_fn(&std::thread::join)); 54 | 55 | return std::accumulate(results.begin(),results.end(),init); 56 | } 57 | 58 | int main() 59 | { 60 | std::vector vi; 61 | for(int i=0;i<10;++i) 62 | { 63 | vi.push_back(10); 64 | } 65 | int sum=parallel_accumulate(vi.begin(),vi.end(),5); 66 | std::cout<<"sum="< 2 | #include 3 | #include 4 | 5 | std::list some_list; 6 | std::mutex some_mutex; 7 | 8 | void add_to_list(int new_value) 9 | { 10 | std::lock_guard guard(some_mutex); 11 | some_list.push_back(new_value); 12 | } 13 | bool list_contains(int value_to_find) 14 | { 15 | std::lock_guard guard(some_mutex); 16 | return std::find(some_list.begin(),some_list.end(),value_to_find) 17 | != some_list.end(); 18 | } 19 | 20 | #include 21 | 22 | int main() 23 | { 24 | add_to_list(42); 25 | std::cout<<"contains(1)="< 16 | class threadsafe_stack 17 | { 18 | private: 19 | std::stack data; 20 | mutable std::mutex m; 21 | public: 22 | threadsafe_stack(){} 23 | threadsafe_stack(const threadsafe_stack& other) 24 | { 25 | std::lock_guard lock(other.m); 26 | data=other.data; 27 | } 28 | threadsafe_stack& operator=(const threadsafe_stack&) = delete; 29 | 30 | void push(T new_value) 31 | { 32 | std::lock_guard lock(m); 33 | data.push(new_value); 34 | } 35 | std::shared_ptr pop() 36 | { 37 | std::lock_guard lock(m); 38 | if(data.empty()) throw empty_stack(); 39 | std::shared_ptr const res(std::make_shared(data.top())); 40 | data.pop(); 41 | return res; 42 | } 43 | void pop(T& value) 44 | { 45 | std::lock_guard lock(m); 46 | if(data.empty()) throw empty_stack(); 47 | value=data.top(); 48 | data.pop(); 49 | } 50 | bool empty() const 51 | { 52 | std::lock_guard lock(m); 53 | return data.empty(); 54 | } 55 | }; 56 | 57 | int main() 58 | { 59 | threadsafe_stack si; 60 | si.push(5); 61 | si.pop(); 62 | if(!si.empty()) 63 | { 64 | int x; 65 | si.pop(x); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/ch03/listing_3.6.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class some_big_object 4 | {}; 5 | 6 | void swap(some_big_object& lhs,some_big_object& rhs) 7 | {} 8 | 9 | class X 10 | { 11 | private: 12 | some_big_object some_detail; 13 | mutable std::mutex m; 14 | public: 15 | X(some_big_object const& sd):some_detail(sd){} 16 | 17 | friend void swap(X& lhs, X& rhs) 18 | { 19 | if(&lhs==&rhs) 20 | return; 21 | std::lock(lhs.m,rhs.m); 22 | std::lock_guard lock_a(lhs.m,std::adopt_lock); 23 | std::lock_guard lock_b(rhs.m,std::adopt_lock); 24 | swap(lhs.some_detail,rhs.some_detail); 25 | } 26 | }; 27 | 28 | int main() 29 | {} 30 | -------------------------------------------------------------------------------- /src/ch03/listing_3.7.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class hierarchical_mutex 4 | { 5 | public: 6 | explicit hierarchical_mutex(unsigned level) 7 | {} 8 | 9 | void lock() 10 | {} 11 | void unlock() 12 | {} 13 | }; 14 | 15 | 16 | hierarchical_mutex high_level_mutex(10000); 17 | hierarchical_mutex low_level_mutex(5000); 18 | 19 | int do_low_level_stuff() 20 | { 21 | return 42; 22 | } 23 | 24 | 25 | int low_level_func() 26 | { 27 | std::lock_guard lk(low_level_mutex); 28 | return do_low_level_stuff(); 29 | } 30 | 31 | void high_level_stuff(int some_param) 32 | {} 33 | 34 | 35 | void high_level_func() 36 | { 37 | std::lock_guard lk(high_level_mutex); 38 | high_level_stuff(low_level_func()); 39 | } 40 | 41 | void thread_a() 42 | { 43 | high_level_func(); 44 | } 45 | 46 | hierarchical_mutex other_mutex(100); 47 | void do_other_stuff() 48 | {} 49 | 50 | 51 | void other_stuff() 52 | { 53 | high_level_func(); 54 | do_other_stuff(); 55 | } 56 | 57 | void thread_b() 58 | { 59 | std::lock_guard lk(other_mutex); 60 | other_stuff(); 61 | } 62 | 63 | int main() 64 | {} 65 | -------------------------------------------------------------------------------- /src/ch03/listing_3.8.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | class hierarchical_mutex 6 | { 7 | std::mutex internal_mutex; 8 | unsigned long const hierarchy_value; 9 | unsigned long previous_hierarchy_value; 10 | static thread_local unsigned long this_thread_hierarchy_value; 11 | 12 | void check_for_hierarchy_violation() 13 | { 14 | if(this_thread_hierarchy_value <= hierarchy_value) 15 | { 16 | throw std::logic_error("mutex hierarchy violated"); 17 | } 18 | } 19 | void update_hierarchy_value() 20 | { 21 | previous_hierarchy_value=this_thread_hierarchy_value; 22 | this_thread_hierarchy_value=hierarchy_value; 23 | } 24 | public: 25 | explicit hierarchical_mutex(unsigned long value): 26 | hierarchy_value(value), 27 | previous_hierarchy_value(0) 28 | {} 29 | void lock() 30 | { 31 | check_for_hierarchy_violation(); 32 | internal_mutex.lock(); 33 | update_hierarchy_value(); 34 | } 35 | void unlock() 36 | { 37 | this_thread_hierarchy_value=previous_hierarchy_value; 38 | internal_mutex.unlock(); 39 | } 40 | bool try_lock() 41 | { 42 | check_for_hierarchy_violation(); 43 | if(!internal_mutex.try_lock()) 44 | return false; 45 | update_hierarchy_value(); 46 | return true; 47 | } 48 | }; 49 | thread_local unsigned long 50 | hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX); 51 | 52 | int main() 53 | { 54 | hierarchical_mutex m1(42); 55 | hierarchical_mutex m2(2000); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/ch03/listing_3.9.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class some_big_object 4 | {}; 5 | 6 | void swap(some_big_object& lhs,some_big_object& rhs) 7 | {} 8 | 9 | class X 10 | { 11 | private: 12 | some_big_object some_detail; 13 | mutable std::mutex m; 14 | public: 15 | X(some_big_object const& sd):some_detail(sd){} 16 | 17 | friend void swap(X& lhs, X& rhs) 18 | { 19 | if(&lhs==&rhs) 20 | return; 21 | std::unique_lock lock_a(lhs.m,std::defer_lock); 22 | std::unique_lock lock_b(rhs.m,std::defer_lock); 23 | std::lock(lock_a,lock_b); 24 | swap(lhs.some_detail,rhs.some_detail); 25 | } 26 | }; 27 | 28 | int main() 29 | {} 30 | -------------------------------------------------------------------------------- /src/ch04/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(listing_4.1 listing_4.1.cpp) 2 | target_link_libraries(listing_4.1 pthread) 3 | 4 | # add_executable(listing_4.2 listing_4.2.cpp) 5 | # target_link_libraries(listing_4.2 pthread) 6 | 7 | add_executable(listing_4.3 listing_4.3.cpp) 8 | target_link_libraries(listing_4.3 pthread) 9 | 10 | # add_executable(listing_4.4 listing_4.4.cpp) 11 | # target_link_libraries(listing_4.4 pthread) 12 | 13 | add_executable(listing_4.5 listing_4.5.cpp) 14 | target_link_libraries(listing_4.5 pthread) 15 | 16 | add_executable(listing_4.6 listing_4.6.cpp) 17 | target_link_libraries(listing_4.6 pthread) 18 | 19 | # add_executable(listing_4.7 listing_4.7.cpp) 20 | # target_link_libraries(listing_4.7 pthread) 21 | 22 | # add_executable(listing_4.8 listing_4.8.cpp) 23 | # target_link_libraries(listing_4.8 pthread) 24 | 25 | # add_executable(listing_4.9 listing_4.9.cpp) 26 | # target_link_libraries(listing_4.9 pthread) 27 | 28 | # add_executable(listing_4.10 listing_4.10.cpp) 29 | # target_link_libraries(listing_4.10 pthread) 30 | 31 | # add_executable(listing_4.11 listing_4.11.cpp) 32 | # target_link_libraries(listing_4.11 pthread) 33 | 34 | # add_executable(listing_4.12 listing_4.12.cpp) 35 | # target_link_libraries(listing_4.12 pthread) 36 | 37 | # add_executable(listing_4.13 listing_4.13.cpp) 38 | # target_link_libraries(listing_4.13 pthread) 39 | 40 | # add_executable(listing_4.14 listing_4.14.cpp) 41 | # target_link_libraries(listing_4.14 pthread) 42 | 43 | # add_executable(listing_4.15 listing_4.15.cpp) 44 | # target_link_libraries(listing_4.15 pthread) 45 | 46 | # add_executable(listing_4.16 listing_4.16.cpp) 47 | # target_link_libraries(listing_4.16 pthread) 48 | -------------------------------------------------------------------------------- /src/ch04/listing_4.1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | bool more_data_to_prepare() 7 | { 8 | return false; 9 | } 10 | 11 | struct data_chunk 12 | {}; 13 | 14 | data_chunk prepare_data() 15 | { 16 | return data_chunk(); 17 | } 18 | 19 | void process(data_chunk&) 20 | {} 21 | 22 | bool is_last_chunk(data_chunk&) 23 | { 24 | return true; 25 | } 26 | 27 | std::mutex mut; 28 | std::queue data_queue; 29 | std::condition_variable data_cond; 30 | 31 | void data_preparation_thread() 32 | { 33 | while(more_data_to_prepare()) 34 | { 35 | data_chunk const data=prepare_data(); 36 | std::lock_guard lk(mut); 37 | data_queue.push(data); 38 | data_cond.notify_one(); 39 | } 40 | } 41 | 42 | void data_processing_thread() 43 | { 44 | while(true) 45 | { 46 | std::unique_lock lk(mut); 47 | data_cond.wait(lk,[]{return !data_queue.empty();}); 48 | data_chunk data=data_queue.front(); 49 | data_queue.pop(); 50 | lk.unlock(); 51 | process(data); 52 | if(is_last_chunk(data)) 53 | break; 54 | } 55 | } 56 | 57 | int main() 58 | { 59 | std::thread t1(data_preparation_thread); 60 | std::thread t2(data_processing_thread); 61 | 62 | t1.join(); 63 | t2.join(); 64 | } 65 | -------------------------------------------------------------------------------- /src/ch04/listing_4.10.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | void process_connections(connection_set& connections) 3 | { 4 | while(!done(connections)) 5 | { 6 | for(connection_iterator 7 | connection=connections.begin(),end=connections.end(); 8 | connection!=end; 9 | ++connection) 10 | { 11 | if(connection->has_incoming_data()) 12 | { 13 | data_packet data=connection->incoming(); 14 | std::promise& p= 15 | connection->get_promise(data.id); 16 | p.set_value(data.payload); 17 | } 18 | if(connection->has_outgoing_data()) 19 | { 20 | outgoing_packet data= 21 | connection->top_of_outgoing_queue(); 22 | connection->send(data.payload); 23 | data.promise.set_value(true); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ch04/listing_4.11.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | std::condition_variable cv; 5 | bool done; 6 | std::mutex m; 7 | bool wait_loop() 8 | { 9 | auto const timeout= std::chrono::steady_clock::now()+ 10 | std::chrono::milliseconds(500); 11 | std::unique_lock lk(m); 12 | while(!done) 13 | { 14 | if(cv.wait_until(lk,timeout)==std::cv_status::timeout) 15 | break; 16 | } 17 | return done; 18 | } 19 | -------------------------------------------------------------------------------- /src/ch04/listing_4.12.cpp: -------------------------------------------------------------------------------- 1 | template 2 | std::list sequential_quick_sort(std::list input) 3 | { 4 | if(input.empty()) 5 | { 6 | return input; 7 | } 8 | std::list result; 9 | result.splice(result.begin(),input,input.begin()); 10 | T const& pivot=*result.begin(); 11 | auto divide_point=std::partition(input.begin(),input.end(), 12 | [&](T const& t){return t lower_part; 14 | lower_part.splice(lower_part.end(),input,input.begin(), 15 | divide_point); 16 | auto new_lower( 17 | sequential_quick_sort(std::move(lower_part))); 18 | auto new_higher( 19 | sequential_quick_sort(std::move(input))); 20 | result.splice(result.end(),new_higher); 21 | Using synchronization of operations to simplify code 22 | result.splice(result.begin(),new_lower); 23 | return result; 24 | } 25 | -------------------------------------------------------------------------------- /src/ch04/listing_4.13.cpp: -------------------------------------------------------------------------------- 1 | template 2 | std::list parallel_quick_sort(std::list input) 3 | { 4 | if(input.empty()) 5 | { 6 | return input; 7 | } 8 | std::list result; 9 | result.splice(result.begin(),input,input.begin()); 10 | T const& pivot=*result.begin(); 11 | auto divide_point=std::partition(input.begin(),input.end(), 12 | [&](T const& t){return t lower_part; 14 | lower_part.splice(lower_part.end(),input,input.begin(), 15 | divide_point); 16 | std::future > new_lower( 17 | std::async(¶llel_quick_sort,std::move(lower_part))); 18 | auto new_higher( 19 | parallel_quick_sort(std::move(input))); 20 | result.splice(result.end(),new_higher); 21 | result.splice(result.begin(),new_lower.get()); 22 | return result; 23 | } 24 | -------------------------------------------------------------------------------- /src/ch04/listing_4.14.cpp: -------------------------------------------------------------------------------- 1 | template 2 | std::future::type> 3 | spawn_task(F&& f,A&& a) 4 | { 5 | typedef std::result_of::type result_type; 6 | std::packaged_task 7 | task(std::move(f)); 8 | std::future res(task.get_future()); 9 | std::thread t(std::move(task),std::move(a)); 10 | t.detach(); 11 | return res; 12 | } 13 | -------------------------------------------------------------------------------- /src/ch04/listing_4.15.cpp: -------------------------------------------------------------------------------- 1 | struct card_inserted 2 | { 3 | std::string account; 4 | }; 5 | class atm 6 | { 7 | messaging::receiver incoming; 8 | messaging::sender bank; 9 | messaging::sender interface_hardware; 10 | void (atm::*state)(); 11 | std::string account; 12 | std::string pin; 13 | void waiting_for_card() 14 | { 15 | interface_hardware.send(display_enter_card()); 16 | incoming.wait() 17 | .handle( 18 | [&](card_inserted const& msg) 19 | { 20 | account=msg.account; 21 | pin=""; 22 | interface_hardware.send(display_enter_pin()); 23 | state=&atm::getting_pin; 24 | } 25 | ); 26 | } 27 | void getting_pin(); 28 | public: 29 | void run() 30 | { 31 | state=&atm::waiting_for_card; 32 | try 33 | { 34 | for(;;) 35 | { 36 | (this->*state)(); 37 | } 38 | } 39 | catch(messaging::close_queue const&) 40 | { 41 | } 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /src/ch04/listing_4.16.cpp: -------------------------------------------------------------------------------- 1 | void atm::getting_pin() 2 | { 3 | incoming.wait() 4 | .handle( 5 | [&](digit_pressed const& msg) 6 | { 7 | unsigned const pin_length=4; 8 | pin+=msg.digit; 9 | if(pin.length()==pin_length) 10 | { 11 | bank.send(verify_pin(account,pin,incoming)); 12 | state=&atm::verifying_pin; 13 | } 14 | } 15 | ) 16 | .handle( 17 | [&](clear_last_pressed const& msg) 18 | { 19 | if(!pin.empty()) 20 | { 21 | pin.resize(pin.length()-1); 22 | } 23 | } 24 | ) 25 | .handle( 26 | [&](cancel_pressed const& msg) 27 | { 28 | state=&atm::done_processing; 29 | } 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/ch04/listing_4.2.cpp: -------------------------------------------------------------------------------- 1 | template > 2 | class queue { 3 | public: 4 | explicit queue(const Container&); 5 | explicit queue(Container&& = Container()); 6 | queue(queue&& q); 7 | 8 | template explicit queue(const Alloc&); 9 | template queue(const Container&, const Alloc&); 10 | template queue(Container&&, const Alloc&); 11 | template queue(queue&&, const Alloc&); 12 | 13 | queue& operator=(queue&& q); 14 | void swap(queue&& q); 15 | 16 | bool empty() const; 17 | size_type size() const; 18 | 19 | T& front(); 20 | const T& front() const; 21 | T& back(); 22 | const T& back() const; 23 | 24 | void push(const T& x); 25 | void push(T&& x); 26 | void pop(); 27 | }; 28 | 29 | int main() 30 | {} 31 | -------------------------------------------------------------------------------- /src/ch04/listing_4.3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | template 3 | class threadsafe_queue 4 | { 5 | public: 6 | threadsafe_queue(); 7 | threadsafe_queue(const threadsafe_queue&); 8 | threadsafe_queue& operator=(const threadsafe_queue&) = delete; 9 | 10 | void push(T new_value); 11 | 12 | bool try_pop(T& value); 13 | std::shared_ptr try_pop(); 14 | 15 | void wait_and_pop(T& value); 16 | std::shared_ptr wait_and_pop(); 17 | 18 | bool empty() const; 19 | }; 20 | 21 | int main() 22 | {} 23 | -------------------------------------------------------------------------------- /src/ch04/listing_4.4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | template 5 | class threadsafe_queue 6 | { 7 | private: 8 | std::mutex mut; 9 | std::queue data_queue; 10 | std::condition_variable data_cond; 11 | public: 12 | void push(T new_value) 13 | { 14 | std::lock_guard lk(mut); 15 | data_queue.push(new_value); 16 | data_cond.notify_one(); 17 | } 18 | 19 | void wait_and_pop(T& value) 20 | { 21 | std::unique_lock lk(mut); 22 | data_cond.wait(lk,[this]{return !data_queue.empty();}); 23 | value=data_queue.front(); 24 | data_queue.pop(); 25 | } 26 | }; 27 | 28 | 29 | struct data_chunk 30 | {}; 31 | 32 | data_chunk prepare_data(); 33 | bool more_data_to_prepare(); 34 | void process(data_chunk); 35 | bool is_last_chunk(data_chunk); 36 | 37 | threadsafe_queue data_queue; 38 | 39 | void data_preparation_thread() 40 | { 41 | while(more_data_to_prepare()) 42 | { 43 | data_chunk const data=prepare_data(); 44 | data_queue.push(data); 45 | } 46 | } 47 | 48 | void data_processing_thread() 49 | { 50 | while(true) 51 | { 52 | data_chunk data; 53 | data_queue.wait_and_pop(data); 54 | process(data); 55 | if(is_last_chunk(data)) 56 | break; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/ch04/listing_4.5.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | template 7 | class threadsafe_queue 8 | { 9 | private: 10 | mutable std::mutex mut; 11 | std::queue data_queue; 12 | std::condition_variable data_cond; 13 | public: 14 | threadsafe_queue() 15 | {} 16 | threadsafe_queue(threadsafe_queue const& other) 17 | { 18 | std::lock_guard lk(other.mut); 19 | data_queue=other.data_queue; 20 | } 21 | 22 | void push(T new_value) 23 | { 24 | std::lock_guard lk(mut); 25 | data_queue.push(new_value); 26 | data_cond.notify_one(); 27 | } 28 | 29 | void wait_and_pop(T& value) 30 | { 31 | std::unique_lock lk(mut); 32 | data_cond.wait(lk,[this]{return !data_queue.empty();}); 33 | value=data_queue.front(); 34 | data_queue.pop(); 35 | } 36 | 37 | std::shared_ptr wait_and_pop() 38 | { 39 | std::unique_lock lk(mut); 40 | data_cond.wait(lk,[this]{return !data_queue.empty();}); 41 | std::shared_ptr res(std::make_shared(data_queue.front())); 42 | data_queue.pop(); 43 | return res; 44 | } 45 | 46 | bool try_pop(T& value) 47 | { 48 | std::lock_guard lk(mut); 49 | if(data_queue.empty) 50 | return false; 51 | value=data_queue.front(); 52 | data_queue.pop(); 53 | } 54 | 55 | std::shared_ptr try_pop() 56 | { 57 | std::lock_guard lk(mut); 58 | if(data_queue.empty()) 59 | return std::shared_ptr(); 60 | std::shared_ptr res(std::make_shared(data_queue.front())); 61 | data_queue.pop(); 62 | return res; 63 | } 64 | 65 | bool empty() const 66 | { 67 | std::lock_guard lk(mut); 68 | return data_queue.empty(); 69 | } 70 | }; 71 | 72 | int main() 73 | {} 74 | -------------------------------------------------------------------------------- /src/ch04/listing_4.6.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | int find_the_answer_to_ltuae() 4 | { 5 | return 42; 6 | } 7 | 8 | void do_other_stuff() 9 | {} 10 | 11 | int main() 12 | { 13 | std::future the_answer=std::async(find_the_answer_to_ltuae); 14 | do_other_stuff(); 15 | std::cout<<"The answer is "< 2 | #include 3 | 4 | struct X 5 | { 6 | void foo(int,std::string const&); 7 | std::string bar(std::string const&); 8 | }; 9 | 10 | 11 | X x; 12 | auto f1=std::async(&X::foo,&x,42,"hello"); 13 | auto f2=std::async(&X::bar,x,"goodbye"); 14 | 15 | struct Y 16 | { 17 | double operator()(double); 18 | }; 19 | Y y; 20 | auto f3=std::async(Y(),3.141); 21 | auto f4=std::async(std::ref(y),2.718); 22 | X baz(X&); 23 | auto f6=std::async(baz,std::ref(x)); 24 | class move_only 25 | { 26 | public: 27 | move_only(); 28 | move_only(move_only&&); 29 | move_only(move_only const&) = delete; 30 | move_only& operator=(move_only&&); 31 | move_only& operator=(move_only const&) = delete; 32 | void operator()(); 33 | }; 34 | auto f5=std::async(move_only()); 35 | -------------------------------------------------------------------------------- /src/ch04/listing_4.8.cpp: -------------------------------------------------------------------------------- 1 | template<> 2 | class packaged_task*,int)> 3 | { 4 | public: 5 | template 6 | explicit packaged_task(Callable&& f); 7 | std::future get_future(); 8 | void operator()(std::vector*,int); 9 | }; 10 | -------------------------------------------------------------------------------- /src/ch04/listing_4.9.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::mutex m; 8 | std::deque > tasks; 9 | 10 | bool gui_shutdown_message_received(); 11 | void get_and_process_gui_message(); 12 | 13 | void gui_thread() 14 | { 15 | while(!gui_shutdown_message_received()) 16 | { 17 | get_and_process_gui_message(); 18 | std::packaged_task task; 19 | { 20 | std::lock_guard lk(m); 21 | if(tasks.empty()) 22 | continue; 23 | task=std::move(tasks.front()); 24 | tasks.pop_front(); 25 | } 26 | task(); 27 | } 28 | } 29 | 30 | std::thread gui_bg_thread(gui_thread); 31 | 32 | template 33 | std::future post_task_for_gui_thread(Func f) 34 | { 35 | std::packaged_task task(f); 36 | std::future res=task.get_future(); 37 | std::lock_guard lk(m); 38 | tasks.push_back(std::move(task)); 39 | return res; 40 | } 41 | -------------------------------------------------------------------------------- /src/ch05/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(ch05 listing_5.01.cpp) 2 | 3 | add_executable(listing_5.02 listing_5.02.cpp) 4 | target_link_libraries(listing_5.02 pthread) 5 | 6 | add_executable(listing_5.03 listing_5.03.cpp) 7 | target_link_libraries(listing_5.03 pthread) 8 | 9 | add_executable(listing_5.04 listing_5.04.cpp) 10 | target_link_libraries(listing_5.04 pthread) 11 | 12 | add_executable(listing_5.05 listing_5.05.cpp) 13 | target_link_libraries(listing_5.05 pthread) 14 | 15 | add_executable(listing_5.06 listing_5.06.cpp) 16 | target_link_libraries(listing_5.06 pthread) 17 | 18 | add_executable(listing_5.07 listing_5.07.cpp) 19 | target_link_libraries(listing_5.07 pthread) 20 | 21 | add_executable(listing_5.08 listing_5.08.cpp) 22 | target_link_libraries(listing_5.08 pthread) 23 | 24 | add_executable(listing_5.09 listing_5.09.cpp) 25 | target_link_libraries(listing_5.09 pthread) 26 | 27 | add_executable(listing_5.10 listing_5.10.cpp) 28 | target_link_libraries(listing_5.10 pthread) 29 | 30 | add_executable(listing_5.11 listing_5.11.cpp) 31 | target_link_libraries(listing_5.11 pthread) 32 | 33 | add_executable(listing_5.12 listing_5.12.cpp) 34 | target_link_libraries(listing_5.12 pthread) 35 | 36 | add_executable(listing_5.13 listing_5.13.cpp) 37 | target_link_libraries(listing_5.13 pthread) 38 | -------------------------------------------------------------------------------- /src/ch05/listing_5.01.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class spinlock_mutex 4 | { 5 | std::atomic_flag flag; 6 | public: 7 | spinlock_mutex(): 8 | flag(ATOMIC_FLAG_INIT) 9 | {} 10 | void lock() 11 | { 12 | while(flag.test_and_set(std::memory_order_acquire)); 13 | } 14 | void unlock() 15 | { 16 | flag.clear(std::memory_order_release); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /src/ch05/listing_5.02.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::vector data; 8 | std::atomic_bool data_ready(false); 9 | 10 | void reader_thread() 11 | { 12 | while (!data_ready.load()) { 13 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 14 | } 15 | std::cout<<"The answer = "< 2 | 3 | void foo(int a,int b) 4 | { 5 | std::cout< 2 | #include 3 | #include 4 | 5 | std::atomic x,y; 6 | std::atomic z; 7 | 8 | void write_x() 9 | { 10 | x.store(true, std::memory_order_seq_cst); 11 | } 12 | 13 | void write_y() 14 | { 15 | y.store(true, std::memory_order_seq_cst); 16 | } 17 | 18 | void read_x_then_y() 19 | { 20 | while (!x.load(std::memory_order_seq_cst)); 21 | 22 | if(y.load(std::memory_order_seq_cst)) 23 | ++z; 24 | } 25 | 26 | void read_y_then_x() 27 | { 28 | while (!y.load(std::memory_order_seq_cst)); 29 | 30 | if(x.load(std::memory_order_seq_cst)) 31 | ++z; 32 | } 33 | 34 | int main() 35 | { 36 | x = false; 37 | y = false; 38 | z = 0; 39 | std::thread a(write_x); 40 | std::thread b(write_y); 41 | std::thread c(read_x_then_y); 42 | std::thread d(read_y_then_x); 43 | a.join(); 44 | b.join(); 45 | c.join(); 46 | d.join(); 47 | assert(z.load() != 0); 48 | } 49 | -------------------------------------------------------------------------------- /src/ch05/listing_5.05.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::atomic x,y; 6 | std::atomic z; 7 | 8 | void write_x_then_y() 9 | { 10 | x.store(true, std::memory_order_relaxed); 11 | y.store(true, std::memory_order_relaxed); 12 | } 13 | 14 | void read_y_then_x() 15 | { 16 | while (!y.load(std::memory_order_relaxed)); 17 | 18 | if (x.load(std::memory_order_relaxed)) 19 | ++z; 20 | } 21 | 22 | int main() 23 | { 24 | x = false; 25 | y = false; 26 | z = 0; 27 | std::thread a(write_x_then_y); 28 | std::thread b(read_y_then_x); 29 | a.join(); 30 | b.join(); 31 | assert(z.load() != 0); 32 | } 33 | -------------------------------------------------------------------------------- /src/ch05/listing_5.06.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::atomic x(0),y(0),z(0); 6 | std::atomic go(false); 7 | unsigned const loop_count = 10; 8 | 9 | struct read_values 10 | { 11 | int x,y,z; 12 | }; 13 | 14 | read_values values1[loop_count]; 15 | read_values values2[loop_count]; 16 | read_values values3[loop_count]; 17 | read_values values4[loop_count]; 18 | read_values values5[loop_count]; 19 | 20 | void increment(std::atomic* var_to_inc, read_values* values) 21 | { 22 | while (!go) 23 | std::this_thread::yield(); 24 | 25 | for (unsigned i = 0; i < loop_count; ++i) { 26 | values[i].x = x.load(std::memory_order_relaxed); 27 | values[i].y = y.load(std::memory_order_relaxed); 28 | values[i].z = z.load(std::memory_order_relaxed); 29 | var_to_inc->store(i+1, std::memory_order_relaxed); 30 | std::this_thread::yield(); 31 | } 32 | } 33 | 34 | void read_vals(read_values* values) 35 | { 36 | while (!go) 37 | std::this_thread::yield(); 38 | 39 | for (unsigned i = 0; i < loop_count; ++i) { 40 | values[i].x = x.load(std::memory_order_relaxed); 41 | values[i].y = y.load(std::memory_order_relaxed); 42 | values[i].z = z.load(std::memory_order_relaxed); 43 | std::this_thread::yield(); 44 | } 45 | } 46 | 47 | void print(read_values* v) 48 | { 49 | for (unsigned i = 0; i < loop_count; ++i) { 50 | if (i) 51 | std::cout<<","; 52 | std::cout<<"("< 2 | #include 3 | #include 4 | 5 | std::atomic x,y; 6 | std::atomic z; 7 | 8 | void write_x() 9 | { 10 | x.store(true, std::memory_order_release); 11 | } 12 | 13 | void write_y() 14 | { 15 | y.store(true, std::memory_order_release); 16 | } 17 | 18 | void read_x_then_y() 19 | { 20 | while (!x.load(std::memory_order_acquire)); 21 | 22 | if (y.load(std::memory_order_acquire)) 23 | ++z; 24 | } 25 | 26 | void read_y_then_x() 27 | { 28 | while (!y.load(std::memory_order_acquire)); 29 | 30 | if (x.load(std::memory_order_acquire)) 31 | ++z; 32 | } 33 | 34 | int main() 35 | { 36 | x = false; 37 | y = false; 38 | z = 0; 39 | std::thread a(write_x); 40 | std::thread b(write_y); 41 | std::thread c(read_x_then_y); 42 | std::thread d(read_y_then_x); 43 | a.join(); 44 | b.join(); 45 | c.join(); 46 | d.join(); 47 | assert(z.load() != 0); 48 | } 49 | -------------------------------------------------------------------------------- /src/ch05/listing_5.08.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::atomic x,y; 6 | std::atomic z; 7 | 8 | void write_x_then_y() 9 | { 10 | x.store(true, std::memory_order_relaxed); 11 | y.store(true, std::memory_order_release); 12 | } 13 | 14 | void read_y_then_x() 15 | { 16 | while (!y.load(std::memory_order_acquire)); 17 | 18 | if (x.load(std::memory_order_relaxed)) 19 | ++z; 20 | } 21 | 22 | int main() 23 | { 24 | x = false; 25 | y = false; 26 | z = 0; 27 | std::thread a(write_x_then_y); 28 | std::thread b(read_y_then_x); 29 | a.join(); 30 | b.join(); 31 | assert(z.load() != 0); 32 | } 33 | -------------------------------------------------------------------------------- /src/ch05/listing_5.09.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::atomic data[5]; 6 | std::atomic sync1(false), sync2(false); 7 | 8 | void thread_1() 9 | { 10 | data[0].store(42, std::memory_order_relaxed); 11 | data[1].store(97, std::memory_order_relaxed); 12 | data[2].store(17, std::memory_order_relaxed); 13 | data[3].store(-141, std::memory_order_relaxed); 14 | data[4].store(2003, std::memory_order_relaxed); 15 | sync1.store(true, std::memory_order_release); 16 | } 17 | 18 | void thread_2() 19 | { 20 | while (!sync1.load(std::memory_order_acquire)); 21 | 22 | sync2.store(std::memory_order_release); 23 | } 24 | 25 | void thread_3() 26 | { 27 | while (!sync2.load(std::memory_order_acquire)); 28 | 29 | assert(data[0].load(std::memory_order_relaxed) == 42); 30 | assert(data[1].load(std::memory_order_relaxed) == 97); 31 | assert(data[2].load(std::memory_order_relaxed) == 17); 32 | assert(data[3].load(std::memory_order_relaxed) == -141); 33 | assert(data[4].load(std::memory_order_relaxed) == 2003); 34 | } 35 | 36 | int main() 37 | { 38 | std::thread t1(thread_1); 39 | std::thread t2(thread_2); 40 | std::thread t3(thread_3); 41 | t1.join(); 42 | t2.join(); 43 | t3.join(); 44 | } 45 | -------------------------------------------------------------------------------- /src/ch05/listing_5.10.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | struct X 6 | { 7 | int i; 8 | std::string s; 9 | }; 10 | 11 | std::atomic p; 12 | std::atomic a; 13 | 14 | void create_x() 15 | { 16 | X* x = new X; 17 | x->i = 42; 18 | x->s = "hello"; 19 | a.store(99, std::memory_order_relaxed); 20 | p.store(x, std::memory_order_release); 21 | } 22 | 23 | void use_x() 24 | { 25 | X* x; 26 | while (!(x=p.load(std::memory_order_consume))) { 27 | std::this_thread::sleep_for(std::chrono::microseconds(1)); 28 | } 29 | assert(x->i == 42); 30 | assert(x->s == "hello"); 31 | assert(a.load(std::memory_order_relaxed) == 99); 32 | } 33 | int main() 34 | { 35 | std::thread t1(create_x); 36 | std::thread t2(use_x); 37 | t1.join(); 38 | t2.join(); 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/ch05/listing_5.11.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::vector queue_data; 6 | std::atomic count; 7 | 8 | void populate_queue() 9 | { 10 | unsigned const number_of_items=20; 11 | queue_data.clear(); 12 | for (unsigned i = 0; i < number_of_items; ++i) { 13 | queue_data.push_back(i); 14 | } 15 | 16 | count.store(number_of_items, std::memory_order_release); 17 | } 18 | 19 | void consume_queue_items() 20 | { 21 | while (true) { 22 | int item_index; 23 | if ((item_index=count.fetch_sub(1,std::memory_order_acquire)) <= 0) { 24 | // wait_for_more_items(); 25 | continue; 26 | } 27 | // process(queue_data[item_index-1]); 28 | } 29 | } 30 | 31 | int main() 32 | { 33 | std::thread a(populate_queue); 34 | std::thread b(consume_queue_items); 35 | std::thread c(consume_queue_items); 36 | a.join(); 37 | b.join(); 38 | c.join(); 39 | } 40 | -------------------------------------------------------------------------------- /src/ch05/listing_5.12.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::atomic x,y; 6 | std::atomic z; 7 | 8 | void write_x_then_y() 9 | { 10 | x.store(true,std::memory_order_relaxed); 11 | std::atomic_thread_fence(std::memory_order_release); 12 | y.store(true,std::memory_order_relaxed); 13 | } 14 | 15 | void read_y_then_x() 16 | { 17 | while (!y.load(std::memory_order_relaxed)); 18 | 19 | std::atomic_thread_fence(std::memory_order_acquire); 20 | 21 | if (x.load(std::memory_order_relaxed)) 22 | ++z; 23 | } 24 | 25 | int main() 26 | { 27 | x=false; 28 | y=false; 29 | z=0; 30 | std::thread a(write_x_then_y); 31 | std::thread b(read_y_then_x); 32 | a.join(); 33 | b.join(); 34 | assert(z.load() != 0); 35 | } 36 | -------------------------------------------------------------------------------- /src/ch05/listing_5.13.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | bool x=false; 6 | std::atomic y; 7 | std::atomic z; 8 | 9 | void write_x_then_y() 10 | { 11 | x = true; 12 | std::atomic_thread_fence(std::memory_order_release); 13 | y.store(true, std::memory_order_relaxed); 14 | } 15 | 16 | void read_y_then_x() 17 | { 18 | while (!y.load(std::memory_order_relaxed)); 19 | 20 | std::atomic_thread_fence(std::memory_order_acquire); 21 | 22 | if(x) 23 | ++z; 24 | } 25 | 26 | int main() 27 | { 28 | x=false; 29 | y=false; 30 | z=0; 31 | std::thread a(write_x_then_y); 32 | std::thread b(read_y_then_x); 33 | a.join(); 34 | b.join(); 35 | assert(z.load() != 0); 36 | } 37 | -------------------------------------------------------------------------------- /src/ch06/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # add_executable(listing_6.1 listing_6.1.cpp) 2 | # target_link_libraries(listing_6.1 pthread) 3 | 4 | add_executable(listing_6.2 listing_6.2.cpp) 5 | target_link_libraries(listing_6.2 pthread) 6 | 7 | # add_executable(listing_6.3 listing_6.3.cpp) 8 | # target_link_libraries(listing_6.3 pthread) 9 | 10 | # add_executable(listing_6.4 listing_6.4.cpp) 11 | # target_link_libraries(listing_6.4 pthread) 12 | 13 | # add_executable(listing_6.5 listing_6.5.cpp) 14 | # target_link_libraries(listing_6.5 pthread) 15 | 16 | # add_executable(listing_6.6 listing_6.6.cpp) 17 | # target_link_libraries(listing_6.6 pthread) 18 | 19 | # add_executable(listing_6.7 listing_6.7.cpp) 20 | # target_link_libraries(listing_6.7 pthread) 21 | 22 | # add_executable(listing_6.8 listing_6.8.cpp) 23 | # target_link_libraries(listing_6.8 pthread) 24 | 25 | # add_executable(listing_6.9 listing_6.9.cpp) 26 | # target_link_libraries(listing_6.9 pthread) 27 | 28 | # add_executable(listing_6.10 listing_6.10.cpp) 29 | # target_link_libraries(listing_6.10 pthread) 30 | 31 | # add_executable(listing_6.11 listing_6.11.cpp) 32 | # target_link_libraries(listing_6.11 pthread) 33 | 34 | # add_executable(listing_6.12 listing_6.12.cpp) 35 | # target_link_libraries(listing_6.12 pthread) 36 | 37 | # add_executable(listing_6.13 listing_6.13.cpp) 38 | # target_link_libraries(listing_6.13 pthread) -------------------------------------------------------------------------------- /src/ch06/listing_6.1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct empty_stack: std::exception 7 | { 8 | const char* what() const throw() 9 | { 10 | return "empty stack"; 11 | } 12 | }; 13 | 14 | template 15 | class threadsafe_stack 16 | { 17 | private: 18 | std::stack data; 19 | mutable std::mutex m; 20 | public: 21 | threadsafe_stack(){} 22 | threadsafe_stack(const threadsafe_stack& other) 23 | { 24 | std::lock_guard lock(other.m); 25 | data=other.data; 26 | } 27 | threadsafe_stack& operator=(const threadsafe_stack&) = delete; 28 | 29 | void push(T new_value) 30 | { 31 | std::lock_guard lock(m); 32 | data.push(std::move(new_value)); 33 | } 34 | std::shared_ptr pop() 35 | { 36 | std::lock_guard lock(m); 37 | if(data.empty()) throw empty_stack(); 38 | std::shared_ptr const res( 39 | std::make_shared(std::move(data.top()))); 40 | data.pop(); 41 | return res; 42 | } 43 | void pop(T& value) 44 | { 45 | std::lock_guard lock(m); 46 | if(data.empty()) throw empty_stack(); 47 | value=std::move(data.top()); 48 | data.pop(); 49 | } 50 | bool empty() const 51 | { 52 | std::lock_guard lock(m); 53 | return data.empty(); 54 | } 55 | }; 56 | 57 | -------------------------------------------------------------------------------- /src/ch06/listing_6.10.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class threadsafe_queue 3 | { 4 | private: 5 | std::unique_ptr try_pop_head() 6 | { 7 | std::lock_guard head_lock(head_mutex); 8 | if(head.get()==get_tail()) 9 | { 10 | return std::unique_ptr(); 11 | } 12 | return pop_head(); 13 | } 14 | 15 | std::unique_ptr try_pop_head(T& value) 16 | { 17 | std::lock_guard head_lock(head_mutex); 18 | if(head.get()==get_tail()) 19 | { 20 | return std::unique_ptr(); 21 | } 22 | value=std::move(*head->data); 23 | return pop_head(); 24 | } 25 | 26 | public: 27 | std::shared_ptr try_pop() 28 | { 29 | std::unique_ptr const old_head=try_pop_head(); 30 | return old_head?old_head->data:std::shared_ptr(); 31 | } 32 | 33 | bool try_pop(T& value) 34 | { 35 | std::unique_ptr const old_head=try_pop_head(value); 36 | return old_head; 37 | } 38 | 39 | void empty() 40 | { 41 | std::lock_guard head_lock(head_mutex); 42 | return (head==get_tail()); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/ch06/listing_6.11.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | template > 10 | class threadsafe_lookup_table 11 | { 12 | private: 13 | class bucket_type 14 | { 15 | private: 16 | typedef std::pair bucket_value; 17 | typedef std::list bucket_data; 18 | typedef typename bucket_data::iterator bucket_iterator; 19 | 20 | bucket_data data; 21 | mutable boost::shared_mutex mutex; 22 | 23 | bucket_iterator find_entry_for(Key const& key) const 24 | { 25 | return std::find_if(data.begin(),data.end(), 26 | [&](bucket_value const& item) 27 | {return item.first==key;}); 28 | } 29 | public: 30 | Value value_for(Key const& key,Value const& default_value) const 31 | { 32 | boost::shared_lock lock(mutex); 33 | bucket_iterator const found_entry=find_entry_for(key); 34 | return (found_entry==data.end())? 35 | default_value : found_entry->second; 36 | } 37 | 38 | void add_or_update_mapping(Key const& key,Value const& value) 39 | { 40 | std::unique_lock lock(mutex); 41 | bucket_iterator const found_entry=find_entry_for(key); 42 | if(found_entry==data.end()) 43 | { 44 | data.push_back(bucket_value(key,value)); 45 | } 46 | else 47 | { 48 | found_entry->second=value; 49 | } 50 | } 51 | 52 | void remove_mapping(Key const& key) 53 | { 54 | std::unique_lock lock(mutex); 55 | bucket_iterator const found_entry=find_entry_for(key); 56 | if(found_entry!=data.end()) 57 | { 58 | data.erase(found_entry); 59 | } 60 | } 61 | }; 62 | 63 | std::vector > buckets; 64 | Hash hasher; 65 | 66 | bucket_type& get_bucket(Key const& key) const 67 | { 68 | std::size_t const bucket_index=hasher(key)%buckets.size(); 69 | return *buckets[bucket_index]; 70 | } 71 | 72 | public: 73 | typedef Key key_type; 74 | typedef Value mapped_type; 75 | typedef Hash hash_type; 76 | 77 | threadsafe_lookup_table( 78 | unsigned num_buckets=19, Hash const& hasher_=Hash()): 79 | buckets(num_buckets),hasher(hasher_) 80 | { 81 | for(unsigned i=0;i threadsafe_lookup_table::get_map() const 2 | { 3 | std::vector > locks; 4 | for(unsigned i=0;i(buckets[i].mutex)); 8 | } 9 | std::map res; 10 | for(unsigned i=0;i 2 | #include 3 | 4 | template 5 | class threadsafe_list 6 | { 7 | struct node 8 | { 9 | std::mutex m; 10 | std::shared_ptr data; 11 | std::unique_ptr next; 12 | 13 | node(): 14 | next() 15 | {} 16 | 17 | node(T const& value): 18 | data(std::make_shared(value)) 19 | {} 20 | }; 21 | 22 | node head; 23 | 24 | public: 25 | threadsafe_list() 26 | {} 27 | 28 | ~threadsafe_list() 29 | { 30 | remove_if([](T const&){return true;}); 31 | } 32 | 33 | threadsafe_list(threadsafe_list const& other)=delete; 34 | threadsafe_list& operator=(threadsafe_list const& other)=delete; 35 | 36 | void push_front(T const& value) 37 | { 38 | std::unique_ptr new_node(new node(value)); 39 | std::lock_guard lk(head.m); 40 | new_node->next=std::move(head.next); 41 | head.next=std::move(new_node); 42 | } 43 | 44 | template 45 | void for_each(Function f) 46 | { 47 | node* current=&head; 48 | std::unique_lock lk(head.m); 49 | while(node* const next=current->next.get()) 50 | { 51 | std::unique_lock next_lk(next->m); 52 | lk.unlock(); 53 | f(*next->data); 54 | current=next; 55 | lk=std::move(next_lk); 56 | } 57 | } 58 | 59 | template 60 | std::shared_ptr find_first_if(Predicate p) 61 | { 62 | node* current=&head; 63 | std::unique_lock lk(head.m); 64 | while(node* const next=current->next.get()) 65 | { 66 | std::unique_lock next_lk(next->m); 67 | lk.unlock(); 68 | if(p(*next->data)) 69 | { 70 | return next->data; 71 | } 72 | current=next; 73 | lk=std::move(next_lk); 74 | } 75 | return std::shared_ptr(); 76 | } 77 | 78 | template 79 | void remove_if(Predicate p) 80 | { 81 | node* current=&head; 82 | std::unique_lock lk(head.m); 83 | while(node* const next=current->next.get()) 84 | { 85 | std::unique_lock next_lk(next->m); 86 | if(p(*next->data)) 87 | { 88 | std::unique_ptr old_next=std::move(current->next); 89 | current->next=std::move(next->next); 90 | next_lk.unlock(); 91 | } 92 | else 93 | { 94 | lk.unlock(); 95 | current=next; 96 | lk=std::move(next_lk); 97 | } 98 | } 99 | } 100 | }; 101 | 102 | -------------------------------------------------------------------------------- /src/ch06/listing_6.2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | template 7 | class threadsafe_queue 8 | { 9 | private: 10 | mutable std::mutex mut; 11 | std::queue data_queue; 12 | std::condition_variable data_cond; 13 | public: 14 | threadsafe_queue() 15 | {} 16 | 17 | void push(T new_value) 18 | { 19 | std::lock_guard lk(mut); 20 | data_queue.push(std::move(new_value)); 21 | data_cond.notify_one(); 22 | } 23 | 24 | void wait_and_pop(T& value) 25 | { 26 | std::unique_lock lk(mut); 27 | data_cond.wait(lk,[this]{return !data_queue.empty();}); 28 | value=std::move(data_queue.front()); 29 | data_queue.pop(); 30 | } 31 | 32 | std::shared_ptr wait_and_pop() 33 | { 34 | std::unique_lock lk(mut); 35 | data_cond.wait(lk,[this]{return !data_queue.empty();}); 36 | std::shared_ptr res( 37 | std::make_shared(std::move(data_queue.front()))); 38 | data_queue.pop(); 39 | return res; 40 | } 41 | 42 | bool try_pop(T& value) 43 | { 44 | std::lock_guard lk(mut); 45 | if(data_queue.empty()) 46 | return false; 47 | value=std::move(data_queue.front()); 48 | data_queue.pop(); 49 | } 50 | 51 | std::shared_ptr try_pop() 52 | { 53 | std::lock_guard lk(mut); 54 | if(data_queue.empty()) 55 | return std::shared_ptr(); 56 | std::shared_ptr res( 57 | std::make_shared(std::move(data_queue.front()))); 58 | data_queue.pop(); 59 | return res; 60 | } 61 | 62 | bool empty() const 63 | { 64 | std::lock_guard lk(mut); 65 | return data_queue.empty(); 66 | } 67 | }; 68 | 69 | int main() 70 | { 71 | threadsafe_queue rq; 72 | } 73 | -------------------------------------------------------------------------------- /src/ch06/listing_6.3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | template 7 | class threadsafe_queue 8 | { 9 | private: 10 | mutable std::mutex mut; 11 | std::queue > data_queue; 12 | std::condition_variable data_cond; 13 | public: 14 | threadsafe_queue() 15 | {} 16 | 17 | void wait_and_pop(T& value) 18 | { 19 | std::unique_lock lk(mut); 20 | data_cond.wait(lk,[this]{return !data_queue.empty();}); 21 | value=std::move(*data_queue.front()); 22 | data_queue.pop(); 23 | } 24 | 25 | bool try_pop(T& value) 26 | { 27 | std::lock_guard lk(mut); 28 | if(data_queue.empty()) 29 | return false; 30 | value=std::move(*data_queue.front()); 31 | data_queue.pop(); 32 | } 33 | 34 | std::shared_ptr wait_and_pop() 35 | { 36 | std::unique_lock lk(mut); 37 | data_cond.wait(lk,[this]{return !data_queue.empty();}); 38 | std::shared_ptr res=data_queue.front(); 39 | data_queue.pop(); 40 | return res; 41 | } 42 | 43 | std::shared_ptr try_pop() 44 | { 45 | std::lock_guard lk(mut); 46 | if(data_queue.empty()) 47 | return std::shared_ptr(); 48 | std::shared_ptr res=data_queue.front(); 49 | data_queue.pop(); 50 | return res; 51 | } 52 | 53 | bool empty() const 54 | { 55 | std::lock_guard lk(mut); 56 | return data_queue.empty(); 57 | } 58 | 59 | void push(T new_value) 60 | { 61 | std::shared_ptr data( 62 | std::make_shared(std::move(new_value))); 63 | std::lock_guard lk(mut); 64 | data_queue.push(data); 65 | data_cond.notify_one(); 66 | } 67 | 68 | }; 69 | -------------------------------------------------------------------------------- /src/ch06/listing_6.4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | class queue 5 | { 6 | private: 7 | struct node 8 | { 9 | T data; 10 | std::unique_ptr next; 11 | 12 | node(T data_): 13 | data(std::move(data_)) 14 | {} 15 | }; 16 | 17 | std::unique_ptr head; 18 | node* tail; 19 | 20 | public: 21 | queue(): 22 | tail(nullptr) 23 | {} 24 | 25 | queue(const queue& other)=delete; 26 | queue& operator=(const queue& other)=delete; 27 | 28 | std::shared_ptr try_pop() 29 | { 30 | if(!head) 31 | { 32 | return std::shared_ptr(); 33 | } 34 | std::shared_ptr const res( 35 | std::make_shared(std::move(head->data))); 36 | std::unique_ptr const old_head=std::move(head); 37 | head=std::move(old_head->next); 38 | return res; 39 | } 40 | 41 | void push(T new_value) 42 | { 43 | std::unique_ptr p(new node(std::move(new_value))); 44 | node* const new_tail=p.get(); 45 | if(tail) 46 | { 47 | tail->next=std::move(p); 48 | } 49 | else 50 | { 51 | head=std::move(p); 52 | } 53 | tail=new_tail; 54 | } 55 | }; 56 | 57 | -------------------------------------------------------------------------------- /src/ch06/listing_6.5.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | template 3 | class queue 4 | { 5 | private: 6 | struct node 7 | { 8 | std::shared_ptr data; 9 | std::unique_ptr next; 10 | }; 11 | 12 | std::unique_ptr head; 13 | node* tail; 14 | 15 | public: 16 | queue(): 17 | head(new node),tail(head.get()) 18 | {} 19 | 20 | queue(const queue& other)=delete; 21 | queue& operator=(const queue& other)=delete; 22 | 23 | std::shared_ptr try_pop() 24 | { 25 | if(head.get()==tail) 26 | { 27 | return std::shared_ptr(); 28 | } 29 | std::shared_ptr const res(head->data); 30 | std::unique_ptr const old_head=std::move(head); 31 | head=std::move(old_head->next); 32 | return res; 33 | } 34 | 35 | void push(T new_value) 36 | { 37 | std::shared_ptr new_data( 38 | std::make_shared(std::move(new_value))); 39 | std::unique_ptr p(new node); 40 | tail->data=new_data; 41 | node* const new_tail=p.get(); 42 | tail->next=std::move(p); 43 | tail=new_tail; 44 | } 45 | }; 46 | 47 | -------------------------------------------------------------------------------- /src/ch06/listing_6.6.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | class threadsafe_queue 6 | { 7 | private: 8 | struct node 9 | { 10 | std::shared_ptr data; 11 | std::unique_ptr next; 12 | }; 13 | 14 | std::mutex head_mutex; 15 | std::unique_ptr head; 16 | std::mutex tail_mutex; 17 | node* tail; 18 | 19 | node* get_tail() 20 | { 21 | std::lock_guard tail_lock(tail_mutex); 22 | return tail; 23 | } 24 | 25 | std::unique_ptr pop_head() 26 | { 27 | std::lock_guard head_lock(head_mutex); 28 | if(head.get()==get_tail()) 29 | { 30 | return nullptr; 31 | } 32 | std::unique_ptr const old_head=std::move(head); 33 | head=std::move(old_head->next); 34 | return old_head; 35 | } 36 | 37 | 38 | public: 39 | threadsafe_queue(): 40 | head(new node),tail(head.get()) 41 | {} 42 | 43 | threadsafe_queue(const threadsafe_queue& other)=delete; 44 | threadsafe_queue& operator=(const threadsafe_queue& other)=delete; 45 | 46 | std::shared_ptr try_pop() 47 | { 48 | std::unique_ptr old_head=pop_head(); 49 | return old_head?old_head->data:std::shared_ptr(); 50 | } 51 | 52 | void push(T new_value) 53 | { 54 | std::shared_ptr new_data( 55 | std::make_shared(std::move(new_value))); 56 | std::unique_ptr p(new node); 57 | node* const new_tail=p.get(); 58 | std::lock_guard tail_lock(tail_mutex); 59 | tail->data=new_data; 60 | tail->next=std::move(p); 61 | tail=new_tail; 62 | } 63 | }; 64 | 65 | -------------------------------------------------------------------------------- /src/ch06/listing_6.7.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class threadsafe_queue 3 | { 4 | private: 5 | struct node 6 | { 7 | std::shared_ptr data; 8 | std::unique_ptr next; 9 | }; 10 | 11 | std::mutex head_mutex; 12 | std::unique_ptr head; 13 | std::mutex tail_mutex; 14 | node* tail; 15 | std::condition_variable data_cond; 16 | public: 17 | threadsafe_queue(): 18 | head(new node),tail(head.get()) 19 | {} 20 | threadsafe_queue(const threadsafe_queue& other)=delete; 21 | threadsafe_queue& operator=(const threadsafe_queue& other)=delete; 22 | 23 | std::shared_ptr try_pop(); 24 | bool try_pop(T& value); 25 | std::shared_ptr wait_and_pop(); 26 | void wait_and_pop(T& value); 27 | void push(T new_value); 28 | void empty(); 29 | }; 30 | -------------------------------------------------------------------------------- /src/ch06/listing_6.8.cpp: -------------------------------------------------------------------------------- 1 | template 2 | void threadsafe_queue::push(T new_value) 3 | { 4 | std::shared_ptr new_data( 5 | std::make_shared(std::move(new_value))); 6 | std::unique_ptr p(new node); 7 | { 8 | std::lock_guard tail_lock(tail_mutex); 9 | tail->data=new_data; 10 | node* const new_tail=p.get(); 11 | tail->next=std::move(p); 12 | tail=new_tail; 13 | } 14 | data_cond.notify_one(); 15 | } 16 | -------------------------------------------------------------------------------- /src/ch06/listing_6.9.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class threadsafe_queue 3 | { 4 | private: 5 | node* get_tail() 6 | { 7 | std::lock_guard tail_lock(tail_mutex); 8 | return tail; 9 | } 10 | 11 | std::unique_ptr pop_head() 12 | { 13 | std::unique_ptr const old_head=std::move(head); 14 | head=std::move(old_head->next); 15 | return old_head; 16 | } 17 | 18 | std::unique_lock wait_for_data() 19 | { 20 | std::unique_lock head_lock(head_mutex); 21 | data_cond.wait(head_lock,[&]{return head!=get_tail();}); 22 | return std::move(head_lock); 23 | } 24 | 25 | std::unique_ptr wait_pop_head() 26 | { 27 | std::unique_lock head_lock(wait_for_data()); 28 | return pop_head(); 29 | } 30 | 31 | std::unique_ptr wait_pop_head(T& value) 32 | { 33 | std::unique_lock head_lock(wait_for_data()); 34 | value=std::move(*head->data); 35 | return pop_head(); 36 | } 37 | 38 | public: 39 | std::shared_ptr wait_and_pop() 40 | { 41 | std::unique_ptr const old_head=wait_pop_head(); 42 | return old_head->data; 43 | } 44 | 45 | void wait_and_pop(T& value) 46 | { 47 | std::unique_ptr const old_head=wait_pop_head(value); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /src/ch07/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # add_executable(listing_7.1 listing_7.1.cpp) 2 | # target_link_libraries(listing_7.1 pthread) -------------------------------------------------------------------------------- /src/ch07/listing_7.1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class spinlock_mutex 4 | { 5 | std::atomic_flag flag; 6 | public: 7 | spinlock_mutex(): 8 | flag(ATOMIC_FLAG_INIT) 9 | {} 10 | void lock() 11 | { 12 | while(flag.test_and_set(std::memory_order_acquire)); 13 | } 14 | void unlock() 15 | { 16 | flag.clear(std::memory_order_release); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /src/ch07/listing_7.10.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | class lock_free_stack 6 | { 7 | private: 8 | struct node; 9 | struct counted_node_ptr 10 | { 11 | int external_count; 12 | node* ptr; 13 | }; 14 | struct node 15 | { 16 | std::shared_ptr data; 17 | std::atomic internal_count; 18 | counted_node_ptr next; 19 | node(T const& data_): 20 | data(std::make_shared(data_)), 21 | internal_count(0) 22 | {} 23 | }; 24 | std::atomic head; 25 | public: 26 | ~lock_free_stack() 27 | { 28 | while(pop()); 29 | } 30 | void push(T const& data) 31 | { 32 | counted_node_ptr new_node; 33 | new_node.ptr=new node(data); 34 | new_node.external_count=1; 35 | new_node.ptr->next=head.load(); 36 | while(!head.compare_exchange_weak(new_node.ptr->next,new_node)); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /src/ch07/listing_7.11.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class lock_free_stack 3 | { 4 | private: 5 | void increase_head_count(counted_node_ptr& old_counter) 6 | { 7 | counted_node_ptr new_counter; 8 | do 9 | { 10 | new_counter=old_counter; 11 | ++new_counter.external_count; 12 | } 13 | while(!head.compare_exchange_strong(old_counter,new_counter)); 14 | old_counter.external_count=new_counter.external_count; 15 | } 16 | public: 17 | std::shared_ptr pop() 18 | { 19 | counted_node_ptr old_head=head.load(); 20 | for(;;) 21 | { 22 | increase_head_count(old_head); 23 | node* const ptr=old_head.ptr; 24 | if(!ptr) 25 | { 26 | return std::shared_ptr(); 27 | } 28 | if(head.compare_exchange_strong(old_head,ptr->next)) 29 | { 30 | std::shared_ptr res; 31 | res.swap(ptr->data); 32 | int const count_increase=old_head.external_count-2; 33 | if(ptr->internal_count.fetch_add(count_increase)== 34 | -count_increase) 35 | { 36 | delete ptr; 37 | } 38 | return res; 39 | } 40 | else if(ptr->internal_count.fetch_sub(1)==1) 41 | { 42 | delete ptr; 43 | } 44 | } 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /src/ch07/listing_7.12.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | class lock_free_stack 6 | { 7 | private: 8 | struct node; 9 | struct counted_node_ptr 10 | { 11 | int external_count; 12 | node* ptr; 13 | }; 14 | struct node 15 | { 16 | std::shared_ptr data; 17 | std::atomic internal_count; 18 | counted_node_ptr next; 19 | node(T const& data_): 20 | data(std::make_shared(data_)), 21 | internal_count(0) 22 | {} 23 | }; 24 | std::atomic head; 25 | void increase_head_count(counted_node_ptr& old_counter) 26 | { 27 | counted_node_ptr new_counter; 28 | do 29 | { 30 | new_counter=old_counter; 31 | ++new_counter.external_count; 32 | } 33 | while(!head.compare_exchange_strong( 34 | old_counter,new_counter, 35 | std::memory_order_acquire, 36 | std::memory_order_relaxed)); 37 | old_counter.external_count=new_counter.external_count; 38 | } 39 | public: 40 | ~lock_free_stack() 41 | { 42 | while(pop()); 43 | } 44 | void push(T const& data) 45 | { 46 | counted_node_ptr new_node; 47 | new_node.ptr=new node(data); 48 | new_node.external_count=1; 49 | new_node.ptr->next=head.load(std::memory_order_relaxed) 50 | while(!head.compare_exchange_weak( 51 | new_node.ptr->next,new_node, 52 | std::memory_order_release, 53 | std::memory_order_relaxed)); 54 | } 55 | std::shared_ptr pop() 56 | { 57 | counted_node_ptr old_head= 58 | head.load(std::memory_order_relaxed); 59 | for(;;) 60 | { 61 | increase_head_count(old_head); 62 | node* const ptr=old_head.ptr; 63 | if(!ptr) 64 | { 65 | return std::shared_ptr(); 66 | } 67 | if(head.compare_exchange_strong( 68 | old_head,ptr->next,std::memory_order_relaxed)) 69 | { 70 | std::shared_ptr res; 71 | res.swap(ptr->data); 72 | int const count_increase=old_head.external_count-2; 73 | if(ptr->internal_count.fetch_add( 74 | count_increase,std::memory_order_release)==-count_increase) 75 | { 76 | delete ptr; 77 | } 78 | return res; 79 | } 80 | else if(ptr->internal_count.fetch_add( 81 | -1,std::memory_order_relaxed)==1) 82 | { 83 | ptr->internal_count.load(std::memory_order_acquire); 84 | delete ptr; 85 | } 86 | } 87 | } 88 | }; 89 | -------------------------------------------------------------------------------- /src/ch07/listing_7.13.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | class lock_free_queue 6 | { 7 | private: 8 | struct node 9 | { 10 | std::shared_ptr data; 11 | node* next; 12 | node(): 13 | next(nullptr) 14 | {} 15 | }; 16 | std::atomic head; 17 | std::atomic tail; 18 | node* pop_head() 19 | { 20 | node* const old_head=head.load(); 21 | if(old_head==tail.load()) 22 | { 23 | return nullptr; 24 | } 25 | head.store(old_head->next); 26 | return old_head; 27 | } 28 | public: 29 | lock_free_queue(): 30 | head(new node),tail(head.load()) 31 | {} 32 | lock_free_queue(const lock_free_queue& other)=delete; 33 | lock_free_queue& operator=(const lock_free_queue& other)=delete; 34 | ~lock_free_queue() 35 | { 36 | while(node* const old_head=head.load()) 37 | { 38 | head.store(old_head->next); 39 | delete old_head; 40 | } 41 | } 42 | std::shared_ptr pop() 43 | { 44 | node* old_head=pop_head(); 45 | if(!old_head) 46 | { 47 | return std::shared_ptr(); 48 | } 49 | std::shared_ptr const res(old_head->data); 50 | delete old_head; 51 | return res; 52 | } 53 | void push(T new_value) 54 | { 55 | std::shared_ptr new_data(std::make_shared(new_value)); 56 | node* p=new node; 57 | node* const old_tail=tail.load(); 58 | old_tail->data.swap(new_data); 59 | old_tail->next=p; 60 | tail.store(p); 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /src/ch07/listing_7.14.cpp: -------------------------------------------------------------------------------- 1 | void push(T new_value) 2 | { 3 | std::unique_ptr new_data(new T(new_value)); 4 | counted_node_ptr new_next; 5 | new_next.ptr=new node; 6 | new_next.external_count=1; 7 | for(;;) 8 | { 9 | node* const old_tail=tail.load(); 10 | T* old_data=nullptr; 11 | if(old_tail->data.compare_exchange_strong( 12 | old_data,new_data.get())) 13 | { 14 | old_tail->next=new_next; 15 | tail.store(new_next.ptr); 16 | new_data.release(); 17 | break; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ch07/listing_7.15.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | class lock_free_queue 5 | { 6 | private: 7 | struct node; 8 | struct counted_node_ptr 9 | { 10 | int external_count; 11 | node* ptr; 12 | }; 13 | std::atomic head; 14 | std::atomic tail; 15 | struct node_counter 16 | { 17 | unsigned internal_count:30; 18 | unsigned external_counters:2; 19 | }; 20 | struct node 21 | { 22 | std::atomic data; 23 | std::atomic count; 24 | counted_node_ptr next; 25 | node() 26 | { 27 | node_counter new_count; 28 | new_count.internal_count=0; 29 | new_count.external_counters=2; 30 | count.store(new_count); 31 | next.ptr=nullptr; 32 | next.external_count=0; 33 | } 34 | }; 35 | public: 36 | void push(T new_value) 37 | { 38 | std::unique_ptr new_data(new T(new_value)); 39 | counted_node_ptr new_next; 40 | new_next.ptr=new node; 41 | new_next.external_count=1; 42 | counted_node_ptr old_tail=tail.load(); 43 | for(;;) 44 | { 45 | increase_external_count(tail,old_tail); 46 | T* old_data=nullptr; 47 | if(old_tail.ptr->data.compare_exchange_strong( 48 | old_data,new_data.get())) 49 | { 50 | old_tail.ptr->next=new_next; 51 | old_tail=tail.exchange(new_next); 52 | free_external_counter(old_tail); 53 | new_data.release(); 54 | break; 55 | } 56 | old_tail.ptr->release_ref(); 57 | } 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /src/ch07/listing_7.16.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class lock_free_queue 3 | { 4 | private: 5 | struct node 6 | { 7 | void release_ref(); 8 | }; 9 | public: 10 | std::unique_ptr pop() 11 | { 12 | counted_node_ptr old_head=head.load(std::memory_order_relaxed); 13 | for(;;) 14 | { 15 | increase_external_count(head,old_head); 16 | node* const ptr=old_head.ptr; 17 | if(ptr==tail.load().ptr) 18 | { 19 | ptr->release_ref(); 20 | return std::unique_ptr(); 21 | } 22 | if(head.compare_exchange_strong(old_head,ptr->next)) 23 | { 24 | T* const res=ptr->data.exchange(nullptr); 25 | free_external_counter(old_head); 26 | return std::unique_ptr(res); 27 | } 28 | ptr->release_ref(); 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/ch07/listing_7.17.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class lock_free_queue 3 | { 4 | private: 5 | struct node 6 | { 7 | void release_ref() 8 | { 9 | node_counter old_counter= 10 | count.load(std::memory_order_relaxed); 11 | node_counter new_counter; 12 | do 13 | { 14 | new_counter=old_counter; 15 | --new_counter.internal_count; 16 | } 17 | while(!count.compare_exchange_strong( 18 | old_counter,new_counter, 19 | std::memory_order_acquire,std::memory_order_relaxed)); 20 | if(!new_counter.internal_count && 21 | !new_counter.external_counters) 22 | { 23 | delete this; 24 | } 25 | } 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /src/ch07/listing_7.18.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class lock_free_queue 3 | { 4 | private: 5 | static void increase_external_count( 6 | std::atomic& counter, 7 | counted_node_ptr& old_counter) 8 | { 9 | counted_node_ptr new_counter; 10 | do 11 | { 12 | new_counter=old_counter; 13 | ++new_counter.external_count; 14 | } 15 | while(!counter.compare_exchange_strong( 16 | old_counter,new_counter, 17 | std::memory_order_acquire,std::memory_order_relaxed)); 18 | old_counter.external_count=new_counter.external_count; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/ch07/listing_7.19.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class lock_free_queue 3 | { 4 | private: 5 | static void free_external_counter(counted_node_ptr &old_node_ptr) 6 | { 7 | node* const ptr=old_node_ptr.ptr; 8 | int const count_increase=old_node_ptr.external_count-2; 9 | node_counter old_counter= 10 | ptr->count.load(std::memory_order_relaxed); 11 | node_counter new_counter; 12 | do 13 | { 14 | new_counter=old_counter; 15 | --new_counter.external_counters; 16 | new_counter.internal_count+=count_increase; 17 | } 18 | while(!ptr->count.compare_exchange_strong( 19 | old_counter,new_counter, 20 | std::memory_order_acquire,std::memory_order_relaxed)); 21 | if(!new_counter.internal_count && 22 | !new_counter.external_counters) 23 | { 24 | delete ptr; 25 | } 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/ch07/listing_7.2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | class lock_free_stack 5 | { 6 | private: 7 | struct node 8 | { 9 | T data; 10 | node* next; 11 | node(T const& data_): 12 | data(data_) 13 | {} 14 | }; 15 | std::atomic head; 16 | public: 17 | void push(T const& data) 18 | { 19 | node* const new_node=new node(data); 20 | new_node->next=head.load(); 21 | while(!head.compare_exchange_weak(new_node->next,new_node)); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/ch07/listing_7.20.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class lock_free_queue 3 | { 4 | private: 5 | struct node 6 | { 7 | std::atomic data; 8 | std::atomic count; 9 | std::atomic next; 10 | }; 11 | public: 12 | std::unique_ptr pop() 13 | { 14 | counted_node_ptr old_head=head.load(std::memory_order_relaxed); 15 | for(;;) 16 | { 17 | increase_external_count(head,old_head); 18 | node* const ptr=old_head.ptr; 19 | if(ptr==tail.load().ptr) 20 | { 21 | return std::unique_ptr(); 22 | } 23 | counted_node_ptr next=ptr->next.load(); 24 | if(head.compare_exchange_strong(old_head,next)) 25 | { 26 | T* const res=ptr->data.exchange(nullptr); 27 | free_external_counter(old_head); 28 | return std::unique_ptr(res); 29 | } 30 | ptr->release_ref(); 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/ch07/listing_7.21.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class lock_free_queue 3 | { 4 | private: 5 | void set_new_tail(counted_node_ptr &old_tail, 6 | counted_node_ptr const &new_tail) 7 | { 8 | node* const current_tail_ptr=old_tail.ptr; 9 | while(!tail.compare_exchange_weak(old_tail,new_tail) && 10 | old_tail.ptr==current_tail_ptr); 11 | if(old_tail.ptr==current_tail_ptr) 12 | free_external_counter(old_tail); 13 | else 14 | current_tail_ptr->release_ref(); 15 | } 16 | public: 17 | void push(T new_value) 18 | { 19 | std::unique_ptr new_data(new T(new_value)); 20 | counted_node_ptr new_next; 21 | new_next.ptr=new node; 22 | new_next.external_count=1; 23 | counted_node_ptr old_tail=tail.load(); 24 | for(;;) 25 | { 26 | increase_external_count(tail,old_tail); 27 | T* old_data=nullptr; 28 | if(old_tail.ptr->data.compare_exchange_strong( 29 | old_data,new_data.get())) 30 | { 31 | counted_node_ptr old_next={0}; 32 | if(!old_tail.ptr->next.compare_exchange_strong( 33 | old_next,new_next)) 34 | { 35 | delete new_next.ptr; 36 | new_next=old_next; 37 | } 38 | set_new_tail(old_tail, new_next); 39 | new_data.release(); 40 | break; 41 | } 42 | else 43 | { 44 | counted_node_ptr old_next={0}; 45 | if(old_tail.ptr->next.compare_exchange_strong( 46 | old_next,new_next)) 47 | { 48 | old_next=new_next; 49 | new_next.ptr=new node; 50 | } 51 | set_new_tail(old_tail, old_next); 52 | } 53 | } 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /src/ch07/listing_7.3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | class lock_free_stack 6 | { 7 | private: 8 | struct node 9 | { 10 | std::shared_ptr data; 11 | node* next; 12 | node(T const& data_): 13 | data(std::make_shared(data_)) 14 | {} 15 | }; 16 | std::atomic head; 17 | public: 18 | void push(T const& data) 19 | { 20 | node* const new_node=new node(data); 21 | new_node->next=head.load(); 22 | while(!head.compare_exchange_weak(new_node->next,new_node)); 23 | } 24 | std::shared_ptr pop() 25 | { 26 | node* old_head=head.load(); 27 | while(old_head && 28 | !head.compare_exchange_weak(old_head,old_head->next)); 29 | return old_head ? old_head->data : std::shared_ptr(); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/ch07/listing_7.4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | class lock_free_stack 6 | { 7 | private: 8 | std::atomic threads_in_pop; 9 | void try_reclaim(node* old_head); 10 | public: 11 | std::shared_ptr pop() 12 | { 13 | ++threads_in_pop; 14 | node* old_head=head.load(); 15 | while(old_head && 16 | !head.compare_exchange_weak(old_head,old_head->next)); 17 | std::shared_ptr res; 18 | if(old_head) 19 | { 20 | res.swap(old_head->data); 21 | } 22 | try_reclaim(old_head); 23 | return res; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/ch07/listing_7.5.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | class lock_free_stack 5 | { 6 | private: 7 | std::atomic to_be_deleted; 8 | static void delete_nodes(node* nodes) 9 | { 10 | while(nodes) 11 | { 12 | node* next=nodes->next; 13 | delete nodes; 14 | nodes=next; 15 | } 16 | } 17 | void try_reclaim(node* old_head) 18 | { 19 | if(threads_in_pop==1) 20 | { 21 | node* nodes_to_delete=to_be_deleted.exchange(nullptr); 22 | if(!--threads_in_pop) 23 | { 24 | delete_nodes(nodes_to_delete); 25 | } 26 | else if(nodes_to_delete) 27 | { 28 | chain_pending_nodes(nodes_to_delete); 29 | } 30 | delete old_head; 31 | } 32 | else 33 | { 34 | chain_pending_node(old_head); 35 | --threads_in_pop; 36 | } 37 | } 38 | void chain_pending_nodes(node* nodes) 39 | { 40 | node* last=nodes; 41 | while(node* const next=last->next) 42 | { 43 | last=next; 44 | } 45 | chain_pending_nodes(nodes,last); 46 | } 47 | void chain_pending_nodes(node* first,node* last) 48 | { 49 | last->next=to_be_deleted; 50 | while(!to_be_deleted.compare_exchange_weak( 51 | last->next,first)); 52 | } 53 | void chain_pending_node(node* n) 54 | { 55 | chain_pending_nodes(n,n); 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /src/ch07/listing_7.6.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | std::shared_ptr pop() 5 | { 6 | std::atomic& hp=get_hazard_pointer_for_current_thread(); 7 | node* old_head=head.load(); 8 | do 9 | { 10 | node* temp; 11 | do 12 | { 13 | temp=old_head; 14 | hp.store(old_head); 15 | old_head=head.load(); 16 | } while(old_head!=temp); 17 | } 18 | while(old_head && 19 | !head.compare_exchange_strong(old_head,old_head->next)); 20 | hp.store(nullptr); 21 | std::shared_ptr res; 22 | if(old_head) 23 | { 24 | res.swap(old_head->data); 25 | if(outstanding_hazard_pointers_for(old_head)) 26 | { 27 | reclaim_later(old_head); 28 | } 29 | else 30 | { 31 | delete old_head; 32 | } 33 | delete_nodes_with_no_hazards(); 34 | } 35 | return res; 36 | } 37 | -------------------------------------------------------------------------------- /src/ch07/listing_7.7.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | unsigned const max_hazard_pointers=100; 5 | struct hazard_pointer 6 | { 7 | std::atomic id; 8 | std::atomic pointer; 9 | }; 10 | hazard_pointer hazard_pointers[max_hazard_pointers]; 11 | class hp_owner 12 | { 13 | hazard_pointer* hp; 14 | public: 15 | hp_owner(hp_owner const&)=delete; 16 | hp_owner operator=(hp_owner const&)=delete; 17 | hp_owner(): 18 | hp(nullptr) 19 | { 20 | for(unsigned i=0;i& get_pointer() 36 | { 37 | return hp->pointer; 38 | } 39 | ~hp_owner() 40 | { 41 | hp->pointer.store(nullptr); 42 | hp->id.store(std::thread::id()); 43 | } 44 | }; 45 | std::atomic& get_hazard_pointer_for_current_thread() 46 | { 47 | thread_local static hp_owner hazard; 48 | return hazard.get_pointer(); 49 | } 50 | -------------------------------------------------------------------------------- /src/ch07/listing_7.8.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | void do_delete(void* p) 5 | { 6 | delete static_cast(p); 7 | } 8 | struct data_to_reclaim 9 | { 10 | void* data; 11 | std::function deleter; 12 | data_to_reclaim* next; 13 | template 14 | data_to_reclaim(T* p): 15 | data(p), 16 | deleter(&do_delete), 17 | next(0) 18 | {} 19 | ~data_to_reclaim() 20 | { 21 | deleter(data); 22 | } 23 | }; 24 | std::atomic nodes_to_reclaim; 25 | void add_to_reclaim_list(data_to_reclaim* node) 26 | { 27 | node->next=nodes_to_reclaim.load(); 28 | while(!nodes_to_reclaim.compare_exchange_weak(node->next,node)); 29 | } 30 | template 31 | void reclaim_later(T* data) 32 | { 33 | add_to_reclaim_list(new data_to_reclaim(data)); 34 | } 35 | void delete_nodes_with_no_hazards() 36 | { 37 | data_to_reclaim* current=nodes_to_reclaim.exchange(nullptr); 38 | while(current) 39 | { 40 | data_to_reclaim* const next=current->next; 41 | if(!outstanding_hazard_pointers_for(current->data)) 42 | { 43 | delete current; 44 | } 45 | else 46 | { 47 | add_to_reclaim_list(current); 48 | } 49 | current=next; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/ch07/listing_7.9.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | class lock_free_stack 6 | { 7 | private: 8 | struct node 9 | { 10 | std::shared_ptr data; 11 | std::shared_ptr next; 12 | node(T const& data_): 13 | data(std::make_shared(data_)) 14 | {} 15 | }; 16 | std::shared_ptr head; 17 | public: 18 | void push(T const& data) 19 | { 20 | std::shared_ptr const new_node=std::make_shared(data); 21 | new_node->next=head.load(); 22 | while(!std::atomic_compare_exchange_weak( 23 | &head,&new_node->next,new_node)); 24 | } 25 | std::shared_ptr pop() 26 | { 27 | std::shared_ptr old_head=std::atomic_load(&head); 28 | while(old_head && !std::atomic_compare_exchange_weak( 29 | &head,&old_head,old_head->next)); 30 | return old_head ? old_head->data : std::shared_ptr(); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /src/ch08/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # add_executable(listing_8.1 listing_8.1.cpp) 2 | # target_link_libraries(listing_8.1 pthread) -------------------------------------------------------------------------------- /src/ch08/listing_8.1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct sorter 3 | { 4 | struct chunk_to_sort 5 | { 6 | std::list data; 7 | std::promise > promise; 8 | }; 9 | 10 | thread_safe_stack chunks; 11 | std::vector threads; 12 | unsigned const max_thread_count; 13 | std::atomic end_of_data; 14 | 15 | sorter(): 16 | max_thread_count(std::thread::hardware_concurrency()-1), 17 | end_of_data(false) 18 | {} 19 | 20 | ~sorter() 21 | { 22 | end_of_data=true; 23 | for(unsigned i=0;i chunk=chunks.pop(); 32 | if(chunk) 33 | { 34 | sort_chunk(chunk); 35 | } 36 | } 37 | 38 | std::list do_sort(std::list& chunk_data) 39 | { 40 | if(chunk_data.empty()) 41 | { 42 | return chunk_data; 43 | } 44 | 45 | std::list result; 46 | result.splice(result.begin(),chunk_data,chunk_data.begin()); 47 | T const& partition_val=*result.begin(); 48 | 49 | typename std::list::iterator divide_point= 50 | std::partition(chunk_data.begin(),chunk_data.end(), 51 | [&](T const& val){return val > new_lower= 58 | new_lower_chunk.promise.get_future(); 59 | chunks.push(std::move(new_lower_chunk)); 60 | if(threads.size()::sort_thread,this)); 63 | } 64 | 65 | std::list new_higher(do_sort(chunk_data)); 66 | 67 | result.splice(result.end(),new_higher); 68 | while(new_lower.wait_for(std::chrono::seconds(0)) != 69 | std::future_status::ready) 70 | { 71 | try_sort_chunk(); 72 | } 73 | 74 | result.splice(result.begin(),new_lower.get()); 75 | return result; 76 | } 77 | 78 | void sort_chunk(boost::shared_ptr const& chunk) 79 | { 80 | chunk->promise.set_value(do_sort(chunk->data)); 81 | } 82 | 83 | void sort_thread() 84 | { 85 | while(!end_of_data) 86 | { 87 | try_sort_chunk(); 88 | std::this_thread::yield(); 89 | } 90 | } 91 | }; 92 | 93 | template 94 | std::list parallel_quick_sort(std::list input) 95 | { 96 | if(input.empty()) 97 | { 98 | return input; 99 | } 100 | sorter s; 101 | return s.do_sort(input); 102 | } 103 | -------------------------------------------------------------------------------- /src/ch08/listing_8.10.cpp: -------------------------------------------------------------------------------- 1 | template 2 | Iterator parallel_find_impl(Iterator first,Iterator last,MatchType match, 3 | std::atomic& done) 4 | { 5 | try 6 | { 7 | unsigned long const length=std::distance(first,last); 8 | unsigned long const min_per_thread=25; 9 | if(length<(2*min_per_thread)) 10 | { 11 | for(;(first!=last) && !done.load();++first) 12 | { 13 | if(*first==match) 14 | { 15 | done=true; 16 | return first; 17 | } 18 | } 19 | return last; 20 | } 21 | else 22 | { 23 | Iterator const mid_point=first+(length/2); 24 | std::future async_result= 25 | std::async(¶llel_find_impl, 26 | mid_point,last,match,std::ref(done)); 27 | Iterator const direct_result= 28 | parallel_find_impl(first,mid_point,match,done); 29 | return (direct_result==mid_point)? 30 | async_result.get():direct_result; 31 | } 32 | } 33 | catch(...) 34 | { 35 | done=true; 36 | throw; 37 | } 38 | } 39 | 40 | template 41 | Iterator parallel_find(Iterator first,Iterator last,MatchType match) 42 | { 43 | std::atomic done(false); 44 | return parallel_find_impl(first,last,match,done); 45 | } 46 | -------------------------------------------------------------------------------- /src/ch08/listing_8.11.cpp: -------------------------------------------------------------------------------- 1 | template 2 | void parallel_partial_sum(Iterator first,Iterator last) 3 | { 4 | typedef typename Iterator::value_type value_type; 5 | struct process_chunk 6 | { 7 | void operator()(Iterator begin,Iterator last, 8 | std::future* previous_end_value, 9 | std::promise* end_value) 10 | { 11 | try 12 | { 13 | Iterator end=last; 14 | ++end; 15 | std::partial_sum(begin,end,begin); 16 | if(previous_end_value) 17 | { 18 | value_type& addend=previous_end_value->get(); 19 | *last+=addend; 20 | if(end_value) 21 | { 22 | end_value->set_value(*last); 23 | } 24 | std::for_each(begin,last,[addend](value_type& item) 25 | { 26 | item+=addend; 27 | }); 28 | } 29 | else if(end_value) 30 | { 31 | end_value->set_value(*last); 32 | } 33 | } 34 | catch(...) 35 | { 36 | if(end_value) 37 | { 38 | end_value->set_exception(std::current_exception()); 39 | } 40 | else 41 | { 42 | throw; 43 | } 44 | } 45 | } 46 | }; 47 | 48 | unsigned long const length=std::distance(first,last); 49 | 50 | if(!length) 51 | return last; 52 | 53 | unsigned long const min_per_thread=25; 54 | unsigned long const max_threads= 55 | (length+min_per_thread-1)/min_per_thread; 56 | 57 | unsigned long const hardware_threads= 58 | std::thread::hardware_concurrency(); 59 | 60 | unsigned long const num_threads= 61 | std::min(hardware_threads!=0?hardware_threads:2,max_threads); 62 | 63 | unsigned long const block_size=length/num_threads; 64 | 65 | typedef typename Iterator::value_type value_type; 66 | 67 | std::vector threads(num_threads-1); 68 | std::vector > 69 | end_values(num_threads-1); 70 | std::vector > 71 | previous_end_values; 72 | previous_end_values.reserve(num_threads-1); 73 | join_threads joiner(threads); 74 | 75 | Iterator block_start=first; 76 | for(unsigned long i=0;i<(num_threads-1);++i) 77 | { 78 | Iterator block_last=block_start; 79 | std::advance(block_last,block_size-1); 80 | threads[i]=std::thread(process_chunk(), 81 | block_start,block_last, 82 | (i!=0)?&previous_end_values[i-1]:0, 83 | &end_values[i]); 84 | block_start=block_last; 85 | ++block_start; 86 | previous_end_values.push_back(end_values[i].get_future()); 87 | } 88 | Iterator final_element=block_start; 89 | std::advance(final_element,std::distance(block_start,last)-1); 90 | process_chunk()(block_start,final_element, 91 | (num_threads>1)?&previous_end_values.back():0, 92 | 0); 93 | } 94 | -------------------------------------------------------------------------------- /src/ch08/listing_8.12.cpp: -------------------------------------------------------------------------------- 1 | class barrier 2 | { 3 | unsigned const count; 4 | std::atomic spaces; 5 | std::atomic generation; 6 | public: 7 | explicit barrier(unsigned count_): 8 | count(count_),spaces(count),generation(0) 9 | {} 10 | void wait() 11 | { 12 | unsigned const my_generation=generation; 13 | if(!--spaces) 14 | { 15 | spaces=count; 16 | ++generation; 17 | } 18 | else 19 | { 20 | while(generation==my_generation) 21 | std::this_thread::yield(); 22 | } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/ch08/listing_8.13.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct join_threads 6 | { 7 | join_threads(std::vector&) 8 | {} 9 | }; 10 | 11 | 12 | struct barrier 13 | { 14 | std::atomic count; 15 | std::atomic spaces; 16 | std::atomic generation; 17 | barrier(unsigned count_): 18 | count(count_),spaces(count_),generation(0) 19 | {} 20 | void wait() 21 | { 22 | unsigned const gen=generation.load(); 23 | if(!--spaces) 24 | { 25 | spaces=count.load(); 26 | ++generation; 27 | } 28 | else 29 | { 30 | while(generation.load()==gen) 31 | { 32 | std::this_thread::yield(); 33 | } 34 | } 35 | } 36 | 37 | void done_waiting() 38 | { 39 | --count; 40 | if(!--spaces) 41 | { 42 | spaces=count.load(); 43 | ++generation; 44 | } 45 | } 46 | }; 47 | 48 | template 49 | void parallel_partial_sum(Iterator first,Iterator last) 50 | { 51 | typedef typename Iterator::value_type value_type; 52 | 53 | struct process_element 54 | { 55 | void operator()(Iterator first,Iterator last, 56 | std::vector& buffer, 57 | unsigned i,barrier& b) 58 | { 59 | value_type& ith_element=*(first+i); 60 | bool update_source=false; 61 | for(unsigned step=0,stride=1;stride<=i;++step,stride*=2) 62 | { 63 | value_type const& source=(step%2)? 64 | buffer[i]:ith_element; 65 | value_type& dest=(step%2)? 66 | ith_element:buffer[i]; 67 | value_type const& addend=(step%2)? 68 | buffer[i-stride]:*(first+i-stride); 69 | dest=source+addend; 70 | update_source=!(step%2); 71 | b.wait(); 72 | } 73 | if(update_source) 74 | { 75 | ith_element=buffer[i]; 76 | } 77 | b.done_waiting(); 78 | } 79 | }; 80 | 81 | unsigned long const length=std::distance(first,last); 82 | 83 | if(length<=1) 84 | return; 85 | 86 | std::vector buffer(length); 87 | barrier b(length); 88 | std::vector threads(length-1); 89 | join_threads joiner(threads); 90 | 91 | Iterator block_start=first; 92 | for(unsigned long i=0;i<(length-1);++i) 93 | { 94 | threads[i]=std::thread(process_element(),first,last, 95 | std::ref(buffer),i,std::ref(b)); 96 | } 97 | process_element()(first,last,buffer,length-1,b); 98 | } 99 | -------------------------------------------------------------------------------- /src/ch08/listing_8.2.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct accumulate_block 3 | { 4 | void operator()(Iterator first,Iterator last,T& result) 5 | { 6 | result=std::accumulate(first,last,result); 7 | } 8 | }; 9 | 10 | template 11 | T parallel_accumulate(Iterator first,Iterator last,T init) 12 | { 13 | unsigned long const length=std::distance(first,last); 14 | 15 | if(!length) 16 | return init; 17 | 18 | unsigned long const min_per_thread=25; 19 | unsigned long const max_threads= 20 | (length+min_per_thread-1)/min_per_thread; 21 | 22 | unsigned long const hardware_threads= 23 | std::thread::hardware_concurrency(); 24 | 25 | unsigned long const num_threads= 26 | std::min(hardware_threads!=0?hardware_threads:2,max_threads); 27 | 28 | unsigned long const block_size=length/num_threads; 29 | 30 | std::vector results(num_threads); 31 | std::vector threads(num_threads-1); 32 | 33 | Iterator block_start=first; 34 | for(unsigned long i=0;i<(num_threads-1);++i) 35 | { 36 | Iterator block_end=block_start; 37 | std::advance(block_end,block_size); 38 | threads[i]=std::thread( 39 | accumulate_block(), 40 | block_start,block_end,std::ref(results[i])); 41 | block_start=block_end; 42 | } 43 | accumulate_block()(block_start,last,results[num_threads-1]); 44 | 45 | std::for_each(threads.begin(),threads.end(), 46 | std::mem_fn(&std::thread::join)); 47 | 48 | return std::accumulate(results.begin(),results.end(),init); 49 | } 50 | -------------------------------------------------------------------------------- /src/ch08/listing_8.3.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct accumulate_block 3 | { 4 | T operator()(Iterator first,Iterator last) 5 | { 6 | return std::accumulate(first,last,T()); 7 | } 8 | }; 9 | 10 | template 11 | T parallel_accumulate(Iterator first,Iterator last,T init) 12 | { 13 | unsigned long const length=std::distance(first,last); 14 | 15 | if(!length) 16 | return init; 17 | 18 | unsigned long const min_per_thread=25; 19 | unsigned long const max_threads= 20 | (length+min_per_thread-1)/min_per_thread; 21 | 22 | unsigned long const hardware_threads= 23 | std::thread::hardware_concurrency(); 24 | 25 | unsigned long const num_threads= 26 | std::min(hardware_threads!=0?hardware_threads:2,max_threads); 27 | 28 | unsigned long const block_size=length/num_threads; 29 | 30 | std::vector > futures(num_threads-1); 31 | std::vector threads(num_threads-1); 32 | 33 | Iterator block_start=first; 34 | for(unsigned long i=0;i<(num_threads-1);++i) 35 | { 36 | Iterator block_end=block_start; 37 | std::advance(block_end,block_size); 38 | std::packaged_task task( 39 | accumulate_block()); 40 | futures[i]=task.get_future(); 41 | threads[i]=std::thread(std::move(task),block_start,block_end); 42 | block_start=block_end; 43 | } 44 | T last_result=accumulate_block()(block_start,last); 45 | 46 | std::for_each(threads.begin(),threads.end(), 47 | std::mem_fn(&std::thread::join)); 48 | 49 | T result=init; 50 | for(unsigned long i=0;i<(num_threads-1);++i) 51 | { 52 | result+=futures[i].get(); 53 | } 54 | result += last_result; 55 | return result; 56 | } 57 | -------------------------------------------------------------------------------- /src/ch08/listing_8.4.cpp: -------------------------------------------------------------------------------- 1 | template 2 | T parallel_accumulate(Iterator first,Iterator last,T init) 3 | { 4 | unsigned long const length=std::distance(first,last); 5 | 6 | if(!length) 7 | return init; 8 | 9 | unsigned long const min_per_thread=25; 10 | unsigned long const max_threads= 11 | (length+min_per_thread-1)/min_per_thread; 12 | 13 | unsigned long const hardware_threads= 14 | std::thread::hardware_concurrency(); 15 | 16 | unsigned long const num_threads= 17 | std::min(hardware_threads!=0?hardware_threads:2,max_threads); 18 | 19 | unsigned long const block_size=length/num_threads; 20 | 21 | std::vector > futures(num_threads-1); 22 | std::vector threads(num_threads-1); 23 | join_threads joiner(threads); 24 | 25 | Iterator block_start=first; 26 | for(unsigned long i=0;i<(num_threads-1);++i) 27 | { 28 | Iterator block_end=block_start; 29 | std::advance(block_end,block_size); 30 | std::packaged_task task( 31 | accumulate_block()); 32 | futures[i]=task.get_future(); 33 | threads[i]=std::thread(std::move(task),block_start,block_end); 34 | block_start=block_end; 35 | } 36 | T last_result=accumulate_block()(block_start,last); 37 | T result=init; 38 | for(unsigned long i=0;i<(num_threads-1);++i) 39 | { 40 | result+=futures[i].get(); 41 | } 42 | result += last_result; 43 | return result; 44 | } 45 | -------------------------------------------------------------------------------- /src/ch08/listing_8.5.cpp: -------------------------------------------------------------------------------- 1 | template 2 | T parallel_accumulate(Iterator first,Iterator last,T init) 3 | { 4 | unsigned long const length=std::distance(first,last); 5 | unsigned long const max_chunk_size=25; 6 | if(length<=max_chunk_size) 7 | { 8 | return std::accumulate(first,last,init); 9 | } 10 | else 11 | { 12 | Iterator mid_point=first; 13 | std::advance(mid_point,length/2); 14 | std::future first_half_result= 15 | std::async(parallel_accumulate, 16 | first,mid_point,init); 17 | T second_half_result=parallel_accumulate(mid_point,last,T()); 18 | return first_half_result.get()+second_half_result; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ch08/listing_8.6.cpp: -------------------------------------------------------------------------------- 1 | std::thread task_thread; 2 | std::atomic task_cancelled(false); 3 | 4 | void gui_thread() 5 | { 6 | while(true) 7 | { 8 | event_data event=get_event(); 9 | if(event.type==quit) 10 | break; 11 | process(event); 12 | } 13 | } 14 | 15 | void task() 16 | { 17 | while(!task_complete() && !task_cancelled) 18 | { 19 | do_next_operation(); 20 | } 21 | if(task_cancelled) 22 | { 23 | perform_cleanup(); 24 | } 25 | else 26 | { 27 | post_gui_event(task_complete); 28 | } 29 | } 30 | 31 | void process(event_data const& event) 32 | { 33 | switch(event.type) 34 | { 35 | case start_task: 36 | task_cancelled=false; 37 | task_thread=std::thread(task); 38 | break; 39 | case stop_task: 40 | task_cancelled=true; 41 | task_thread.join(); 42 | break; 43 | case task_complete: 44 | task_thread.join(); 45 | display_results(); 46 | break; 47 | default: 48 | //... 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/ch08/listing_8.7.cpp: -------------------------------------------------------------------------------- 1 | template 2 | void parallel_for_each(Iterator first,Iterator last,Func f) 3 | { 4 | unsigned long const length=std::distance(first,last); 5 | 6 | if(!length) 7 | return; 8 | 9 | unsigned long const min_per_thread=25; 10 | unsigned long const max_threads= 11 | (length+min_per_thread-1)/min_per_thread; 12 | 13 | unsigned long const hardware_threads= 14 | std::thread::hardware_concurrency(); 15 | 16 | unsigned long const num_threads= 17 | std::min(hardware_threads!=0?hardware_threads:2,max_threads); 18 | 19 | unsigned long const block_size=length/num_threads; 20 | 21 | std::vector > futures(num_threads-1); 22 | std::vector threads(num_threads-1); 23 | join_threads joiner(threads); 24 | 25 | Iterator block_start=first; 26 | for(unsigned long i=0;i<(num_threads-1);++i) 27 | { 28 | Iterator block_end=block_start; 29 | std::advance(block_end,block_size); 30 | std::packaged_task task( 31 | [=]() 32 | { 33 | std::for_each(block_start,block_end,f); 34 | }); 35 | futures[i]=task.get_future(); 36 | threads[i]=std::thread(std::move(task)); 37 | block_start=block_end; 38 | } 39 | std::for_each(block_start,last,f); 40 | for(unsigned long i=0;i<(num_threads-1);++i) 41 | { 42 | futures[i].get(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ch08/listing_8.8.cpp: -------------------------------------------------------------------------------- 1 | template 2 | void parallel_for_each(Iterator first,Iterator last,Func f) 3 | { 4 | unsigned long const length=std::distance(first,last); 5 | 6 | if(!length) 7 | return; 8 | 9 | unsigned long const min_per_thread=25; 10 | 11 | if(length<(2*min_per_thread)) 12 | { 13 | std::for_each(first,last,f); 14 | } 15 | else 16 | { 17 | Iterator const mid_point=first+length/2; 18 | std::future first_half= 19 | std::async(¶llel_for_each, 20 | first,mid_point,f); 21 | parallel_for_each(mid_point,last,f); 22 | first_half.get(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ch08/listing_8.9.cpp: -------------------------------------------------------------------------------- 1 | template 2 | Iterator parallel_find(Iterator first,Iterator last,MatchType match) 3 | { 4 | struct find_element 5 | { 6 | void operator()(Iterator begin,Iterator end, 7 | MatchType match, 8 | std::promise* result, 9 | std::atomic* done_flag) 10 | { 11 | try 12 | { 13 | for(;(begin!=end) && !done_flag->load();++begin) 14 | { 15 | if(*begin==match) 16 | { 17 | result->set_value(begin); 18 | done_flag->store(true); 19 | return; 20 | } 21 | } 22 | } 23 | catch(...) 24 | { 25 | try 26 | { 27 | result->set_exception(std::current_exception()); 28 | done_flag->store(true); 29 | } 30 | catch(...) 31 | {} 32 | } 33 | } 34 | }; 35 | 36 | unsigned long const length=std::distance(first,last); 37 | 38 | if(!length) 39 | return last; 40 | 41 | unsigned long const min_per_thread=25; 42 | unsigned long const max_threads= 43 | (length+min_per_thread-1)/min_per_thread; 44 | 45 | unsigned long const hardware_threads= 46 | std::thread::hardware_concurrency(); 47 | 48 | unsigned long const num_threads= 49 | std::min(hardware_threads!=0?hardware_threads:2,max_threads); 50 | 51 | unsigned long const block_size=length/num_threads; 52 | 53 | std::promise result; 54 | std::atomic done_flag(false); 55 | std::vector threads(num_threads-1); 56 | { 57 | join_threads joiner(threads); 58 | 59 | Iterator block_start=first; 60 | for(unsigned long i=0;i<(num_threads-1);++i) 61 | { 62 | Iterator block_end=block_start; 63 | std::advance(block_end,block_size); 64 | threads[i]=std::thread(find_element(), 65 | block_start,block_end,match, 66 | &result,&done_flag); 67 | block_start=block_end; 68 | } 69 | find_element()(block_start,last,match,&result,&done_flag); 70 | } 71 | if(!done_flag.load()) 72 | { 73 | return last; 74 | } 75 | return result.get_future().get(); 76 | } 77 | -------------------------------------------------------------------------------- /src/ch09/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # add_executable(listing_9.1 listing_9.1.cpp) 2 | # target_link_libraries(listing_9.1 pthread) 3 | 4 | # add_executable(listing_9.13 listing_9.13.cpp) 5 | # target_link_libraries(listing_9.13 pthread) -------------------------------------------------------------------------------- /src/ch09/listing_9.1.cpp: -------------------------------------------------------------------------------- 1 | class thread_pool 2 | { 3 | std::atomic_bool done; 4 | thread_safe_queue > work_queue; 5 | std::vector threads; 6 | join_threads joiner; 7 | 8 | void worker_thread() 9 | { 10 | while(!done) 11 | { 12 | std::function task; 13 | if(work_queue.try_pop(task)) 14 | { 15 | task(); 16 | } 17 | else 18 | { 19 | std::this_thread::yield(); 20 | } 21 | } 22 | } 23 | public: 24 | thread_pool(): 25 | done(false),joiner(threads) 26 | { 27 | unsigned const thread_count=std::thread::hardware_concurrency(); 28 | try 29 | { 30 | for(unsigned i=0;i 49 | void submit(FunctionType f) 50 | { 51 | work_queue.push(std::function(f)); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/ch09/listing_9.10.cpp: -------------------------------------------------------------------------------- 1 | void interruptible_wait(std::condition_variable& cv, 2 | std::unique_lock& lk) 3 | { 4 | interruption_point(); 5 | this_thread_interrupt_flag.set_condition_variable(cv); 6 | cv.wait(lk); 7 | this_thread_interrupt_flag.clear_condition_variable(); 8 | interruption_point(); 9 | } 10 | -------------------------------------------------------------------------------- /src/ch09/listing_9.11.cpp: -------------------------------------------------------------------------------- 1 | class interrupt_flag 2 | { 3 | std::atomic flag; 4 | std::condition_variable* thread_cond; 5 | std::mutex set_clear_mutex; 6 | 7 | public: 8 | interrupt_flag(): 9 | thread_cond(0) 10 | {} 11 | 12 | void set() 13 | { 14 | flag.store(true,std::memory_order_relaxed); 15 | std::lock_guard lk(set_clear_mutex); 16 | if(thread_cond) 17 | { 18 | thread_cond->notify_all(); 19 | } 20 | } 21 | 22 | bool is_set() const 23 | { 24 | return flag.load(std::memory_order_relaxed); 25 | } 26 | 27 | void set_condition_variable(std::condition_variable& cv) 28 | { 29 | std::lock_guard lk(set_clear_mutex); 30 | thread_cond=&cv; 31 | } 32 | 33 | void clear_condition_variable() 34 | { 35 | std::lock_guard lk(set_clear_mutex); 36 | thread_cond=0; 37 | } 38 | 39 | struct clear_cv_on_destruct 40 | { 41 | ~clear_cv_on_destruct() 42 | { 43 | this_thread_interrupt_flag.clear_condition_variable(); 44 | } 45 | }; 46 | }; 47 | 48 | void interruptible_wait(std::condition_variable& cv, 49 | std::unique_lock& lk) 50 | { 51 | interruption_point(); 52 | this_thread_interrupt_flag.set_condition_variable(cv); 53 | interrupt_flag::clear_cv_on_destruct guard; 54 | interruption_point(); 55 | cv.wait_for(lk,std::chrono::milliseconds(1)); 56 | interruption_point(); 57 | } 58 | -------------------------------------------------------------------------------- /src/ch09/listing_9.12.cpp: -------------------------------------------------------------------------------- 1 | class interrupt_flag 2 | { 3 | std::atomic flag; 4 | std::condition_variable* thread_cond; 5 | std::condition_variable_any* thread_cond_any; 6 | std::mutex set_clear_mutex; 7 | 8 | public: 9 | interrupt_flag(): 10 | Interrupting threads 11 | thread_cond(0),thread_cond_any(0) 12 | {} 13 | 14 | void set() 15 | { 16 | flag.store(true,std::memory_order_relaxed); 17 | std::lock_guard lk(set_clear_mutex); 18 | if(thread_cond) 19 | { 20 | thread_cond->notify_all(); 21 | } 22 | else if(thread_cond_any) 23 | { 24 | thread_cond_any->notify_all(); 25 | } 26 | } 27 | 28 | template 29 | void wait(std::condition_variable_any& cv,Lockable& lk) 30 | { 31 | struct custom_lock 32 | { 33 | interrupt_flag* self; 34 | Lockable& lk; 35 | 36 | custom_lock(interrupt_flag* self_, 37 | std::condition_variable_any& cond, 38 | Lockable& lk_): 39 | self(self_),lk(lk_) 40 | { 41 | self->set_clear_mutex.lock(); 42 | self->thread_cond_any=&cond; 43 | } 44 | 45 | void unlock() 46 | { 47 | lk.unlock(); 48 | self->set_clear_mutex.unlock(); 49 | } 50 | 51 | void lock() 52 | { 53 | std::lock(self->set_clear_mutex,lk); 54 | } 55 | 56 | ~custom_lock() 57 | { 58 | self->thread_cond_any=0; 59 | self->set_clear_mutex.unlock(); 60 | } 61 | }; 62 | 63 | custom_lock cl(this,cv,lk); 64 | interruption_point(); 65 | cv.wait(cl); 66 | interruption_point(); 67 | } 68 | 69 | // rest as before 70 | }; 71 | 72 | template 73 | void interruptible_wait(std::condition_variable_any& cv, 74 | Lockable& lk) 75 | { 76 | this_thread_interrupt_flag.wait(cv,lk); 77 | } 78 | -------------------------------------------------------------------------------- /src/ch09/listing_9.13.cpp: -------------------------------------------------------------------------------- 1 | std::mutex config_mutex; 2 | std::vector background_threads; 3 | 4 | void background_thread(int disk_id) 5 | { 6 | while(true) 7 | { 8 | interruption_point(); 9 | fs_change fsc=get_fs_changes(disk_id); 10 | if(fsc.has_changes()) 11 | { 12 | update_index(fsc); 13 | } 14 | } 15 | } 16 | 17 | void start_background_processing() 18 | { 19 | background_threads.push_back( 20 | interruptible_thread(background_thread,disk_1)); 21 | background_threads.push_back( 22 | interruptible_thread(background_thread,disk_2)); 23 | } 24 | 25 | int main() 26 | { 27 | start_background_processing(); 28 | process_gui_until_exit(); 29 | std::unique_lock lk(config_mutex); 30 | for(unsigned i=0;i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class function_wrapper 9 | { 10 | struct impl_base { 11 | virtual void call()=0; 12 | virtual ~impl_base() {} 13 | }; 14 | std::unique_ptr impl; 15 | template 16 | struct impl_type: impl_base 17 | { 18 | F f; 19 | impl_type(F&& f_): f(std::move(f_)) {} 20 | void call() { f(); } 21 | }; 22 | public: 23 | template 24 | function_wrapper(F&& f): 25 | impl(new impl_type(std::move(f))) 26 | {} 27 | 28 | void call() { impl->call(); } 29 | 30 | function_wrapper(function_wrapper&& other): 31 | impl(std::move(other.impl)) 32 | {} 33 | 34 | function_wrapper& operator=(function_wrapper&& other) 35 | { 36 | impl=std::move(other.impl); 37 | return *this; 38 | } 39 | 40 | function_wrapper(const function_wrapper&)=delete; 41 | function_wrapper(function_wrapper&)=delete; 42 | function_wrapper& operator=(const function_wrapper&)=delete; 43 | }; 44 | 45 | class thread_pool 46 | { 47 | public: 48 | std::deque work_queue; 49 | 50 | template 51 | std::future::type> 52 | submit(FunctionType f) 53 | { 54 | typedef typename std::result_of::type result_type; 55 | 56 | std::packaged_task task(std::move(f)); 57 | std::future res(task.get_future()); 58 | work_queue.push_back(std::move(task)); 59 | return res; 60 | } 61 | // rest as before 62 | }; 63 | -------------------------------------------------------------------------------- /src/ch09/listing_9.3.cpp: -------------------------------------------------------------------------------- 1 | template 2 | T parallel_accumulate(Iterator first,Iterator last,T init) 3 | { 4 | unsigned long const length=std::distance(first,last); 5 | 6 | if(!length) 7 | return init; 8 | 9 | unsigned long const block_size=25; 10 | unsigned long const num_blocks=(length+block_size-1)/block_size; 11 | 12 | std::vector > futures(num_blocks-1); 13 | thread_pool pool; 14 | 15 | Iterator block_start=first; 16 | for(unsigned long i=0;i<(num_threads-1);++i) 17 | { 18 | Iterator block_end=block_start; 19 | std::advance(block_end,block_size); 20 | futures[i]=pool.submit(accumulate_block()); 21 | block_start=block_end; 22 | } 23 | T last_result=accumulate_block()(block_start,last); 24 | T result=init; 25 | for(unsigned long i=0;i<(num_blocks-1);++i) 26 | { 27 | result+=futures[i].get(); 28 | } 29 | result += last_result; 30 | return result; 31 | } 32 | -------------------------------------------------------------------------------- /src/ch09/listing_9.4.cpp: -------------------------------------------------------------------------------- 1 | void thread_pool::run_pending_task() 2 | { 3 | function_wrapper task; 4 | if(work_queue.try_pop(task)) 5 | { 6 | task(); 7 | } 8 | else 9 | { 10 | std::this_thread::yield(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/ch09/listing_9.5.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct sorter 3 | { 4 | thread_pool pool; 5 | 6 | std::list do_sort(std::list& chunk_data) 7 | { 8 | if(chunk_data.empty()) 9 | { 10 | return chunk_data; 11 | } 12 | 13 | std::list result; 14 | result.splice(result.begin(),chunk_data,chunk_data.begin()); 15 | T const& partition_val=*result.begin(); 16 | 17 | typename std::list::iterator divide_point= 18 | std::partition( 19 | chunk_data.begin(),chunk_data.end(), 20 | [&](T const& val){return val new_lower_chunk; 23 | new_lower_chunk.splice( 24 | new_lower_chunk.end(), 25 | chunk_data,chunk_data.begin(), 26 | divide_point); 27 | 28 | thread_pool::task_handle > new_lower= 29 | pool.submit( 30 | std::bind( 31 | &sorter::do_sort,this, 32 | std::move(new_lower_chunk))); 33 | 34 | std::list new_higher(do_sort(chunk_data)); 35 | 36 | result.splice(result.end(),new_higher); 37 | while(!new_lower.is_ready()) 38 | { 39 | pool.run_pending_task(); 40 | } 41 | 42 | result.splice(result.begin(),new_lower.get()); 43 | return result; 44 | } 45 | }; 46 | 47 | 48 | template 49 | std::list parallel_quick_sort(std::list input) 50 | { 51 | if(input.empty()) 52 | { 53 | return input; 54 | } 55 | sorter s; 56 | 57 | return s.do_sort(input); 58 | } 59 | -------------------------------------------------------------------------------- /src/ch09/listing_9.6.cpp: -------------------------------------------------------------------------------- 1 | class thread_pool 2 | { 3 | thread_safe_queue pool_work_queue; 4 | 5 | typedef std::queue local_queue_type; 6 | static thread_local std::unique_ptr 7 | local_work_queue; 8 | 9 | void worker_thread() 10 | { 11 | local_work_queue.reset(new local_queue_type); 12 | 13 | while(!done) 14 | { 15 | run_pending_task(); 16 | } 17 | } 18 | 19 | public: 20 | template 21 | std::future::type> 22 | submit(FunctionType f) 23 | { 24 | typedef std::result_of::type result_type; 25 | 26 | std::packaged_task task(f); 27 | std::future res(task.get_future()); 28 | if(local_work_queue) 29 | { 30 | local_work_queue->push(std::move(task)); 31 | } 32 | else 33 | { 34 | pool_work_queue.push(std::move(task)); 35 | } 36 | return res; 37 | } 38 | 39 | void run_pending_task() 40 | { 41 | function_wrapper task; 42 | if(local_work_queue && !local_work_queue->empty()) 43 | { 44 | task=std::move(local_work_queue->front()); 45 | local_work_queue->pop(); 46 | task(); 47 | } 48 | else if(pool_work_queue.try_pop(task)) 49 | { 50 | task(); 51 | } 52 | else 53 | { 54 | std::this_thread::yield(); 55 | } 56 | } 57 | // rest as before 58 | }; 59 | -------------------------------------------------------------------------------- /src/ch09/listing_9.7.cpp: -------------------------------------------------------------------------------- 1 | class work_stealing_queue 2 | { 3 | private: 4 | typedef function_wrapper data_type; 5 | std::deque the_queue; 6 | mutable std::mutex the_mutex; 7 | 8 | public: 9 | work_stealing_queue() 10 | {} 11 | 12 | work_stealing_queue(const work_stealing_queue& other)=delete; 13 | work_stealing_queue& operator=( 14 | const work_stealing_queue& other)=delete; 15 | 16 | void push(data_type data) 17 | { 18 | std::lock_guard lock(the_mutex); 19 | the_queue.push_front(std::move(data)); 20 | } 21 | 22 | bool empty() const 23 | { 24 | std::lock_guard lock(the_mutex); 25 | return the_queue.empty(); 26 | } 27 | 28 | bool try_pop(data_type& res) 29 | { 30 | std::lock_guard lock(the_mutex); 31 | if(the_queue.empty()) 32 | { 33 | return false; 34 | } 35 | 36 | res=std::move(the_queue.front()); 37 | the_queue.pop_front(); 38 | return true; 39 | } 40 | 41 | bool try_steal(data_type& res) 42 | { 43 | std::lock_guard lock(the_mutex); 44 | if(the_queue.empty()) 45 | { 46 | return false; 47 | } 48 | 49 | res=std::move(the_queue.back()); 50 | the_queue.pop_back(); 51 | return true; 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/ch09/listing_9.8.cpp: -------------------------------------------------------------------------------- 1 | class thread_pool 2 | { 3 | typedef function_wrapper task_type; 4 | 5 | std::atomic_bool done; 6 | thread_safe_queue pool_work_queue; 7 | std::vector > queues; 8 | std::vector threads; 9 | join_threads joiner; 10 | 11 | static thread_local work_stealing_queue* local_work_queue; 12 | static thread_local unsigned my_index; 13 | 14 | void worker_thread(unsigned my_index_) 15 | { 16 | my_index=my_index_; 17 | local_work_queue=queues[my_index].get(); 18 | while(!done) 19 | { 20 | run_pending_task(); 21 | } 22 | } 23 | 24 | bool pop_task_from_local_queue(task_type& task) 25 | { 26 | return local_work_queue && local_work_queue->try_pop(task); 27 | } 28 | 29 | bool pop_task_from_pool_queue(task_type& task) 30 | { 31 | return pool_work_queue.try_pop(task); 32 | } 33 | 34 | bool pop_task_from_other_thread_queue(task_type& task) 35 | { 36 | for(unsigned i=0;itry_steal(task)) 40 | { 41 | return true; 42 | } 43 | } 44 | 45 | return false; 46 | } 47 | 48 | public: 49 | thread_pool(): 50 | joiner(threads),done(false) 51 | { 52 | unsigned const thread_count=std::thread::hardware_concurrency(); 53 | 54 | try 55 | { 56 | for(unsigned i=0;i( 59 | new work_stealing_queue)); 60 | threads.push_back( 61 | std::thread(&thread_pool::worker_thread,this,i)); 62 | } 63 | } 64 | catch(...) 65 | { 66 | done=true; 67 | throw; 68 | } 69 | } 70 | 71 | ~thread_pool() 72 | { 73 | done=true; 74 | } 75 | 76 | template 77 | using task_handle=std::unique_future; 78 | 79 | template 80 | task_handle::type> submit( 81 | FunctionType f) 82 | { 83 | typedef std::result_of::type result_type; 84 | 85 | std::packaged_task task(f); 86 | task_handle res(task.get_future()); 87 | if(local_work_queue) 88 | { 89 | local_work_queue->push(std::move(task)); 90 | } 91 | else 92 | { 93 | pool_work_queue.push(std::move(task)); 94 | } 95 | return res; 96 | } 97 | 98 | void run_pending_task() 99 | { 100 | task_type task; 101 | if(pop_task_from_local_queue(task) || 102 | pop_task_from_pool_queue(task) || 103 | pop_task_from_other_thread_queue(task)) 104 | { 105 | task(); 106 | } 107 | else 108 | { 109 | std::this_thread::yield(); 110 | } 111 | } 112 | }; 113 | -------------------------------------------------------------------------------- /src/ch09/listing_9.9.cpp: -------------------------------------------------------------------------------- 1 | class interrupt_flag 2 | { 3 | public: 4 | void set(); 5 | bool is_set() const; 6 | }; 7 | thread_local interrupt_flag this_thread_interrupt_flag; 8 | 9 | class interruptible_thread 10 | { 11 | std::thread internal_thread; 12 | interrupt_flag* flag; 13 | public: 14 | template 15 | interruptible_thread(FunctionType f) 16 | { 17 | std::promise p; 18 | internal_thread=std::thread([f,&p]{ 19 | p.set_value(&this_thread_interrupt_flag); 20 | f(); 21 | }); 22 | flag=p.get_future().get(); 23 | } 24 | void interrupt() 25 | { 26 | if(flag) 27 | { 28 | flag->set(); 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/ch10/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # add_executable(listing_10.1 listing_10.1.cpp) 2 | # target_link_libraries(listing_10.1 pthread) -------------------------------------------------------------------------------- /src/ch10/listing_10.1.cpp: -------------------------------------------------------------------------------- 1 | void test_concurrent_push_and_pop_on_empty_queue() 2 | { 3 | threadsafe_queue q; 4 | 5 | std::promise go,push_ready,pop_ready; 6 | std::shared_future ready(go.get_future()); 7 | 8 | std::future push_done; 9 | std::future pop_done; 10 | 11 | try 12 | { 13 | push_done=std::async(std::launch::async, 14 | [&q,ready,&push_ready]() 15 | { 16 | push_ready.set_value(); 17 | ready.wait(); 18 | q.push(42); 19 | } 20 | ); 21 | pop_done=std::async(std::launch::async, 22 | [&q,ready,&pop_ready]() 23 | { 24 | pop_ready.set_value(); 25 | ready.wait(); 26 | return q.pop(); 27 | } 28 | ); 29 | push_ready.get_future().wait(); 30 | pop_ready.get_future().wait(); 31 | go.set_value(); 32 | 33 | push_done.get(); 34 | assert(pop_done.get()==42); 35 | assert(q.empty()); 36 | } 37 | catch(...) 38 | { 39 | go.set_value(); 40 | throw; 41 | } 42 | } 43 | --------------------------------------------------------------------------------