├── Chapter 3 ├── dir_file_rename.cpp ├── fifo_check.cpp ├── create_exemplary_links.cpp ├── dir_symlink.cpp ├── dir_creator.cpp ├── dir_iterator.cpp ├── dir_file_removal.cpp ├── anonymous_pipe_sigpipe_example.c ├── anonymous_pipe_example.cpp ├── named_pipe_example.c └── cpp_and_named_pipe.cpp ├── Chapter 8 ├── invalid-date.cpp ├── system-clock-epoch.cpp ├── get-time-zones.cpp ├── clocks.cpp ├── time-epoch.cpp ├── get-date-time.cpp ├── tai-to-utc.cpp ├── operator-slash.cpp ├── round-timepoint.cpp ├── leap-year.cpp ├── duration-cast.cpp ├── posix-measure-time.cpp ├── chrono-measure-time.cpp ├── duration-test.cpp ├── posix-alarm.cpp └── find-time-zones.cpp ├── Chapter 2 ├── exec_example.cpp ├── fork_example.cpp ├── exec_and_exit_example.cpp ├── waitpid_example.cpp ├── waitid_example.cpp └── thread_creation_example.cpp ├── Chapter 5 ├── leak.cpp ├── rethrow.cpp ├── posix-file.cpp ├── error-code.cpp ├── error-condition.cpp ├── system-error.cpp ├── raii.cpp └── custom-error-category.cpp ├── Chapter 7 ├── cpp_increment_atomic.cpp ├── cpp_mutex.cpp ├── cpp_atomic_flags.cpp ├── cpp_sem_example.cpp ├── posix_semaphore_example.c ├── tcp_client.cpp ├── tcp_server.cpp ├── mq_example.cpp ├── shm_example.cpp └── udp_example.cpp ├── Chapter 4 ├── storage_duration.cpp ├── init2.cpp ├── lambdas.cpp ├── init.cpp ├── sizeof.cpp └── mean_value.cpp ├── Chapter 6 ├── future-promise.cpp ├── hello-jthreads.cpp ├── stop-source.cpp ├── stop-token.cpp ├── latch.cpp ├── data-race.cpp └── barrier-temperature.cpp ├── Chapter 9 ├── condition_var.cpp ├── cycle_break.cpp ├── smart_pointers.cpp ├── lazy_init.cpp ├── coop_cancellation.cpp ├── hardware_interference_example.cpp ├── ticket_lock.cpp ├── read_write_lock.cpp └── shared_mem_sp.cpp ├── Chapter 10 ├── coroutines.cpp ├── generator_example.cpp ├── shmem_coroutines.cpp └── coroutine_socket.cpp ├── LICENSE └── README.md /Chapter 3/dir_file_rename.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | using namespace std::filesystem; 6 | 7 | int main() { 8 | if (exists("some_data_sl")) { 9 | rename("some_data_sl", "some_data_sl_rndm"); 10 | } 11 | return 0; 12 | } -------------------------------------------------------------------------------- /Chapter 3/fifo_check.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | using namespace std::filesystem; 6 | 7 | int main() { 8 | if (exists("example_fifo") && is_fifo("example_fifo")) { 9 | remove("example_fifo"); 10 | cout << "FIFO is removed"; 11 | } 12 | 13 | return 0; 14 | } -------------------------------------------------------------------------------- /Chapter 8/invalid-date.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void invalid_date() { 7 | std::cout << "Year: " << year{2023} ; 8 | std::cout << ", Month: " << month{13}; 9 | std::cout << ", Day: " << day{32} << '\n'; 10 | } 11 | 12 | int main() { 13 | invalid_date(); 14 | return 0; 15 | } -------------------------------------------------------------------------------- /Chapter 2/exec_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | void process_creator() { 7 | 8 | if (execvp("./test_fork", NULL) == -1) 9 | cout << "Process creation failed!" << endl; 10 | else 11 | cout << "Process called!" << endl; 12 | } 13 | 14 | int main() { 15 | process_creator(); 16 | return 0; 17 | } -------------------------------------------------------------------------------- /Chapter 8/system-clock-epoch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void system_clock_epoch() { 7 | time_point systemClockEpoch; 8 | std::cout << std::format("system_clock epoch: {0:%F}T{0:%R%z}.", systemClockEpoch) << '\n'; 9 | } 10 | 11 | int main() { 12 | system_clock_epoch(); 13 | return 0; 14 | } -------------------------------------------------------------------------------- /Chapter 3/create_exemplary_links.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | using namespace std::filesystem; 6 | 7 | int main() { 8 | const path path_to_iterate{"test_dir"}; 9 | if (exists("some_data")) { 10 | create_hard_link("some_data", "some_data_hl"); 11 | create_symlink("some_data", "some_data_sl"); 12 | } 13 | return 0; 14 | } -------------------------------------------------------------------------------- /Chapter 8/get-time-zones.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void get_time_zones() { 7 | const tzdb& tzdb{get_tzdb()}; 8 | const std::vector& tzs{tzdb.zones}; 9 | for (const time_zone& tz : tzs) { 10 | std::cout << tz.name() << '\n'; 11 | } 12 | } 13 | 14 | int main() { 15 | get_time_zones(); 16 | return 0; 17 | } -------------------------------------------------------------------------------- /Chapter 8/clocks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void clocks() { 7 | tai_time tai{tai_clock::now()}; 8 | utc_time utc{utc_clock::now()}; 9 | std::cout << "International atomic time (TAI): " << tai << '\n'; 10 | std::cout << "Coordinated universal time (UTC): " << utc << '\n'; 11 | } 12 | 13 | int main() { 14 | clocks(); 15 | return 0; 16 | } -------------------------------------------------------------------------------- /Chapter 8/time-epoch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void time_epoch() { 7 | const time_point start{steady_clock::now()}; // {1} 8 | const duration epoch_to_start{start.time_since_epoch()}; // {2} 9 | std::cout << "Time since clock epoch: " << epoch_to_start << '\n'; // {3} 10 | } 11 | 12 | int main() { 13 | time_epoch(); 14 | return 0; 15 | } -------------------------------------------------------------------------------- /Chapter 8/get-date-time.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void get_date_time() { 7 | year this_year{2023}; 8 | month this_month{8}; 9 | day this_day{4}; 10 | std::cout << "Year: " << this_year ; 11 | std::cout << ", Month: " << this_month; 12 | std::cout << ", Day: " << this_day << '\n'; 13 | } 14 | 15 | int main() { 16 | get_date_time(); 17 | return 0; 18 | } -------------------------------------------------------------------------------- /Chapter 8/tai-to-utc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void tai_to_utc() { 7 | tai_time tai{tai_clock::now()}; 8 | std::cout << "International atomic time (TAI): " << tai << '\n'; 9 | utc_time utc{clock_cast(tai)}; 10 | std::cout << "Coordinated universal time (UTC): " << utc << '\n'; 11 | } 12 | 13 | int main() { 14 | tai_to_utc(); 15 | return 0; 16 | } -------------------------------------------------------------------------------- /Chapter 2/fork_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | void process_creator() { 6 | 7 | if (fork() == 0) { 8 | cout << "Child process id: " << getpid() << endl; 9 | exit(EXIT_SUCCESS); 10 | } 11 | else { 12 | cout << "Parent process id: " << getpid() << endl; 13 | } 14 | } 15 | 16 | int main() { 17 | process_creator(); 18 | return 0; 19 | } -------------------------------------------------------------------------------- /Chapter 2/exec_and_exit_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | void process_creator() { 6 | 7 | if (fork() == 0) { 8 | cout << "Child process id: " << getpid() << endl; 9 | exit(EXIT_SUCCESS); 10 | } 11 | else { 12 | cout << "Parent process id: " << getpid() << endl; 13 | } 14 | } 15 | 16 | int main() { 17 | process_creator(); 18 | return 0; 19 | } -------------------------------------------------------------------------------- /Chapter 3/dir_symlink.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | using namespace std::filesystem; 6 | 7 | int main() { 8 | const path path_to_iterate{"test_dir"}; 9 | for (auto const& dir_entry : 10 | recursive_directory_iterator{path_to_iterate}) { 11 | auto result = is_symlink(dir_entry.path()); 12 | if (result) cout << read_symlink(dir_entry.path()); 13 | } 14 | return 0; 15 | } -------------------------------------------------------------------------------- /Chapter 3/dir_creator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | using namespace std::filesystem; 6 | 7 | int main() { 8 | error_code ec; 9 | auto result = create_directory("test_dir", ec); 10 | if (result) 11 | cout << "Directory created successfuly!\n"; 12 | else { 13 | cout << "Directory creation failed!\n"; 14 | cout << ec.category().name() << endl; 15 | } 16 | 17 | return 0; 18 | } -------------------------------------------------------------------------------- /Chapter 8/operator-slash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void operator_slash() { 7 | year_month_day date1{July/5d/2023y}; 8 | year_month_day date2{1d/October/2023y}; 9 | year_month_day date3{2023y/January/27d}; 10 | std::cout << date1 << '\n'; 11 | std::cout << date2 << '\n'; 12 | std::cout << date3 << '\n'; 13 | } 14 | 15 | int main() { 16 | operator_slash(); 17 | return 0; 18 | } -------------------------------------------------------------------------------- /Chapter 5/leak.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | try { 7 | const int fd{open("/tmp/cpp-test-file", O_RDONLY)}; // {1} 8 | if (fd == -1) { return errno; } 9 | // Do something with the file and suddenly something throws {2} 10 | if (close(fd) == -1) { return errno; } // {3} 11 | } catch (...) { 12 | std::cerr << "Something somewhere went terribly wrong!\n"; 13 | return -1; 14 | } 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /Chapter 8/round-timepoint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void round_time_point() { 7 | seconds dur_sec_1{55s}; //{1} 8 | seconds dur_sec_2{65s}; //{2} 9 | minutes dur_min_1{round(dur_sec_1)}; //{3} 10 | minutes dur_min_2{round(dur_sec_2)}; //{4} 11 | std::cout << "Rounding up to " << dur_min_1 << '\n'; 12 | std::cout << "Rounding down to " << dur_min_2 << '\n'; 13 | } 14 | 15 | int main() { 16 | round_time_point(); 17 | return 0; 18 | } -------------------------------------------------------------------------------- /Chapter 7/cpp_increment_atomic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | atomic shared_resource = 0; 8 | constexpr uint32_t limit = INT_MAX; 9 | 10 | void increment() { 11 | for (auto i = 0; i < limit; i++) { 12 | ++shared_resource; 13 | } 14 | cout << "\nIncrement finished!" << endl; 15 | } 16 | 17 | int main() { 18 | std::thread t1(increment); 19 | std::thread t2(increment); 20 | 21 | t1.join(); 22 | t2.join(); 23 | } 24 | -------------------------------------------------------------------------------- /Chapter 3/dir_iterator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | using namespace std::filesystem; 6 | 7 | int main() { 8 | const path path_to_iterate{"test_dir"}; 9 | for (auto const& dir_entry : 10 | directory_iterator{path_to_iterate}) { 11 | cout << dir_entry.path() << endl; 12 | } 13 | 14 | cout << endl; 15 | 16 | for (auto const& dir_entry : 17 | recursive_directory_iterator{path_to_iterate}) { 18 | cout << dir_entry.path() << endl; 19 | } 20 | return 0; 21 | } -------------------------------------------------------------------------------- /Chapter 8/leap-year.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void leap_year(){ 7 | sys_time now{system_clock::now()}; 8 | year_month_day today{floor(now)}; 9 | std::cout << "Today is: " << today << '\n'; 10 | year thisYear{today.year()}; 11 | std::cout << "Year " << thisYear; 12 | if (thisYear.is_leap()) { 13 | std::cout << " is a leap year\n"; 14 | } else { 15 | std::cout << " is not a leap year\n"; 16 | } 17 | } 18 | 19 | int main() { 20 | leap_year(); 21 | return 0; 22 | } -------------------------------------------------------------------------------- /Chapter 4/storage_duration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int global_var = 1; // Static storage duration 4 | 5 | void foo() { 6 | int automatic_var = 2; 7 | static int static_var = 3; 8 | int* dynamic_var = new int(4); 9 | std::cout << "Automatic var: " << automatic_var << '\n'; 10 | std::cout << "Static var: " << static_var << '\n'; 11 | std::cout << "Dynamic var: " << *dynamic_var << '\n'; 12 | delete dynamic_var; 13 | } 14 | 15 | int main() { 16 | foo(); 17 | std::cout << "Global var: " << global_var << '\n'; 18 | return 0; 19 | } -------------------------------------------------------------------------------- /Chapter 7/cpp_mutex.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | uint32_t shared_resource = 0; 8 | mutex shres_guard; 9 | 10 | constexpr uint32_t limit = INT_MAX; 11 | 12 | void increment() { 13 | for (auto i = 0; i < limit; i++) { 14 | lock_guard lock(shres_guard); 15 | ++shared_resource; 16 | } 17 | cout << "\nIncrement finished!" << endl; 18 | } 19 | 20 | int main() { 21 | jthread t1(increment); 22 | jthread t2(increment); 23 | 24 | t1.join(); 25 | t2.join(); 26 | } 27 | -------------------------------------------------------------------------------- /Chapter 8/duration-cast.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void duration_cast() { 7 | // const minutes dur_minutes{steady_clock::now().time_since_epoch()}; // doesn't compile 8 | auto dur_from_epoch{steady_clock::now().time_since_epoch()}; // {1} 9 | const minutes dur_minutes{duration_cast(dur_from_epoch)}; // {2} 10 | std::cout << "Duration in nanoseconds: " << dur_from_epoch << '\n'; //{3} 11 | std::cout << "Duration in minutes: " << dur_minutes << '\n'; //{4} 12 | } 13 | 14 | int main() { 15 | duration_cast(); 16 | return 0; 17 | } -------------------------------------------------------------------------------- /Chapter 5/rethrow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Throw() { 4 | using namespace std; 5 | throw system_error{make_error_code(errc::bad_file_descriptor)}; 6 | } 7 | 8 | int main() { 9 | using namespace std; 10 | try { 11 | try { 12 | Throw(); // {1} 13 | } catch (runtime_error e) { // {2} 14 | throw e; // {3} 15 | } 16 | } catch (const exception& e) { // {4} 17 | const system_error& se{dynamic_cast(e)}; // {5} 18 | const auto econd{se.code().default_error_condition()}; 19 | std::cerr << econd.message() << '\n'; 20 | } 21 | return 0; 22 | } -------------------------------------------------------------------------------- /Chapter 5/posix-file.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | const int fd{open("no-such-file.txt", O_RDONLY)}; // {1} 8 | if (fd == -1) { 9 | std::cerr << "Error opening file: " << strerror(errno) << '\n'; 10 | std::cerr << "Error code: " << errno << '\n'; 11 | return EXIT_FAILURE; 12 | } 13 | // Do something with the file... 14 | if (close(fd) == -1) { 15 | std::cerr << "Error closing file: " << strerror(errno) << '\n'; 16 | std::cerr << "Error code: " << errno << '\n'; 17 | return EXIT_FAILURE; 18 | } 19 | return 0; 20 | } -------------------------------------------------------------------------------- /Chapter 8/posix-measure-time.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static const auto LIMIT{10000}; 5 | 6 | void dummy_f() { 7 | for (auto i{0}; i < LIMIT; ++i); 8 | } 9 | int main() { 10 | timespec start, end; 11 | // Start the timer 12 | clock_gettime(CLOCK_MONOTONIC, &start); 13 | // Measured code segment 14 | for (auto i{0}; i < LIMIT; ++i) { 15 | dummy_f(); 16 | } 17 | // Stop the timer 18 | clock_gettime(CLOCK_MONOTONIC, &end); 19 | // Calculate the elapsed time 20 | const auto elapsed{(end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9}; 21 | std::cout << "Elapsed time: " << elapsed << " seconds\n"; 22 | return 0; 23 | } -------------------------------------------------------------------------------- /Chapter 6/future-promise.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std::literals::chrono_literals; 6 | 7 | int main() { 8 | std::promise promise; 9 | std::future future{promise.get_future()}; // Get the future from the promise. 10 | 11 | std::jthread th1{[p{std::move(promise)}]() mutable { 12 | std::this_thread::sleep_for(20ms); 13 | p.set_value_at_thread_exit("I promised to call you back once I am ready!\n"); 14 | }}; // Move the promise inside the worker thread. 15 | 16 | std::cout << "Main thread is ready.\n"; 17 | std::cout << future.get(); // This is a blocking call! 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /Chapter 8/chrono-measure-time.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static const auto LIMIT{10000}; 6 | void just_busy_wait_f() { 7 | for (auto i{0}; i < LIMIT; ++i) { 8 | for (auto j{0}; j < LIMIT; ++j); 9 | } 10 | } 11 | 12 | using namespace std::chrono; 13 | 14 | void first_duration() { 15 | const auto start{steady_clock::now()}; // {1} 16 | just_busy_wait_f(); // {2} 17 | const auto end{steady_clock::now()}; // {3} 18 | const auto duration{duration_cast(end - start)}; // {4} 19 | std::cout << "Execution time: " << duration.count() << " milliseconds\n"; // {5} 20 | } 21 | 22 | int main() { 23 | first_duration(); 24 | return 0; 25 | } -------------------------------------------------------------------------------- /Chapter 9/condition_var.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | mutex cv_mutex; 8 | condition_variable cond_var; 9 | 10 | void processing() { 11 | cout << "Processing shared resource." << endl; 12 | } 13 | 14 | void waiting() { 15 | cout << "Waiting for work..." << endl; 16 | 17 | unique_lock lock(cv_mutex); 18 | cond_var.wait(lock); 19 | processing(); 20 | cout << "Work done." << endl; 21 | } 22 | 23 | void done() { 24 | cout << "Shared resource ready." << endl; 25 | cond_var.notify_one(); 26 | } 27 | 28 | int main () { 29 | jthread t1(waiting); 30 | jthread t2(done); 31 | 32 | t1.join(); 33 | t2.join(); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /Chapter 8/duration-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void duration_test() { 7 | constexpr std::chrono::duration> six_minutes_1{360}; 8 | constexpr std::chrono::duration> six_minutes_2{0.1}; 9 | constexpr std::chrono::minutes six_minutes_3{6}; 10 | constexpr auto six_minutes_4{6min}; 11 | std::cout << six_minutes_1 << '\n'; 12 | std::cout << six_minutes_2 << '\n'; 13 | std::cout << six_minutes_3 << '\n'; 14 | std::cout << six_minutes_4 << '\n'; 15 | static_assert(six_minutes_1 == six_minutes_2); 16 | static_assert(six_minutes_2 == six_minutes_3); 17 | static_assert(six_minutes_3 == six_minutes_4); 18 | } 19 | 20 | int main() { 21 | duration_test(); 22 | return 0; 23 | } -------------------------------------------------------------------------------- /Chapter 5/error-code.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | std::error_code CreateDirectory(const std::string& dirPath) { 5 | std::error_code ecode{}; 6 | if (mkdir(dirPath.c_str(), 0777) != 0) { 7 | ecode = std::error_code{errno, std::generic_category()}; // {1} 8 | } 9 | 10 | return ecode; 11 | } 12 | 13 | int main() { 14 | auto ecode{CreateDirectory("/tmp/test")}; 15 | if (ecode){ // {2} 16 | std::cerr << "Error 1: " << ecode.message() << '\n'; 17 | } 18 | 19 | ecode = CreateDirectory("/tmp/test"); // {3} 20 | if (ecode){ 21 | std::cerr << "Error 2: " << ecode.message() << '\n'; 22 | } 23 | 24 | if (ecode.value() == EEXIST) { // {4} 25 | std::cout << "This is platform specific and not portable.\n"; 26 | } 27 | return 0; 28 | } -------------------------------------------------------------------------------- /Chapter 5/error-condition.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | std::error_code CreateDirectory(const std::string& dirPath) { 6 | std::error_code ecode{}; 7 | if (mkdir(dirPath.c_str(), 0777) != 0) { 8 | std::errc cond{errno}; // {1} 9 | ecode = std::make_error_code(cond); // {2} 10 | } 11 | 12 | return ecode; 13 | } 14 | 15 | int main() { 16 | auto ecode{CreateDirectory("/tmp/test")}; 17 | if (ecode){ 18 | std::cerr << "Error 1: " << ecode.message() << '\n'; 19 | } 20 | 21 | ecode = CreateDirectory("/tmp/test"); 22 | if (ecode){ 23 | std::cerr << "Error 2: " << ecode.message() << '\n'; 24 | } 25 | 26 | if (ecode == std::errc::file_exists) { // {3} 27 | std::cout << "This is platform agnostic and is portable.\n"; 28 | } 29 | return 0; 30 | } -------------------------------------------------------------------------------- /Chapter 9/cycle_break.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | using std::shared_ptr; 6 | using std::weak_ptr; 7 | 8 | struct Book { 9 | string_view title; 10 | }; 11 | 12 | struct ListNode { 13 | Book data; 14 | ListNode(string_view p_title) { 15 | data.title = p_title; 16 | cout << "Node created: " << data.title 17 | << endl; 18 | } 19 | ~ListNode() { 20 | cout << "Node destroyed: " << data.title 21 | << endl; 22 | } 23 | shared_ptr next; 24 | weak_ptr prev; 25 | }; 26 | 27 | int main() { 28 | shared_ptr head = 29 | make_shared("Dune"); 30 | head->next = make_shared("Jaws"); 31 | if (!head->next->prev.expired()) 32 | head->next->prev = head; 33 | return 0; 34 | } -------------------------------------------------------------------------------- /Chapter 3/dir_file_removal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | using namespace std::filesystem; 6 | 7 | int main() { 8 | if (exists("some_data")) { 9 | std::filesystem::space_info space_obj = 10 | space(current_path()); 11 | cout << "Capacity: " 12 | << space_obj.capacity << endl; 13 | cout << "Free: " 14 | << space_obj.free << endl; 15 | cout << "Available: " 16 | << space_obj.available << endl; 17 | 18 | remove("some_data"); 19 | space_obj = space(current_path()); 20 | 21 | cout << "Capacity: " 22 | << space_obj.capacity << endl; 23 | cout << "Free: " 24 | << space_obj.free << endl; 25 | cout << "Available: " 26 | << space_obj.available << endl; 27 | } 28 | return 0; 29 | } -------------------------------------------------------------------------------- /Chapter 9/smart_pointers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | struct Book { 7 | string_view title; 8 | Book(string_view p_title) : title(p_title) { 9 | cout << "Constructor for: " << title << endl; 10 | } 11 | 12 | ~Book() { 13 | cout << "Destructor for: " << title << endl; 14 | } 15 | }; 16 | 17 | int main() { 18 | unique_ptr book1 = 19 | make_unique("Jaws"); 20 | unique_ptr book1_new; 21 | book1_new = move(book1); 22 | cout << book1_new->title << endl; 23 | 24 | shared_ptr book2 = 25 | make_shared("Dune"); 26 | shared_ptr book2_new; 27 | book2_new = book2; 28 | 29 | cout << book2->title << " " 30 | << book2_new->title 31 | << endl; 32 | cout << book2.use_count() << endl; 33 | 34 | return 0; 35 | } -------------------------------------------------------------------------------- /Chapter 6/hello-jthreads.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | std::array my_threads; // Just an array of 5 jthread objects which do nothing. 8 | 9 | const auto worker{[]{ 10 | const auto thread_id = std::this_thread::get_id(); // 3 11 | std::osyncstream sync_cout{std::cout}; 12 | sync_cout << "Hello from new jthread with id:" << thread_id << '\n'; 13 | }}; 14 | 15 | for (auto& thread : my_threads) { 16 | thread = std::jthread{worker}; // This moves the new jthread on 17 | // the place of the placeholder 18 | } 19 | 20 | std::osyncstream{std::cout} << "Hello Main program thread with id:" 21 | << std::this_thread::get_id() 22 | << '\n'; 23 | return 0; // jthread dtors join them here. 24 | } -------------------------------------------------------------------------------- /Chapter 2/waitpid_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | void process_creator() { 7 | 8 | pid_t pids[2] = {0}; 9 | if ((pids[0] = fork()) == 0) { 10 | cout << "Child process id: " << getpid() << endl; 11 | exit(EXIT_SUCCESS); 12 | } 13 | if ((pids[1] = fork()) == 0) { 14 | cout << "Child process id: " << getpid() << endl; 15 | exit(EXIT_FAILURE); 16 | } 17 | 18 | int status = 0; 19 | waitpid(pids[0], &status, 0); 20 | if (WIFEXITED(status)) 21 | cout << "Child " << pids[0] 22 | << " terminated with: " 23 | << status << endl; 24 | 25 | waitpid(pids[1], &status, 0); 26 | if (WIFEXITED(status)) 27 | cout << "Child " << pids[1] 28 | << " terminated with: " 29 | << status << endl; 30 | } 31 | 32 | int main() { 33 | process_creator(); 34 | return 0; 35 | } -------------------------------------------------------------------------------- /Chapter 5/system-error.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void CreateDirectory(const std::string& dirPath) { 5 | using namespace std; 6 | if (mkdir(dirPath.c_str(), 0777) != 0) { 7 | const auto ecode{make_error_code(errc{errno})}; // {1} 8 | cout << "CreateDirectory reports error: " << ecode.message() << '\n'; 9 | throw system_error{ecode}; // {2} 10 | } 11 | } 12 | 13 | int main() { 14 | try { 15 | CreateDirectory("/tmp/test"); // First try 16 | CreateDirectory("/tmp/test"); // Second try throws 17 | } catch (const std::system_error& se) { // {3} 18 | const auto econd{se.code().default_error_condition()}; // {4} 19 | if (econd != std::errc::file_exists) { // {5} 20 | std::cerr << "Unexpected system error: " << se.what() << '\n'; 21 | throw; // {6} 22 | } 23 | std::cout << "Nothing unexpected, safe to continue.\n"; 24 | } 25 | 26 | return 0; 27 | } -------------------------------------------------------------------------------- /Chapter 10/coroutines.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | struct Task { 6 | struct promise_type { 7 | using Handle = coroutine_handle; 8 | Task get_return_object() { 9 | return Task { Handle::from_promise(*this) }; 10 | } 11 | suspend_always 12 | initial_suspend() { return {}; } 13 | suspend_never 14 | final_suspend() noexcept { return {}; } 15 | void return_void() { } 16 | void unhandled_exception() { } 17 | }; 18 | 19 | explicit Task (promise_type::Handle crtHdnl) : 20 | crtHandle(crtHdnl) {} 21 | void destroy() { crtHandle.destroy(); } 22 | void resume() { crtHandle.resume(); } 23 | 24 | private: 25 | promise_type::Handle crtHandle; 26 | }; 27 | 28 | Task exCoroutine() { 29 | co_return; 30 | } 31 | 32 | int main() { 33 | auto async_task = exCoroutine(); 34 | async_task.resume(); 35 | //async_task.destroy(); 36 | } -------------------------------------------------------------------------------- /Chapter 4/init2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | class Person { 3 | public: 4 | explicit Person(const std::string& the_name) : name{the_name} {} 5 | private: 6 | std::string name; 7 | }; 8 | class Employee { 9 | public: 10 | Employee(const Person& p) : p{p} {} 11 | explicit Employee(const Employee& e) : p{e.p} {} 12 | private: 13 | Person p; 14 | }; 15 | void init() { 16 | int c{2}; 17 | double mm{0.0}; 18 | Person john{"John M."}; 19 | 20 | int d(c); // {1} Copy initialization 21 | int e{d}; // {2} Copy list initialization 22 | int f = e; // {3} Copy initialization 23 | int f1 = {d}; // {4} Copy list initialization 24 | 25 | Employee staff1{john}; // OK 26 | // Employee staff2{std::string{"George"}}; // Error - explicit 27 | Employee staff3{staff1}; // OK 28 | // Employee staff4 = staff1; // Error - explicit copy ctor 29 | // Employee staff5 = {staff1}; // Error - explicit copy ctor 30 | } 31 | 32 | int main() { 33 | init(); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /Chapter 2/waitid_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | void process_creator() { 7 | 8 | pid_t pids[2] = {0}; 9 | if ((pids[0] = fork()) == 0) { 10 | cout << "Child process id: " << getpid() << endl; 11 | exit(EXIT_SUCCESS); 12 | } 13 | if ((pids[1] = fork()) == 0) { 14 | cout << "Child process id: " << getpid() << endl; 15 | abort(); 16 | } 17 | 18 | siginfo_t status = {0}; 19 | waitid(P_PID, pids[1], &status, WEXITED); 20 | if (WIFSIGNALED(status)) 21 | cout << "Child " << pids[1] 22 | << " aborted: " 23 | << "\nStatus update with SIGCHLD: " 24 | << status.si_signo 25 | << "\nTermination signal - SIGABRT: " 26 | << status.si_status 27 | << "\nTermination code - _exit(2): " 28 | << status.si_code << endl; 29 | } 30 | 31 | int main() { 32 | process_creator(); 33 | return 0; 34 | } -------------------------------------------------------------------------------- /Chapter 8/posix-alarm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static std::atomic_bool continue_execution{true}; 8 | 9 | int main() { 10 | struct sigaction sa{}; 11 | sa.sa_handler = [](int signum) { 12 | // Timer triggered, stop the loop. 13 | std::cout << "Timer expired. Stopping the task...\n"; 14 | continue_execution = false; 15 | }; 16 | sigemptyset(&sa.sa_mask); 17 | sa.sa_flags = 0; 18 | sigaction(SIGALRM, &sa, nullptr); 19 | 20 | // Configure the timer to trigger every 1 seconds 21 | struct itimerval timer{ 22 | .it_interval{.tv_sec{1}, .tv_usec{0}}, 23 | .it_value{.tv_sec{1}, .tv_usec{0}} 24 | }; 25 | // Start the timer 26 | setitimer(ITIMER_REAL, &timer, nullptr); 27 | std::cout << "Timer started. Waiting for timer expiration...\n"; 28 | // Keep the program running to allow the timer to trigger 29 | while (continue_execution) { 30 | sleep(1); 31 | } 32 | return 0; 33 | } -------------------------------------------------------------------------------- /Chapter 7/cpp_atomic_flags.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | atomic shared_resource = 0; 8 | atomic_flag condFlag{}; 9 | 10 | constexpr uint16_t limit = 100; 11 | 12 | void increment() { 13 | for (auto i = 0; i < limit; i++) { 14 | condFlag.wait(true); 15 | condFlag.test_and_set(); 16 | ++shared_resource; 17 | cout << shared_resource << " "; 18 | condFlag.notify_one(); 19 | } 20 | cout << "\nIncrement finished!" << endl; 21 | } 22 | 23 | void decrement() { 24 | for (auto i = 0; i < limit; i++) { 25 | condFlag.wait(false); 26 | condFlag.clear(); 27 | --shared_resource; 28 | cout << shared_resource << " "; 29 | condFlag.notify_one(); 30 | } 31 | cout << "\nDecrement finished!" << endl; 32 | } 33 | 34 | int main() { 35 | condFlag.test_and_set(); 36 | std::thread t1(increment); 37 | std::thread t2(decrement); 38 | 39 | t1.join(); 40 | t2.join(); 41 | } 42 | -------------------------------------------------------------------------------- /Chapter 8/find-time-zones.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::chrono; 5 | 6 | void find_time_zones() { 7 | const tzdb& tzdb{get_tzdb()}; 8 | const std::vector& tzs{tzdb.zones}; 9 | const auto& res{std::find_if(tzs.begin(), tzs.end(), [](const time_zone& tz){ 10 | std::string name{tz.name()}; 11 | return name.ends_with("Sofia"); 12 | })}; 13 | 14 | if (res != tzs.end()) { 15 | try { 16 | const std::string_view myLocation{res->name()}; 17 | const std::string_view london{"Europe/London"}; 18 | const time_point now{system_clock::now()}; 19 | const zoned_time zt_1{myLocation, now}; 20 | const zoned_time zt_2{london, now}; 21 | std::cout << myLocation << ": " << zt_1 << '\n'; 22 | std::cout << london << ": " << zt_2 << '\n'; 23 | } catch (const std::runtime_error& e) { 24 | std::cout << e.what() << '\n'; 25 | } 26 | } 27 | } 28 | 29 | int main() { 30 | find_time_zones(); 31 | return 0; 32 | } -------------------------------------------------------------------------------- /Chapter 3/anonymous_pipe_sigpipe_example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define BUFF_LEN ((size_t) 64) 7 | #define pipeIn 0 8 | #define pipeOut 1 9 | 10 | void handle_sigpipe(int sig) { 11 | printf("SIGPIPE handled!\n"); 12 | } 13 | 14 | int main() { 15 | int a_pipe[2] = {0}; 16 | char buff[BUFF_LEN + 1] = {0}; 17 | 18 | if (pipe(a_pipe) == 0) { // {1} 19 | int pid = fork(); 20 | 21 | if (pid == 0) { 22 | close(a_pipe[pipeOut]); // {2} 23 | close(a_pipe[pipeIn]); 24 | } 25 | else { 26 | struct sigaction act = {0}; 27 | sigemptyset(&act.sa_mask); 28 | act.sa_handler = SIG_IGN; // {3} 29 | 30 | if(sigaction(SIGPIPE, &act, 0) == -1) {// {4} 31 | perror("sigaction"); 32 | return (1); 33 | } 34 | close(a_pipe[pipeIn]); 35 | sleep(1); 36 | const char *msg = {"Sending message to child!"}; 37 | write(a_pipe[pipeOut], msg, strlen(msg) + 1); // {5} 38 | } 39 | } 40 | 41 | return 0; 42 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Chapter 3/anonymous_pipe_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | constexpr auto BUFF_LEN = 64; 8 | constexpr auto pipeIn = 0; 9 | constexpr auto pipeOut = 1; 10 | 11 | int main() { 12 | int a_pipe[2]{}; 13 | char buff[BUFF_LEN + 1]{}; 14 | 15 | if (pipe(a_pipe) == -1) { // {1} 16 | perror("Pipe creation failed"); 17 | exit(EXIT_FAILURE); 18 | } 19 | else { 20 | if (int pid = fork(); pid == -1) { 21 | perror("Process creation failed"); 22 | exit(EXIT_FAILURE); 23 | } 24 | else if (pid == 0) { 25 | // Child: will be the reader! 26 | sleep(1); // Just to give some extra time! 27 | close(a_pipe[pipeOut]); // {2} 28 | read(a_pipe[pipeIn], buff, BUFF_LEN); // {3} 29 | cout << "Child: " << buff << endl; 30 | } 31 | else { 32 | // Parent: will be the writer! 33 | close(a_pipe[pipeIn]); // {4} 34 | const char *msg = {"Sending message to child!"}; 35 | write(a_pipe[pipeOut], msg, strlen(msg) + 1); // {5} 36 | } 37 | } 38 | 39 | return 0; 40 | } -------------------------------------------------------------------------------- /Chapter 4/lambdas.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void mean_lambda() { 6 | double mean{}; 7 | std::vector v1{1.0, 2.5, 4.0, 5.5}; 8 | std::string_view text{"calculating ..."}; 9 | std::for_each(v1.begin(), v1.end(), 10 | [&mean, sum{0.0}, count{0}, text](const double& val) mutable { 11 | std::cout << text << '\n'; 12 | sum += val; 13 | ++count; 14 | mean = sum / count; 15 | }); 16 | std::cout << mean << '\n'; 17 | } 18 | 19 | constexpr auto min_lambda = [](const auto& name) -> void { 20 | std::cout << name << " lambda.\n"; 21 | }; 22 | 23 | constexpr auto sum = [](T1 a, T2 b) { 24 | return a*b; 25 | }; 26 | 27 | class platform_specifi_name { 28 | public: 29 | template 30 | constexpr auto operator()(const T1& a, const T2& b) const { 31 | return a*b; 32 | } 33 | }; 34 | 35 | int main() { 36 | min_lambda("Simple"); 37 | mean_lambda(); 38 | constexpr auto val1 = sum(5,3); 39 | constexpr auto val2 = platform_specifi_name{}(5,3); 40 | static_assert(val1 == val2); 41 | return 0; 42 | } -------------------------------------------------------------------------------- /Chapter 4/init.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Point { 4 | double x; 5 | double y; 6 | }; 7 | 8 | struct Person { 9 | std::string name; 10 | int age; 11 | }; 12 | 13 | void init1() { 14 | long a; // {1} 15 | Point p1; // {2} 16 | std::cout << "{1}: " << a << '\n'; 17 | std::cout << "{2}: " << p1.x << ", " << p1.y << '\n'; 18 | } 19 | 20 | void init2() { 21 | int b(1); // {3.1} 22 | int b_trunc(1.2); // {3.2} 23 | int c{2}; // {4.1} 24 | // int c_warn{2.2}; // {4.2} Direct list initialization => narrowing conversion error 25 | 26 | std::cout << "{3.1}: " << b << '\n'; 27 | std::cout << "{3.2}: " << b_trunc << '\n'; 28 | std::cout << "{4.1}: " << c << '\n'; 29 | } 30 | 31 | void init3() { 32 | int zero1{}; // {1} 33 | int zero2 = int(); // {2} 34 | int zero3 = int{}; // {3} 35 | Person nick{"Nick L.", 42}; // {4} 36 | Person john{.name{"John M."}, .age{24}}; // {5} 37 | } 38 | 39 | void init4() { 40 | int c{2}; 41 | 42 | int d(c); // {1} 43 | int e{d}; // {2} 44 | int f = e; // {3} 45 | int f1 = {d}; // {4} 46 | } 47 | 48 | int main() { 49 | init1(); 50 | init2(); 51 | init3(); 52 | init4(); 53 | return 0; 54 | } -------------------------------------------------------------------------------- /Chapter 2/thread_creation_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | using namespace std::chrono; 6 | void detached_routine() { 7 | cout << "Starting detached_routine thread.\n"; 8 | this_thread::sleep_for(seconds(2)); 9 | cout << "Exiting detached_routine thread.\n"; 10 | } 11 | 12 | void joined_routine() { 13 | cout << "Starting joined_routine thread.\n"; 14 | this_thread::sleep_for(seconds(2)); 15 | cout << "Exiting joined_routine thread.\n"; 16 | } 17 | 18 | void thread_creator() { 19 | cout << "Starting thread_creator.\n"; 20 | thread t1(detached_routine); 21 | cout << "Before - Is the detached thread joinable: " 22 | << t1.joinable() << endl; 23 | t1.detach(); 24 | cout << "After - Is the detached thread joinable: " 25 | << t1.joinable() << endl; 26 | thread t2(joined_routine); 27 | cout << "Before - Is the joined thread joinable: " 28 | << t2.joinable() << endl; 29 | t2.join(); 30 | cout << "After - Is the joined thread joinable: " 31 | << t2.joinable() << endl; 32 | this_thread::sleep_for(chrono::seconds(1)); 33 | cout << "Exiting thread_creator.\n"; 34 | } 35 | 36 | int main() { 37 | thread_creator(); 38 | } -------------------------------------------------------------------------------- /Chapter 9/lazy_init.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | using namespace std::literals::chrono_literals; 9 | 10 | struct Settings { 11 | Settings(string_view fileName) { 12 | cout << "Loading settings: " << fileName << endl; 13 | } 14 | ~Settings() { 15 | cout << "Removing settings" << endl; 16 | } 17 | 18 | void setSetting(string_view setting, 19 | string_view value) { 20 | cout << "Set setting: " << setting 21 | << " to: " << value << endl; 22 | } 23 | }; 24 | 25 | struct Config { 26 | optional settings{}; 27 | Config() { 28 | cout << "Config loaded..." << endl; 29 | } 30 | 31 | void changeSetting(string_view setting, 32 | string_view value) { 33 | if (!settings) 34 | settings.emplace("settings.cfg"); 35 | settings->setSetting(setting, value); 36 | } 37 | }; 38 | 39 | int main() { 40 | Config cfg; 41 | cout << "Application startup..." << endl; 42 | this_thread::sleep_for(10s); 43 | cfg.changeSetting("Drive mode", "Sport"); 44 | cfg.changeSetting("Gear label", "PRNDL"); 45 | 46 | return 0; 47 | } -------------------------------------------------------------------------------- /Chapter 7/cpp_sem_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | uint32_t shared_resource = 0; 9 | 10 | binary_semaphore sem_to_produce(0); 11 | binary_semaphore sem_to_consume(0); 12 | 13 | constexpr uint32_t limit = 65536; 14 | 15 | void producer() { 16 | for (auto i = 0; i < limit; i++) { 17 | sem_to_produce.acquire(); 18 | ++shared_resource; 19 | osyncstream(cout) << "Producing value: " 20 | << shared_resource << endl; 21 | sem_to_consume.release(); 22 | osyncstream(cout) << "Producer finished!" << endl; 23 | } 24 | } 25 | 26 | void consumer() { 27 | for (auto i = 0; i <= limit; i++) { 28 | osyncstream(cout) << "Waiting for data..." << endl; 29 | sem_to_consume.acquire(); 30 | --shared_resource; 31 | osyncstream(cout) << "Value after consumer: " << shared_resource << endl; 32 | sem_to_produce.release(); 33 | osyncstream(cout) << "Consumer finished!" << endl; 34 | } 35 | } 36 | 37 | int main() { 38 | sem_to_produce.release(); 39 | jthread t1(producer); 40 | jthread t2(consumer); 41 | 42 | t1.join(); 43 | t2.join(); 44 | } 45 | -------------------------------------------------------------------------------- /Chapter 6/stop-source.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals::chrono_literals; 7 | 8 | int main() { 9 | std::stop_source source; 10 | const auto worker{[](std::stop_source sr, int num){ 11 | std::stop_token token = sr.get_token(); 12 | while (!token.stop_requested()) { 13 | std::osyncstream{std::cout} << "Thread with id " 14 | << num 15 | << " is currently working.\n"; 16 | 17 | std::this_thread::sleep_for(200ms); 18 | } 19 | 20 | std::osyncstream{std::cout} << "Thread with id " 21 | << num 22 | << " is now stopped!\n"; 23 | }}; 24 | 25 | std::array my_threads{ 26 | std::jthread{worker, source, 0}, 27 | std::jthread{worker, source, 1}, 28 | std::jthread{worker, source, 2} 29 | }; 30 | 31 | std::this_thread::sleep_for(1s); 32 | source.request_stop(); // this is not a blocking call, it is just a request. 33 | 34 | std::osyncstream{std::cout} << "Main thread just requested stop!\n"; 35 | 36 | return 0; // jthread dtors join them here. 37 | } -------------------------------------------------------------------------------- /Chapter 4/sizeof.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Empty {}; 4 | struct Padding { 5 | long test; 6 | char m; 7 | }; 8 | struct Virt { 9 | virtual char GetChar() const { return ch; } 10 | char ch; 11 | }; 12 | 13 | void SizeOfStructs() { 14 | std::cout << "Empty: " << sizeof(Empty) << '\n'; 15 | std::cout << "Padding: " << sizeof(Padding) << '\n'; 16 | std::cout << "Virt: " << sizeof(Virt) << '\n'; 17 | } 18 | 19 | void SizeOfPrimitives() { 20 | int i; 21 | long l; 22 | char* p; 23 | std::cout << "sizeof(int) = " << sizeof(int) << "; sizeof(i) = " << sizeof(i) << '\n'; 24 | std::cout << "sizeof(long) = " << sizeof(long) << "; sizeof(l) = " << sizeof(l) << '\n'; 25 | std::cout << "sizeof(char*) = " << sizeof(char*) << "; sizeof(p) = " << sizeof(p) << '\n'; 26 | } 27 | 28 | void SizeOfRefs() { 29 | char c; 30 | char& r_c{c}; 31 | char* p_c; 32 | std::cout << "sizeof(char) = " << sizeof(char) << "; sizeof(c) = " << sizeof(c) << '\n'; 33 | std::cout << "sizeof(char&) = " << sizeof(char&) << "; sizeof(r_c) = " << sizeof(r_c) << '\n'; 34 | std::cout << "sizeof(char*) = " << sizeof(char*) << "; sizeof(p_c) = " << sizeof(p_c) << '\n'; 35 | } 36 | 37 | int main() { 38 | SizeOfPrimitives(); 39 | SizeOfStructs(); 40 | SizeOfRefs(); 41 | return 0; 42 | } -------------------------------------------------------------------------------- /Chapter 9/coop_cancellation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | int main() { 10 | osyncstream{cout} << "Main thread id: " 11 | << this_thread::get_id() 12 | << endl; 13 | jthread worker{[](stop_token stoken) { // 1 14 | mutex mutex; 15 | unique_lock lock(mutex); 16 | condition_variable_any().wait(lock, stoken, 17 | [&stoken] { return stoken.stop_requested(); }); 18 | osyncstream{cout} << "Thread with id " 19 | << this_thread::get_id() 20 | << " is currently working." 21 | << endl; 22 | }}; 23 | 24 | stop_callback callback(worker.get_stop_token(), [] { 25 | osyncstream{cout} << "Stop callback executed by thread: " 26 | << this_thread::get_id() 27 | << endl; 28 | }); 29 | 30 | auto stopper_func = [&worker] { 31 | if (worker.request_stop()) 32 | osyncstream{cout} << "Stop request executed by thread: " 33 | << this_thread::get_id() 34 | << endl; 35 | }; 36 | 37 | jthread stopper(stopper_func); 38 | stopper.join(); 39 | } -------------------------------------------------------------------------------- /Chapter 6/stop-token.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals::chrono_literals; 7 | 8 | int main() { 9 | const auto worker{[](std::stop_token token, int num){ 10 | while (!token.stop_requested()) { 11 | std::osyncstream{std::cout} << "Thread with id " 12 | << num 13 | << " is currently working.\n"; 14 | std::this_thread::sleep_for(200ms); 15 | } 16 | 17 | std::osyncstream{std::cout} << "Thread with id " 18 | << num 19 | << " is now stopped!\n"; 20 | }}; 21 | 22 | std::array my_threads{ 23 | std::jthread{worker, 0}, 24 | std::jthread{worker, 1}, 25 | std::jthread{worker, 2} 26 | }; 27 | 28 | // Give some time to the other threads to start executing ... 29 | std::this_thread::sleep_for(1s); 30 | 31 | // Let's stop them 32 | for (auto& thread : my_threads) { 33 | thread.request_stop(); // this is not a blocking call, 34 | // it is just a request. 35 | } 36 | 37 | std::osyncstream{std::cout} << "Main thread just requested stop!\n"; 38 | 39 | // jthread dtors join them here. 40 | return 0; 41 | } -------------------------------------------------------------------------------- /Chapter 6/latch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std::literals::chrono_literals; 8 | 9 | int main() { 10 | std::latch progress{2}; 11 | std::array threads { 12 | std::jthread{[&](int num){ 13 | std::osyncstream{std::cout} << "Starting thread " 14 | << num 15 | << " and go to sleep.\n"; 16 | std::this_thread::sleep_for(100ms); 17 | std::osyncstream{std::cout} << "Decrementing the latch for thread " 18 | << num << '\n'; 19 | progress.count_down(); 20 | std::osyncstream{std::cout} << "Thread " << num 21 | << " finished!\n"; 22 | }, 0}, 23 | std::jthread{[&](int num){ 24 | std::osyncstream{std::cout} << "Starting thread " 25 | << num 26 | << ". Arrive on latch and wait to become zero.\n"; 27 | progress.arrive_and_wait(); 28 | std::osyncstream{std::cout} << "Thread " << num << " finished!\n"; 29 | }, 1} 30 | }; 31 | 32 | std::osyncstream{std::cout} << "Main thread waiting workers to finish.\n"; 33 | progress.wait(); // wait for all threads to finish. 34 | std::cout << "Main thread finished!\n"; 35 | 36 | return 0; 37 | } -------------------------------------------------------------------------------- /Chapter 9/hardware_interference_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | using std::hardware_destructive_interference_size; 9 | 10 | void increment(std::atomic& shared_res) { 11 | for(int i = 0; i < 100000; ++i) {shared_res++;} 12 | } 13 | 14 | int main() { 15 | auto start = chrono::steady_clock::now(); 16 | alignas(hardware_destructive_interference_size) 17 | atomic a_var1 = 0; 18 | alignas(hardware_destructive_interference_size) 19 | atomic a_var2 = 0; 20 | alignas(hardware_destructive_interference_size) 21 | atomic a_var3 = 0; 22 | 23 | jthread t1([&]() {increment(a_var1);}); 24 | jthread t2([&]() {increment(a_var2);}); 25 | jthread t3([&]() {increment(a_var3);}); 26 | 27 | t1.join(); 28 | t2.join(); 29 | t3.join(); 30 | auto end = chrono::steady_clock::now(); 31 | cout << "Elapsed time in microseconds: " 32 | << chrono::duration_cast(end - start).count() 33 | << " µs" << endl; 34 | 35 | cout << "L1 Cache Line size: " 36 | << hardware_destructive_interference_size 37 | << " bytes" << endl; 38 | 39 | cout << "The atomic var size is: " << sizeof(a_var1) 40 | << " and the addresses are: \n" 41 | << &a_var1 << endl 42 | << &a_var2 << endl 43 | << &a_var3 << endl; 44 | 45 | return 0; 46 | } -------------------------------------------------------------------------------- /Chapter 10/generator_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | struct Generator { 7 | struct promise_type { 8 | using Handle = coroutine_handle; 9 | Generator get_return_object() { 10 | return Generator 11 | { Handle::from_promise(*this) }; 12 | } 13 | suspend_always initial_suspend() 14 | { return {}; } 15 | suspend_always final_suspend() noexcept 16 | { return {}; } 17 | suspend_always yield_value(auto value) { 18 | currValue = value; 19 | return {}; 20 | } 21 | void unhandled_exception() { 22 | exit(EXIT_FAILURE); 23 | } 24 | uint32_t currValue; 25 | }; 26 | 27 | explicit Generator(promise_type::Handle p_crtHdnl) : 28 | crtHndl(p_crtHdnl) {} 29 | 30 | ~Generator() { 31 | if (crtHndl) 32 | crtHndl.destroy(); 33 | } 34 | 35 | int next() { 36 | crtHndl.resume(); 37 | return crtHndl.promise().currValue; 38 | } 39 | 40 | private: 41 | promise_type::Handle crtHndl; 42 | }; 43 | 44 | Generator exCoroutine() { 45 | auto idx = 0; 46 | for (;;) { 47 | co_yield idx++; 48 | } 49 | } 50 | 51 | int main() { 52 | auto crt = exCoroutine(); 53 | 54 | for (auto idx = 0; (idx = crt.next()) < 100000; ) 55 | cout << idx << " "; 56 | 57 | cout << endl; 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /Chapter 5/raii.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | class file_guard final { 8 | public: 9 | file_guard(string_view file, mode_t mode) : // {5} 10 | fd{open(file.data(), mode)} 11 | { 12 | if (fd == -1) { 13 | throw system_error{make_error_code(errc{errno})}; 14 | } 15 | cout << "File '" << file << 16 | "' with file descriptor '" << 17 | fd << "' is opened.\n"; 18 | } 19 | explicit file_guard(const file_guard&) = delete; // {6} 20 | file_guard& operator=(const file_guard&) = delete; 21 | explicit file_guard(file_guard&& other) noexcept : // {7} 22 | fd{move(other.fd)} { other.fd = -1; } 23 | file_guard& operator=(file_guard&& other) noexcept 24 | { 25 | fd = move(other.fd); 26 | other.fd = -1; 27 | return *this; 28 | } 29 | int getFileDescriptor() const noexcept { // {8} 30 | return fd; 31 | } 32 | ~file_guard() noexcept { // {9} 33 | if (fd != -1) { 34 | close(fd); 35 | cout << "File with file descriptor '" << fd << "' is closed.\n"; 36 | } 37 | } 38 | private: 39 | int fd; 40 | }; 41 | void Throw() { 42 | cout << "Ops, I need to throw ...\n"; 43 | throw system_error{make_error_code(errc::bad_file_descriptor)}; 44 | } 45 | int main() { 46 | const string_view myFileName{"/tmp/cpp-test-file"}; // {1} 47 | ofstream theFile(myFileName.data()); // {2} 48 | try { 49 | file_guard guard(myFileName, O_RDONLY); // {3} 50 | const auto fd = guard.getFileDescriptor(); 51 | Throw(); // {4} 52 | } catch (const exception& e) { 53 | cout << e.what(); 54 | return -1; 55 | } 56 | return 0; 57 | } -------------------------------------------------------------------------------- /Chapter 7/posix_semaphore_example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define COUNT_LIMIT 100 7 | 8 | void* consumer(void* params); 9 | void* producer(void* params); 10 | 11 | unsigned int shared_resource = 0; 12 | unsigned char full_flag = 0; 13 | unsigned short counter = 0; 14 | 15 | sem_t sem; 16 | 17 | int main() { 18 | pthread_t tid_prod; 19 | pthread_t tid_cons; 20 | 21 | if (sem_init(&sem, 0, 1)) { 22 | perror("sem_init_failed!"); 23 | } 24 | else { 25 | pthread_create(&tid_prod, NULL, producer, NULL); 26 | pthread_create(&tid_cons, NULL, consumer, NULL); 27 | 28 | pthread_join(tid_prod, NULL); 29 | pthread_join(tid_cons, NULL); 30 | 31 | sem_destroy(&sem); 32 | pthread_exit(NULL); 33 | } 34 | 35 | return 0; 36 | } 37 | 38 | void* consumer(void* params) { 39 | sem_post(&sem); //V(S) 40 | 41 | while (counter++ <= COUNT_LIMIT) { 42 | sem_wait(&sem); // P(S) 43 | printf("\nConsuming... "); 44 | if (1 == full_flag) { 45 | printf("value: %d\n", --shared_resource); 46 | full_flag = 0; 47 | } 48 | 49 | sem_post(&sem); // V(S) 50 | sched_yield(); // usleep(10) 51 | } 52 | 53 | sem_post(&sem); 54 | pthread_exit(0); 55 | } 56 | 57 | void* producer(void* params) { 58 | while (counter++ < COUNT_LIMIT) { 59 | sem_wait(&sem); // P(S) 60 | printf("\nProducing... "); 61 | if (0 == full_flag) { 62 | printf("value: %d\n", ++shared_resource); 63 | full_flag = 1; 64 | } 65 | 66 | sem_post(&sem); // V(S) 67 | sched_yield(); // usleep(10) 68 | } 69 | 70 | sem_post(&sem); 71 | pthread_exit(0); 72 | } -------------------------------------------------------------------------------- /Chapter 3/named_pipe_example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define FIFO_NAME "example_fifo" 8 | #define BUFF_LEN ((size_t) 64) 9 | 10 | int main() { 11 | pid_t childId = 0; 12 | int named_pipe_fd = 0; 13 | 14 | mkfifo(FIFO_NAME, 0666); // {1} 15 | childId = fork(); 16 | 17 | if (0 == childId) { 18 | named_pipe_fd = open(FIFO_NAME, O_RDWR); // {2} 19 | 20 | if (0 <= named_pipe_fd) { 21 | char message[BUFF_LEN] = {0}; 22 | sleep(1); 23 | 24 | size_t bytes_recv = 25 | read(named_pipe_fd, 26 | message, 27 | sizeof(message)); // {3} 28 | 29 | if(bytes_recv > 0) { 30 | const char response_msg[] 31 | = "Child printed the message!\n"; 32 | printf("Child: %s\n", message); 33 | write(named_pipe_fd, 34 | response_msg, 35 | sizeof(response_msg)); // {4} 36 | close(named_pipe_fd); // {5} 37 | } 38 | } 39 | else { 40 | printf("Child cannot open the pipe!\n"); 41 | } 42 | } 43 | else if (0 < childId) { 44 | named_pipe_fd = open(FIFO_NAME, O_RDWR); // {6} 45 | char message[BUFF_LEN] 46 | = "Sending some message to the child!"; 47 | write(named_pipe_fd, 48 | message, 49 | strlen(message) + 1); // {7} 50 | 51 | sleep(1); 52 | memset(message, 0, 53 | strlen(message) + 1); // {8} 54 | read(named_pipe_fd, 55 | message, 56 | sizeof(message)); // {9} 57 | printf("Parent: %s", message); 58 | close(named_pipe_fd); // {10} 59 | } 60 | else { 61 | printf("Fork failed!"); 62 | } 63 | 64 | //unlink(FIFO_NAME); // {11} 65 | return 0; 66 | } -------------------------------------------------------------------------------- /Chapter 6/data-race.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std::literals::chrono_literals; 6 | 7 | void Print(std::string_view what, std::string_view who, unsigned amount) 8 | { 9 | std::osyncstream{std::cout} << std::this_thread::get_id() << " " << who 10 | << what 11 | << amount << '\n'; 12 | } 13 | 14 | struct Account { 15 | Account(std::string_view the_owner, unsigned the_amount) noexcept : 16 | balance{the_amount}, owner{the_owner} {} 17 | 18 | Account& operator+=(unsigned amount) noexcept { 19 | Print(" balance before depositing: ", owner, balance); 20 | auto temp{balance}; 21 | std::this_thread::sleep_for(1ms); 22 | balance = temp + amount; 23 | Print(" balance after depositing: ", owner, balance); 24 | return *this; 25 | } 26 | 27 | Account& operator-=(unsigned amount) noexcept { 28 | Print(" balance before withdrawing: ", owner, balance); 29 | auto temp{balance}; 30 | balance = temp - amount; 31 | Print(" balance after withdrawing: ", owner, balance); 32 | return *this; 33 | } 34 | 35 | std::string GetBalance() const { 36 | return "Current account balance of " + owner + 37 | " is " + std::to_string(balance) + '\n'; 38 | } 39 | private: 40 | unsigned balance; 41 | std::string owner; 42 | }; 43 | 44 | void TransferMoney(unsigned amount, Account& from, Account& to) { 45 | from -= amount; 46 | to += amount; 47 | } 48 | 49 | int main() { 50 | Account bill_account{"Bill", 100}; 51 | Account john_account{"John", 50}; 52 | 53 | std::jthread first_transfer{[&](){ TransferMoney(10, bill_account, john_account); }}; 54 | std::jthread second_transfer{[&](){ TransferMoney(20, bill_account, john_account); }}; 55 | 56 | std::this_thread::sleep_for(100ms); 57 | std::cout << bill_account.GetBalance(); 58 | std::cout << john_account.GetBalance(); 59 | 60 | return 0; 61 | } -------------------------------------------------------------------------------- /Chapter 5/custom-error-category.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | enum class MyFileLibraryError { 4 | FileNotFound = 1000, 5 | FileAlreadyExists = 2000, 6 | FileBusy = 3000, 7 | FileTooBig = 4000 8 | }; 9 | 10 | class MyFileLibraryCategory : public std::error_category { 11 | public: 12 | const char* name() const noexcept override { 13 | return "MyFileLibrary"; 14 | } 15 | 16 | std::string message(int ev) const override { 17 | switch (static_cast(ev)) { 18 | case MyFileLibraryError::FileNotFound: 19 | return "The file was not found"; 20 | case MyFileLibraryError::FileBusy: 21 | return "The file is busy"; 22 | case MyFileLibraryError::FileAlreadyExists: 23 | return "The file already exists"; 24 | case MyFileLibraryError::FileTooBig: 25 | return "The file too big and can't be processed"; 26 | default: 27 | return "Unsupported error"; 28 | } 29 | } 30 | 31 | bool equivalent(int code, const std::error_condition& condition) const noexcept override { 32 | switch (static_cast(code)) { 33 | case MyFileLibraryError::FileAlreadyExists: 34 | return condition == std::errc::file_exists; 35 | case MyFileLibraryError::FileNotFound: 36 | return condition == std::errc::no_such_file_or_directory; 37 | case MyFileLibraryError::FileBusy: 38 | return condition == std::errc::text_file_busy; 39 | case MyFileLibraryError::FileTooBig: 40 | return condition == std::errc::file_too_large; 41 | default: 42 | return false; 43 | } 44 | } 45 | 46 | }; 47 | 48 | const MyFileLibraryCategory my_file_lib_category{}; 49 | 50 | int main() { 51 | std::error_code file_exists{static_cast(MyFileLibraryError::FileAlreadyExists), 52 | my_file_lib_category}; 53 | if (file_exists == std::errc::file_exists) { 54 | std::cout << "Msg: " << file_exists.message() << '\n'; 55 | std::cout << "Category: " << file_exists.default_error_condition().category().name() << '\n'; 56 | } 57 | 58 | return 0; 59 | } -------------------------------------------------------------------------------- /Chapter 9/ticket_lock.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using std::hardware_destructive_interference_size; 11 | using namespace std; 12 | using namespace std::literals::chrono_literals; 13 | 14 | struct TicketLock { 15 | alignas(hardware_destructive_interference_size) 16 | atomic_size_t serving; 17 | alignas(hardware_destructive_interference_size) 18 | atomic_size_t next; 19 | 20 | void lock() { 21 | const auto ticket = next.fetch_add(1, 22 | memory_order_relaxed); 23 | 24 | while (serving.load(memory_order_acquire) != 25 | ticket) {// Consider this_thread::yield() 26 | // for excessive iterations, which 27 | // go over a given threshold. 28 | } 29 | 30 | } 31 | 32 | void unlock() { 33 | serving.fetch_add(1, memory_order_release); 34 | } 35 | }; 36 | 37 | TicketLock spinlock = {0}; 38 | vector shared_res {}; 39 | 40 | void producer() { 41 | for(int i = 0; i < 100; i ++) { 42 | osyncstream{cout} << "Producing: " << endl; 43 | spinlock.lock(); 44 | shared_res.emplace_back("test1"); 45 | shared_res.emplace_back("test2"); 46 | for (const auto& el : shared_res) 47 | osyncstream{cout} << "p:" << el << endl; 48 | spinlock.unlock(); 49 | this_thread::sleep_for(100ms); 50 | } 51 | } 52 | 53 | void consumer() { 54 | for(int i = 0; i < 100; i ++) { 55 | this_thread::sleep_for(100ms); 56 | osyncstream{cout} << "Consuming: " << endl; 57 | spinlock.lock(); 58 | for (const auto& el : shared_res) 59 | osyncstream{cout} << "c:" << el << endl; 60 | shared_res.clear(); 61 | spinlock.unlock(); 62 | if (shared_res.empty()) 63 | osyncstream{cout} << "Consumed" << endl; 64 | } 65 | } 66 | 67 | int main() { 68 | jthread produce([]{ producer(); }); 69 | jthread consume([]{ consumer(); }); 70 | produce.join(); 71 | consume.join(); 72 | return 0; 73 | } -------------------------------------------------------------------------------- /Chapter 4/mean_value.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | struct Mean { 8 | Mean() noexcept { 9 | std::cout << 10 | std::source_location::current().function_name() << 11 | " of " << this << '\n'; 12 | } 13 | Mean(Mean&& a) noexcept : sum{a.sum}, count{a.count} { 14 | std::cout << 15 | std::source_location::current().function_name() << 16 | " from: " << &a << " to: " << this << '\n'; 17 | a.sum = 0; 18 | a.count = -1; 19 | } 20 | Mean& operator=(Mean&& a) noexcept { 21 | std::cout << 22 | std::source_location::current().function_name() << 23 | " from: " << &a << " to: " << this << '\n'; 24 | sum = a.sum; 25 | count = a.count; 26 | return *this; 27 | } 28 | Mean(const Mean& a) noexcept : sum{a.sum}, count{a.count} { 29 | std::cout << 30 | std::source_location::current().function_name() << 31 | " from: " << &a << " to: " << this << '\n'; 32 | } 33 | ~Mean() noexcept { 34 | std::cout << 35 | std::source_location::current().function_name() << 36 | " of " << this << '\n'; 37 | } 38 | void operator()(const double& val) { 39 | std::cout << 40 | std::source_location::current().function_name() << 41 | " of " << this << '\n'; 42 | sum += val; 43 | ++count; 44 | } 45 | private: 46 | double sum{}; 47 | int count{}; 48 | friend std::ostream& operator<<(std::ostream& os, const Mean& a); 49 | }; 50 | 51 | std::ostream& operator<<(std::ostream& os, const Mean& a) { 52 | double mean{std::nan("")}; 53 | if (a.count > 0) { 54 | mean = a.sum / a.count; 55 | } 56 | os << mean; 57 | return os; 58 | } 59 | 60 | int main() { 61 | Mean calc_mean; 62 | std::vector v1{1.0, 2.5, 4.0, 5.5}; 63 | std::cout << "Start calculation\n"; 64 | 65 | // Pass by value 66 | //calc_mean = std::for_each(v1.begin(), v1.end(), calc_mean); 67 | 68 | // Pass by reference 69 | std::for_each(v1.begin(), v1.end(), std::ref(calc_mean)); 70 | 71 | std::cout << "Finish calculation\n"; 72 | std::cout << calc_mean << '\n'; 73 | return 0; 74 | } -------------------------------------------------------------------------------- /Chapter 7/tcp_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | constexpr auto PORT = 8080; 10 | constexpr auto BUF_SIZE = 16; 11 | 12 | int main() { 13 | constexpr auto ip = "192.168.136.128"; 14 | struct sockaddr_in addr = { 0 }; 15 | socklen_t addr_size = 0; 16 | 17 | if (auto serv_sock = 18 | socket(AF_INET, SOCK_STREAM, 0); 19 | serv_sock < 0) { 20 | const auto ecode 21 | { make_error_code(errc{errno}) }; 22 | cerr << "Error opening socket!"; 23 | system_error exception{ ecode }; 24 | throw exception; 25 | } 26 | else { 27 | addr.sin_family = AF_INET; 28 | addr.sin_port = htons(PORT); 29 | addr.sin_addr.s_addr = inet_addr(ip); 30 | 31 | if (auto res = 32 | connect(serv_sock, 33 | (struct sockaddr*)&addr, 34 | sizeof(addr)); res == -1) { 35 | const auto ecode 36 | { make_error_code(errc{errno}) }; 37 | cerr << "Error connecting to socket!"; 38 | system_error exception{ ecode }; 39 | throw exception; 40 | } 41 | 42 | string_view req = { to_string(getpid()) }; 43 | cout << "Client request: " << req << endl; 44 | if (auto res = 45 | send(serv_sock, 46 | req.data(), 47 | req.size(), 48 | MSG_DONTWAIT); 49 | res >= 0) { 50 | array buffer{}; 51 | if (auto b_recv = 52 | recv(serv_sock, 53 | buffer.data(), 54 | buffer.size(), 55 | MSG_PEEK); 56 | b_recv > 0) { 57 | buffer.data()[b_recv] = '\0'; 58 | cout << "Server response: " 59 | << buffer.data(); 60 | } 61 | else { 62 | cerr << "Error sending to socket!"; 63 | } 64 | } 65 | else { 66 | cerr << "Error receiving from socket!"; 67 | } 68 | if (auto res = 69 | close(serv_sock); res == -1) { 70 | const auto ecode 71 | { make_error_code(errc{errno}) }; 72 | cerr << "Error closing socket!"; 73 | system_error exception{ ecode }; 74 | throw exception; 75 | } 76 | cout << "\nJob done! Disconnecting." << endl; 77 | } 78 | return 0; 79 | } -------------------------------------------------------------------------------- /Chapter 3/cpp_and_named_pipe.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace std::filesystem; 11 | 12 | static string_view fifo_name = "example_fifo"; // {1} 13 | static constexpr size_t buf_size = 64; 14 | 15 | void write(int out_fd, 16 | string_view message) { // {2} 17 | write(out_fd, 18 | message.data(), 19 | message.size()); 20 | } 21 | 22 | string read(int in_fd) { // {3} 23 | array buffer; 24 | size_t bytes = read(in_fd, 25 | buffer.data(), 26 | buffer.size()); 27 | if (bytes > 0) { 28 | return {buffer.data(), bytes}; // {4} 29 | } 30 | return {}; 31 | } 32 | 33 | int main() { 34 | if (!exists(fifo_name)) 35 | mkfifo(fifo_name.data(), 0666); // {5} 36 | 37 | if (pid_t childId = fork(); childId == -1) { 38 | perror("Process creation failed"); 39 | exit(EXIT_FAILURE); 40 | } 41 | else { 42 | if(is_fifo(fifo_name)) { // {6} 43 | if (childId == 0) { 44 | if (int named_pipe_fd = 45 | open(fifo_name.data(), O_RDWR); 46 | named_pipe_fd >= 0) { // {7} 47 | string message; 48 | message.reserve(buf_size); 49 | sleep(1); 50 | message = read(named_pipe_fd); // {8} 51 | string_view response_msg 52 | = "Child printed the message!"; 53 | cout << "Child: " << message << endl; 54 | write(named_pipe_fd, 55 | response_msg); // {9} 56 | close(named_pipe_fd); 57 | } 58 | else { 59 | cout << "Child cannot open the pipe!" 60 | << endl; 61 | } 62 | } 63 | else if (childId > 0) { 64 | if (int named_pipe_fd = 65 | open(fifo_name.data(), O_RDWR); 66 | named_pipe_fd >= 0) { // {10} 67 | string message 68 | = "Sending some message to the child!"; 69 | write(named_pipe_fd, 70 | message); // {11} 71 | 72 | sleep(1); 73 | message = read(named_pipe_fd); // {12} 74 | cout << "Parent: " << message << endl; 75 | close(named_pipe_fd); 76 | } 77 | } 78 | else { 79 | cout << "Fork failed!"; 80 | } 81 | remove(fifo_name); // {13} 82 | } 83 | } 84 | return 0; 85 | } -------------------------------------------------------------------------------- /Chapter 6/barrier-temperature.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using Temperature = 10 | std::tuple, // Temperature measurements 12 | double>; // Calculated mean temperature 13 | // value for a specific room 14 | std::vector room_temperatures { 15 | {"living_room",{}, 0.0}, 16 | {"bedroom", {}, 0.0}, 17 | {"kitchen", {}, 0.0}, 18 | {"closet", {}, 0.0} 19 | }; 20 | 21 | void GetTemperatures(Temperature& temp) { 22 | std::mt19937 gen{std::random_device{}()}; 23 | 24 | // Add normal distribution with mean = 20 25 | // and standard deviation of 8 26 | std::normal_distribution<> d{20, 8}; 27 | 28 | auto& input_data{std::get<1>(temp)}; 29 | input_data.clear(); 30 | for (auto n{0}; n < 10; ++n) { 31 | // Add input data 32 | input_data.emplace_back(d(gen)); 33 | } 34 | } 35 | 36 | void ProcessMeasurement(Temperature& temp){ 37 | const auto& values{std::get<1>(temp)}; 38 | auto& mean{std::get<2>(temp)}; 39 | mean = std::reduce(values.begin(), values.end()) / values.size(); 40 | } 41 | 42 | int main() { 43 | // Init data 44 | std::ranges::for_each(room_temperatures, GetTemperatures); 45 | 46 | std::stop_source message; 47 | std::barrier measurementBarrier{ // {1} 48 | static_cast(room_temperatures.size()), // {2} 49 | [&message]() noexcept { // {3} 50 | // Get all results 51 | double mean{0.0}; 52 | for (const auto& room_t : room_temperatures) { 53 | std::cout << "Mean temperature in " 54 | << std::get<0>(room_t) 55 | << " is " << std::get<2>(room_t) << ".\n"; 56 | mean += std::get<2>(room_t); 57 | } 58 | mean /= room_temperatures.size(); 59 | std::cout << "Mean temperature in your home is " << mean << " degrees Celsius.\n"; 60 | std::cout << "=============================================\n"; 61 | 62 | // Add new input data 63 | std::ranges::for_each(room_temperatures, GetTemperatures); 64 | 65 | // Make 4 measurements and request stop. 66 | static unsigned timer{0}; 67 | if (timer >= 3) { 68 | message.request_stop(); 69 | } 70 | ++timer; 71 | } 72 | }; 73 | 74 | std::vector measurementSensors; 75 | for (auto& temp : room_temperatures) { 76 | measurementSensors.emplace_back([&measurementBarrier, &message, &temp](){ 77 | const auto& token = message.get_token(); 78 | while(!token.stop_requested()) { 79 | ProcessMeasurement(temp); 80 | measurementBarrier.arrive_and_wait(); // {4} 81 | } 82 | }); 83 | } 84 | 85 | return 0; 86 | } -------------------------------------------------------------------------------- /Chapter 9/read_write_lock.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | using namespace std::chrono_literals; 12 | 13 | struct Book { 14 | string_view title; 15 | string_view author; 16 | uint32_t year; 17 | }; 18 | 19 | shared_mutex shresMutex; 20 | 21 | vector shared_data = {{"Harry Potter", "Rowling", 1997}, 22 | {"East of Eden", "Steinbeck", 1952}, 23 | {"It", "King", 1986}}; 24 | 25 | void rd_applyYearFilter(uint32_t yearKey) { 26 | auto year_filter = 27 | [yearKey](const auto& book) 28 | { return book.year < yearKey; }; 29 | shared_lock readerLock(shresMutex); 30 | osyncstream{cout} << "Apply year filter: " << endl; 31 | for (const auto &book : shared_data | 32 | views::filter(year_filter)) 33 | osyncstream{cout} << book.title << endl; 34 | } 35 | 36 | void rd_Reversed() { 37 | shared_lock readerLock(shresMutex); 38 | osyncstream{cout} << "Apply reversed order: " << endl; 39 | for (const auto &book : views::reverse(shared_data)) 40 | osyncstream{cout} << book.title << endl; 41 | } 42 | 43 | void rd_ReversedFilteredByAuthorNameSize(uint32_t nameSizeKey) { 44 | shared_lock readerLock(shresMutex); 45 | osyncstream{cout} 46 | << "Apply reversed order and filter by author name size: " 47 | << endl; 48 | for (const auto &book : views::reverse(shared_data) | 49 | views::filter([nameSizeKey](Book book) 50 | { return book.author.size() < nameSizeKey; })) 51 | osyncstream{cout} << book.title << endl; 52 | } 53 | 54 | void rd_dropFirstN(uint32_t dropKey) { 55 | shared_lock readerLock(shresMutex); 56 | osyncstream{cout} << "Drop first N elements: " << endl; 57 | for (const auto &book : ranges::drop_view(shared_data, dropKey)) 58 | osyncstream{cout} << book.title << endl; 59 | } 60 | 61 | void wr_addNewBook(string_view title, 62 | string_view author, 63 | uint32_t year) { 64 | lock_guard writerLock(shresMutex); 65 | osyncstream{cout} << "Add new book: " << title << endl; 66 | shared_data.emplace_back(Book {title, author, year}); 67 | this_thread::sleep_for(500ms); 68 | } 69 | 70 | int main() { 71 | thread yearFilter1( 72 | []{ rd_applyYearFilter(1990); }); 73 | thread reversed( 74 | []{ rd_Reversed(); }); 75 | thread reversed_and_filtered( 76 | []{ rd_ReversedFilteredByAuthorNameSize(8); }); 77 | thread addBook1( 78 | []{ wr_addNewBook("Dune", "Herbert", 1965); }); 79 | thread dropFirstElements( 80 | []{ rd_dropFirstN(1); }); 81 | thread addBook2( 82 | []{ wr_addNewBook("Jaws", "Benchley", 1974); }); 83 | thread yearFilter2( 84 | []{ rd_applyYearFilter(1970); }); 85 | 86 | yearFilter1.join(); 87 | reversed.join(); 88 | reversed_and_filtered.join(); 89 | dropFirstElements.join(); 90 | yearFilter2.join(); 91 | addBook1.join(); 92 | addBook2.join(); 93 | 94 | osyncstream{cout} 95 | << "Print subranged books in main thread:" << endl; 96 | auto const sub_res = 97 | ranges::subrange(shared_data.begin()+1, 98 | shared_data.begin()+3); 99 | for (const auto& book: sub_res){ 100 | osyncstream{cout} << book.title << " " << book.author 101 | << " " << book.year << endl; 102 | } 103 | return 0; 104 | } -------------------------------------------------------------------------------- /Chapter 7/tcp_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | constexpr auto PORT = 8080; 14 | constexpr auto BUF_SIZE = 256; 15 | constexpr auto BACKLOG = 5; 16 | constexpr auto SIG_MAX = 128; 17 | void exitHandler(int sig) { 18 | cerr << "Exit command called - terminating server!" 19 | << endl; 20 | exit(SIG_MAX + sig); 21 | } 22 | 23 | int main() { 24 | signal(SIGINT, exitHandler); 25 | 26 | constexpr auto ip = "192.168.136.128"; 27 | auto client_sock = 0; 28 | 29 | struct sockaddr_in server_addr = { 0 }; 30 | struct sockaddr_in client_addr = { 0 }; 31 | 32 | socklen_t addr_size = 0; 33 | auto result = 0; 34 | 35 | if (auto server_sock = 36 | socket(AF_INET, SOCK_STREAM, 0); 37 | server_sock < 0) { 38 | const auto ecode 39 | { make_error_code(errc{errno}) }; 40 | cerr << "Error opening socket!"; 41 | system_error exception{ ecode }; 42 | throw exception; 43 | } 44 | else { 45 | server_addr.sin_family = AF_INET; 46 | server_addr.sin_port = htons(PORT); 47 | server_addr.sin_addr.s_addr = inet_addr(ip); 48 | 49 | result = bind(server_sock, 50 | (struct sockaddr*)&server_addr, 51 | sizeof(server_addr)); 52 | if (result < 0) { 53 | const auto ecode 54 | { make_error_code(errc{errno}) }; 55 | cerr << "Error binding socket!"; 56 | system_error exception{ ecode }; 57 | throw exception; 58 | } 59 | 60 | result = listen(server_sock, BACKLOG); 61 | if (result != 0) { 62 | cerr << "Cannot accept connection"; 63 | } 64 | 65 | cout << "Listening..." << endl; 66 | 67 | for (;;) { 68 | addr_size = sizeof(client_addr); 69 | client_sock = 70 | accept(server_sock, 71 | (struct sockaddr*)&client_addr, 72 | &addr_size); 73 | 74 | if (client_sock > 0) { 75 | cout << "Client connected." << endl; 76 | array buffer{}; 77 | 78 | if (auto b_recv = recv(client_sock, 79 | buffer.data(), 80 | buffer.size(), 81 | MSG_PEEK); 82 | b_recv > 0) { 83 | buffer.data()[b_recv] = '\0'; 84 | cout << "Client request: " 85 | << buffer.data() << endl; 86 | string_view response = 87 | { to_string(getpid()) }; 88 | cout << "Server response: " 89 | << response << endl; 90 | send(client_sock, 91 | response.data(), 92 | response.size(), 93 | MSG_DONTWAIT); 94 | } 95 | else { 96 | cerr << "Error receiving from socket!"; 97 | } 98 | if (auto res = 99 | close(client_sock); res == -1) { 100 | const auto ecode 101 | { make_error_code(errc{errno}) }; 102 | cerr << "Error closing socket!"; 103 | system_error exception{ ecode }; 104 | throw exception; 105 | } 106 | } 107 | else { 108 | cerr << "Client connection failed" << endl; 109 | } 110 | } 111 | } 112 | 113 | return 0; 114 | } -------------------------------------------------------------------------------- /Chapter 9/shared_mem_sp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | 14 | constexpr auto PROT_RW = PROT_READ | PROT_WRITE; 15 | constexpr auto SHM_ID = "/test_shm"; 16 | constexpr auto BUF_SIZE = 256; 17 | 18 | string_view message{ "This is a testing message!" }; 19 | 20 | mutex cv_mutex; 21 | condition_variable cond_var; 22 | bool work_done = false; 23 | 24 | template 25 | struct mmap_deallocator { 26 | size_t m_size; 27 | mmap_deallocator(size_t size) : m_size{ size } {} 28 | 29 | void operator()(T* ptr) const { 30 | munmap(ptr, m_size); 31 | ptr->~T(); 32 | } 33 | }; 34 | 35 | template 36 | auto producer(T buffer, N size) { 37 | unique_lock lock(cv_mutex); 38 | cond_var.wait(lock, [] { return work_done == false; }); 39 | if (int fd = 40 | shm_open(SHM_ID, O_CREAT | O_RDWR, 0644); 41 | fd != -1) { 42 | ftruncate(fd, size); 43 | if (auto ptr = 44 | mmap(0, size, 45 | PROT_RW, MAP_SHARED, 46 | fd, 0); ptr != MAP_FAILED) { 47 | auto obj = new (ptr) T(buffer); 48 | auto del = mmap_deallocator(size); 49 | work_done = true; 50 | lock.unlock(); 51 | cond_var.notify_one(); 52 | return unique_ptr>(obj, del); 54 | } 55 | else { 56 | const auto ecode 57 | { make_error_code(errc{errno}) }; 58 | cerr << "Error mapping the shmem!"; 59 | system_error exception{ ecode }; 60 | throw exception; 61 | } 62 | } 63 | else { 64 | const auto ecode 65 | { make_error_code(errc{errno}) }; 66 | cerr << "Error opening the shmem!"; 67 | system_error exception{ ecode }; 68 | throw exception; 69 | } 70 | // Some shm function failed. 71 | throw bad_alloc(); 72 | } 73 | 74 | template 75 | auto consumer(N size) { 76 | unique_lock lock(cv_mutex); 77 | cond_var.wait(lock, [] { return work_done == true; }); 78 | if (int fd = 79 | shm_open(SHM_ID, O_RDWR, 0644); fd != -1) { 80 | ftruncate(fd, size); 81 | if (auto ptr = 82 | mmap(0, size, PROT_RW, MAP_SHARED, 83 | fd, 0); ptr != MAP_FAILED) { 84 | auto obj = static_cast(ptr); 85 | auto del = mmap_deallocator(sizeof(T)); 86 | work_done = false; 87 | return unique_ptr>(obj, del); 89 | } 90 | else { 91 | const auto ecode 92 | { make_error_code(errc{errno}) }; 93 | cerr << "Error mapping the shmem!"; 94 | system_error exception{ ecode }; 95 | throw exception; 96 | } 97 | } 98 | else { 99 | const auto ecode 100 | { make_error_code(errc{errno}) }; 101 | cerr << "Error opening the shmem!"; 102 | system_error exception{ ecode }; 103 | throw exception; 104 | } 105 | // Some shm function failed. 106 | throw bad_alloc(); 107 | } 108 | 109 | int main() { 110 | 111 | thread server([] { 112 | auto ptr = 113 | producer(message.data(), message.size()); 114 | cout << "Sending: " << *ptr << '\n'; 115 | }); 116 | thread client([] { 117 | auto ptr = 118 | consumer(message.size()); 119 | cout << "Receiving: " << *ptr << '\n'; 120 | }); 121 | 122 | server.join(); 123 | client.join(); 124 | return 0; 125 | } -------------------------------------------------------------------------------- /Chapter 7/mq_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | constexpr auto MAX_SIZE = 1024; 13 | string_view QUEUE_NAME = "/test_queue"; 14 | 15 | void readFromQueue() { 16 | cout << "Thread READER starting..." << endl; 17 | 18 | mqd_t mq = { 0 }; 19 | struct mq_attr attr = { 0 }; 20 | 21 | array buffer{}; 22 | 23 | // Initialize the queue attributes. 24 | attr.mq_flags = 0; 25 | attr.mq_maxmsg = 10; 26 | attr.mq_msgsize = MAX_SIZE; 27 | attr.mq_curmsgs = 0; 28 | 29 | // Check if the queue open is successful. 30 | if (mq = mq_open(QUEUE_NAME.data(), O_CREAT | O_RDONLY, 31 | 0700, &attr); mq > -1) { // {1} 32 | for (;;) { 33 | if (auto bytes_read = mq_receive(mq, 34 | buffer.data(), 35 | buffer.size(), 36 | NULL); 37 | bytes_read > 0) { // {2} 38 | buffer[bytes_read] = '\0'; 39 | cout << "Received: " 40 | << buffer.data() 41 | << endl; // {3} 42 | } 43 | else if (bytes_read == -1) { 44 | perror("Receive message failed!"); 45 | } 46 | else { 47 | cout << "\n\n\n***Receiving ends***" 48 | << endl; 49 | mq_close(mq); // {4} 50 | break; 51 | } 52 | } 53 | } 54 | else { 55 | cout << "Receiver: Failed to load queue: " 56 | << strerror(errno) << endl; 57 | } 58 | 59 | mq_unlink(QUEUE_NAME.data()); 60 | } 61 | 62 | void writeToQueue() { 63 | cout << "Thread WRITER starting..." << endl; 64 | 65 | mqd_t mq = { 0 }; 66 | 67 | if (mq = mq_open(QUEUE_NAME.data(), O_WRONLY, 68 | 0700, NULL); mq > -1) { // {5} 69 | int fd = open("test.dat", O_RDONLY); // {6} 70 | if (fd > 0) { 71 | for (;;) { 72 | // This could be taken from cin. 73 | array buffer{}; 74 | if (auto bytes_to_send = 75 | read(fd, 76 | buffer.data(), 77 | buffer.size()); 78 | bytes_to_send > 0) { // {7} 79 | if (auto b_sent = 80 | mq_send(mq, 81 | buffer.data(), 82 | buffer.size(), 83 | 0); 84 | b_sent == -1) {// {8} 85 | cout << "Sent failed!" 86 | << strerror(errno) 87 | << endl; 88 | } 89 | } 90 | else if (bytes_to_send == 0) { 91 | cout << "Sending ends...." << endl; 92 | if (auto b_sent = 93 | mq_send(mq, 94 | buffer.data(), 95 | 0, 96 | 0); b_sent == -1) { // {9} 97 | cout << "Sent failed!" 98 | << strerror(errno) 99 | << endl; 100 | } 101 | break; 102 | } 103 | else { 104 | perror("Reading from file failed!"); 105 | } 106 | } 107 | } 108 | else { 109 | cout << "Error opening file" << endl; 110 | } 111 | close(fd); // {10} 112 | mq_close(mq); 113 | } 114 | else { 115 | cout << "Sender: Failed to load queue: " 116 | << strerror(errno) << endl; 117 | } 118 | } 119 | 120 | int main() { 121 | thread reader{ readFromQueue }; 122 | thread writer{ writeToQueue }; 123 | 124 | reader.join(); 125 | writer.join(); 126 | 127 | cout << "Main: program completed. Exiting." << endl; 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /Chapter 7/shm_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | string_view SHM_ID = "/test_shm"; 14 | string_view SEM_PROD_ID = "/test_sem_prod"; 15 | string_view SEM_CONS_ID = "/test_sem_cons"; 16 | 17 | constexpr auto SHM_SIZE = 1024; 18 | 19 | sem_t *sem_prod; 20 | sem_t *sem_cons; 21 | 22 | void process_creator() { 23 | int res = 0; 24 | int fd = 0; 25 | void *shm_addr = NULL; 26 | 27 | if (int pid = fork(); pid == 0) { 28 | // Child - used for consuming data. 29 | if (fd = shm_open(SHM_ID.data(), 30 | O_RDONLY, 31 | 0700); // {1} 32 | fd == -1) { 33 | const auto ecode{make_error_code(errc{errno})}; 34 | cerr << "Error opening shm region"; 35 | system_error exception{ecode}; 36 | throw exception; 37 | } 38 | 39 | shm_addr = mmap(NULL, SHM_SIZE, 40 | PROT_READ, MAP_SHARED, 41 | fd, 0); // {2} 42 | if (shm_addr == MAP_FAILED) { 43 | const auto ecode{make_error_code(errc{errno})}; 44 | cerr << "Error mapping shm region"; 45 | system_error exception{ecode}; 46 | throw exception; 47 | } 48 | 49 | array buffer{}; 50 | sem_wait(sem_cons); 51 | memcpy(buffer.data(), 52 | shm_addr, 53 | buffer.size()); // {3} 54 | if(strlen(buffer.data()) != 0) { 55 | cout << "PID : " << getpid() 56 | << "consumed: " << buffer.data(); 57 | } 58 | sem_post(sem_prod); 59 | 60 | exit(EXIT_SUCCESS); 61 | } 62 | else if (pid > 0) { 63 | // Parent - used for producing data. 64 | fd = shm_open(SHM_ID.data(), 65 | O_CREAT | O_RDWR, 66 | 0700); // {4} 67 | if (fd == -1) { 68 | const auto ecode{make_error_code(errc{errno})}; 69 | cerr << "Error opening shm region"; 70 | system_error exception{ecode}; 71 | throw exception; 72 | } 73 | 74 | res = ftruncate(fd, SHM_SIZE); // {5} 75 | if (res == -1) { 76 | const auto ecode{make_error_code(errc{errno})}; 77 | cerr << "Error resizing shm region"; 78 | system_error exception{ecode}; 79 | throw exception; 80 | } 81 | 82 | shm_addr = mmap(NULL, SHM_SIZE, 83 | PROT_WRITE, MAP_SHARED, 84 | fd, 0); // {6} 85 | if (shm_addr == MAP_FAILED) { 86 | const auto ecode{make_error_code(errc{errno})}; 87 | cerr << "Error mapping shm region"; 88 | system_error exception{ecode}; 89 | throw exception; 90 | } 91 | sem_wait(sem_prod); 92 | string_view produced_data {"Some test data, coming!"}; 93 | memcpy(shm_addr, 94 | produced_data.data(), 95 | produced_data.size()); // {7} 96 | sem_post(sem_cons); 97 | 98 | waitpid(pid, NULL, 0); // {8} 99 | res = munmap(shm_addr, SHM_SIZE); // {9} 100 | if (res == -1) { 101 | const auto ecode{make_error_code(errc{errno})}; 102 | cerr << "Error cleaning-up shm region"; 103 | system_error exception{ecode}; 104 | throw exception; 105 | } 106 | 107 | fd = shm_unlink(SHM_ID.data()); //{10} 108 | if (fd == -1) { 109 | const auto ecode{make_error_code(errc{errno})}; 110 | cerr << "Error destructing shm region"; 111 | system_error exception{ecode}; 112 | throw exception; 113 | } 114 | } 115 | else { 116 | const auto ecode{make_error_code(errc{errno})}; 117 | cerr << "Process creation failed!"; 118 | system_error exception{ecode}; 119 | throw exception; 120 | } 121 | exit(EXIT_SUCCESS); 122 | } 123 | 124 | int main() { 125 | sem_prod = sem_open(SEM_PROD_ID.data(), 126 | O_CREAT, 0644, 0); // {11} 127 | sem_cons = sem_open(SEM_CONS_ID.data(), 128 | O_CREAT, 0644, 0); 129 | sem_post(sem_prod); 130 | process_creator(); 131 | sem_close(sem_prod); // {12} 132 | sem_close(sem_cons); 133 | sem_unlink(SEM_PROD_ID.data()); 134 | sem_unlink(SEM_CONS_ID.data()); 135 | return 0; 136 | } -------------------------------------------------------------------------------- /Chapter 10/shmem_coroutines.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | constexpr auto PROT_RW = PROT_READ | PROT_WRITE; 18 | constexpr auto SHM_ID = "/test_shm"; 19 | 20 | using namespace std; 21 | using namespace std::chrono_literals; 22 | 23 | string_view message = "This is a testing message!"; 24 | 25 | template 26 | struct mmap_deallocator { 27 | size_t m_size; 28 | mmap_deallocator(size_t size) : m_size{ size } {} 29 | 30 | void operator()(T* ptr) const { 31 | munmap(ptr, m_size); 32 | ptr->~T(); 33 | } 34 | }; 35 | 36 | struct Event { 37 | Event() = default; 38 | Event(const Event&) = delete; 39 | Event(Event&&) = delete; 40 | Event& operator=(const Event&) = delete; 41 | Event& operator=(Event&&) = delete; 42 | 43 | class Awaiter; 44 | Awaiter operator co_await() const noexcept; 45 | 46 | template 47 | void notify(T, int, N) noexcept; 48 | 49 | private: 50 | mutable atomic suspended{ nullptr }; 51 | mutable atomic notified{ false }; 52 | 53 | }; 54 | 55 | struct Event::Awaiter { 56 | Awaiter(const Event& p_event) : event(p_event) {} 57 | 58 | bool await_ready() const { return false; }; 59 | bool await_suspend(coroutine_handle<> handle) noexcept; 60 | void await_resume() noexcept {} 61 | 62 | private: 63 | friend class Event; 64 | const Event& event; 65 | coroutine_handle<> coroutineHandle; 66 | }; 67 | 68 | bool 69 | Event::Awaiter::await_suspend(coroutine_handle<> handle) 70 | noexcept { 71 | coroutineHandle = handle; 72 | 73 | if (event.notified) return false; 74 | event.suspended.store(this); 75 | 76 | return true; 77 | } 78 | 79 | Event::Awaiter Event::operator co_await() const noexcept { 80 | return Awaiter{ *this }; 81 | } 82 | 83 | struct Task { 84 | struct promise_type { 85 | Task get_return_object() { return {}; } 86 | suspend_never initial_suspend() { return {}; } 87 | suspend_never final_suspend() noexcept { return {}; } 88 | void return_void() {} 89 | void unhandled_exception() { 90 | exit(EXIT_FAILURE); 91 | } 92 | }; 93 | }; 94 | 95 | template 96 | void Event::notify(T buffer, int fd, N size) noexcept { 97 | notified = false; 98 | 99 | auto* waiter = 100 | static_cast(suspended.load()); 101 | if (waiter != nullptr) { 102 | ftruncate(fd, size); 103 | if (const auto ptr = mmap(0, size, 104 | PROT_RW, MAP_SHARED, 105 | fd, 0); ptr != MAP_FAILED) { 106 | auto* obj = new (ptr) T(buffer); 107 | auto del = mmap_deallocator(size); 108 | auto res = 109 | unique_ptr>(obj, del); 110 | // You can do something else with res, if required. 111 | } 112 | else { 113 | cerr << "Error mapping shm region"; 114 | } 115 | waiter->coroutineHandle.resume(); 116 | } 117 | } 118 | 119 | template 120 | Task receiver(Event& event, int fd, N size) { 121 | co_await event; 122 | ftruncate(fd, size); 123 | if (const auto ptr = mmap(0, size, 124 | PROT_RW, MAP_SHARED, 125 | fd, 0); ptr != MAP_FAILED) { 126 | auto* obj = static_cast(ptr); 127 | auto del = mmap_deallocator(size); 128 | auto res = 129 | unique_ptr>(obj, del); 130 | 131 | if (res != nullptr) 132 | cout << "Receiver: " << *res << endl; 133 | } 134 | else { 135 | cerr << "Error mapping shm region"; 136 | } 137 | } 138 | 139 | int main() { 140 | Event event{}; 141 | if (int fd = shm_open(SHM_ID, O_CREAT | O_RDWR, 0644); 142 | fd > 0) { 143 | auto receiverT = jthread([&event, &fd]{ 144 | receiver(ref(event), 145 | fd, 146 | (message.size())); 147 | }); 148 | 149 | auto senderT = jthread([&event, &fd]{ 150 | this_thread::sleep_for(2s); 151 | event.notify(message.data(), 152 | fd, 153 | message.size()); 154 | }); 155 | 156 | receiverT.join(); 157 | senderT.join(); 158 | close(fd); 159 | shm_unlink(SHM_ID); 160 | } 161 | else { 162 | const auto ecode{ make_error_code(errc{errno}) }; 163 | cerr << "Error opening shm region"; 164 | system_error exception{ ecode }; 165 | throw exception; 166 | } 167 | 168 | return 0; 169 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ Programming for Linux Systems 2 | 3 | C++ Programming for Linux Systems 4 | 5 | This is the code repository for [C++ Programming for Linux Systems](https://www.packtpub.com/product/c-programming-for-linux-systems/9781805129004), published by Packt. 6 | 7 | **Create robust enterprise software for Linux and Unix-based operating systems** 8 | 9 | ## What is this book about? 10 | Around 35 million Linux and almost 2 billion Android users rely on C++ for everything from the simplest embedded and IoT devices to cloud services, supercomputing, and space exploration. To help you produce high-quality software, two industry experts have transformed their knowledge and experience into practical examples in system programming with C++ Programming for Linux Systems. 11 | 12 | This book covers the following exciting features: 13 | * Use C++20 features to craft elegant, efficient, and modern code for Linux systems 14 | * Acquire essential system programming skills with hands-on examples 15 | * Develop a deep understanding of Linux programming, from embedded systems to cloud services 16 | * Tailor your applications to exploit the strengths and mitigate the weaknesses of different architectures 17 | * Merge advanced C++, system programming, Linux insights, and architecture to create exceptional software 18 | * Boost your code quality by using system programming techniques to refine and optimize your codebase 19 | 20 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1805129007) today! 21 | 22 | https://www.packtpub.com/ 24 | 25 | 26 | ## Instructions and Navigations 27 | All of the code is organized into folders. For example, Chapter 2. 28 | 29 | The code will look like the following: 30 | ``` 31 | std::for_each(v1.begin(), v1.end(), 32 | [&mean, sum{0.0}, count{0}, text](const double& val) 33 | mutable 34 | ``` 35 | 36 | **Following is what you need for this book:** 37 | This book is for every software developer looking to improve and update their C++ development skills. Both students and professionals will find this book useful as the examples are curated to match any area of expertise and are easily adaptable. At the same time, they don’t lose focus of the system specifics. A basic understanding of operating systems’ interfaces is a must along with experience in software development. 38 | 39 | With the following software and hardware list you can run all code files present in the book (Chapter 1-10). 40 | 41 | ### Software and Hardware List 42 | 43 | | Chapter | Software required | OS required | 44 | | -------- | ------------------------------------| -----------------------------------| 45 | | 1 | C++20 | Linux Mint 21 | 46 | | 2 | GCC12.2 | | 47 | | 3 | godbolt.org | | 48 | 49 | 50 | ### Related products 51 | * Expert C++ - Second Edition [[Packt]](https://www.packtpub.com/product/expert-c-second-edition/9781804617830) [[Amazon]](https://www.amazon.com/dp/1804617830) 52 | 53 | * Hands-On Design Patterns with C++ - Second Edition [[Packt]](https://www.packtpub.com/product/hands-on-design-patterns-with-c-second-edition/9781804611555) [[Amazon]](https://www.amazon.com/dp/1804611557) 54 | 55 | ## Get to Know the Authors 56 | **Desislav Andreev** 57 | is a software engineer with a PhD in artificial intelligence systems and quantum machine learning. He has several publications in software engineering and AI applications. For his 10 years in the field he has a demonstrated history of working in automotive software engineering and in the area of the higher education. He is skilled in system and software architectures, operating systems, C and C++ development, autonomous driving and computer graphics. He is currently working as a Lead C++ Developer in VMware, developing its core software functionalities. He is also a lecturer at the Technical University of Sofia. He was previously a Technical Professional and software architect in the CRE and ADAS departments of Visteon Corporation, working closely with both OEMs and development teams. 58 | 59 | **Stanimir Lukanov** 60 | is a C++ expert, software tech lead and architect at VMWare. He has more than 15 years of professional experience in writing efficient and robust C++ enterprise code. Stanimir is a member of the Bulgarian National Body which is part of The C++ Standards Committee (JTC1/SC22/WG21). His interests are in the area of software security for distributed enterprise software systems. Since 2017 he has worked at VMWare where he currently leads a team that develops core security functionality in one of the major products in the company's portfolio. Before joining VMWare he held the position of senior software engineer at Visteon Corporation and Johnson Controls. He was responsible for defining software architecture, making code reviews, leading C++ training and delivering fast and robust C++ code for real-time automotive embedded systems. 61 | -------------------------------------------------------------------------------- /Chapter 10/coroutine_socket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace std; 14 | 15 | constexpr size_t BUF_SIZE = 256; 16 | constexpr auto PORT = 8080; 17 | 18 | template 19 | struct Generator { 20 | struct promise_type { 21 | promise_type() = default; 22 | ~promise_type() = default; 23 | 24 | auto get_return_object() { 25 | return Generator{ 26 | handle_type::from_promise(*this) 27 | }; 28 | } 29 | auto initial_suspend() { 30 | return suspend_always{}; 31 | } 32 | auto final_suspend() noexcept { 33 | return suspend_always{}; 34 | } 35 | 36 | auto return_void() { 37 | return suspend_never{}; 38 | } 39 | 40 | auto yield_value(const T value) { // {1} 41 | currValue = value; 42 | return suspend_always{}; 43 | } 44 | 45 | void unhandled_exception() { 46 | exit(EXIT_FAILURE); // {2} 47 | } 48 | 49 | T currValue; 50 | }; 51 | 52 | private: 53 | using handle_type = coroutine_handle; 54 | handle_type c_routine; 55 | 56 | public: 57 | explicit Generator(handle_type handle) : 58 | c_routine(handle) {} // {3} 59 | 60 | ~Generator() { 61 | if (c_routine) c_routine.destroy(); // {4} 62 | } 63 | Generator(const Generator&) = delete; 64 | Generator& operator = (const Generator&) = delete; 65 | Generator(Generator&& other) noexcept : 66 | c_routine(other.c_routine) { 67 | other.c_routine = {}; 68 | } 69 | Generator& operator = (Generator&& other) noexcept { 70 | if (this == &other) 71 | return *this; 72 | if (c_routine) 73 | c_routine.destroy(); 74 | c_routine = other.c_routine; 75 | other.c_routine = {}; 76 | return *this; 77 | } 78 | 79 | optional operator()() { 80 | c_routine.resume(); 81 | if (c_routine.done()) { 82 | return nullopt; 83 | } 84 | return c_routine.promise().currValue; 85 | } 86 | }; 87 | 88 | Generator send_to(int sockfd, 89 | string_view buffer, 90 | auto servaddr) noexcept { 91 | for (;;) { 92 | auto value = sendto(sockfd, 93 | buffer.data(), 94 | buffer.size(), 95 | MSG_DONTWAIT, 96 | (const struct sockaddr*) 97 | &servaddr, 98 | sizeof(servaddr)); // {6} 99 | 100 | co_yield value; // {7} 101 | } 102 | } 103 | 104 | Generator recv_from(int sockfd, 105 | auto clntaddr, 106 | size_t buf_size = BUF_SIZE) 107 | noexcept { 108 | socklen_t len = sizeof(struct sockaddr_in); 109 | array tmp_buf = {}; 110 | for (;;) { 111 | recvfrom(sockfd, 112 | tmp_buf.data(), 113 | tmp_buf.size(), 114 | MSG_DONTWAIT, 115 | (struct sockaddr*)&clntaddr, 116 | &len); 117 | co_yield tmp_buf.data(); // {8} 118 | } 119 | } 120 | 121 | int main() { 122 | auto sockfd = 0; 123 | 124 | if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 125 | const auto ecode{ make_error_code(errc{errno}) }; 126 | cerr << "Error opening shm region"; 127 | system_error exception{ ecode }; 128 | throw exception; 129 | } 130 | 131 | auto server = jthread([&sockfd] { 132 | struct sockaddr_in servaddr = { 0 }; 133 | servaddr.sin_family = AF_INET; 134 | servaddr.sin_addr.s_addr = INADDR_ANY; 135 | servaddr.sin_port = htons(PORT); 136 | 137 | if (bind(sockfd, 138 | (const struct sockaddr*)&servaddr, 139 | sizeof(struct sockaddr_in)) < 0) { 140 | perror("Bind failed"); 141 | exit(EXIT_FAILURE); 142 | } 143 | 144 | cout << "\nsend_to():\n"; 145 | string_view message{ "This is a test!" }; 146 | auto sender = send_to(sockfd, message, servaddr); // {9} 147 | for (int i = 1; i <= 10; i++) { 148 | auto sentData = sender(); 149 | cout << i << " Bytes sent: " 150 | << *sentData << endl; // {10} 151 | } 152 | }); 153 | 154 | auto client = jthread([&sockfd] { 155 | cout << "\nrecv_from():\n" << endl; 156 | struct sockaddr_in clntaddr = { 0 }; 157 | auto receiver = recv_from(sockfd, clntaddr); 158 | for (auto i = 1; i <= 10; i++) { 159 | auto recvData = receiver(); 160 | cout << i << " Message received: " 161 | << *recvData << endl; // {11} 162 | } 163 | }); 164 | 165 | server.join(); 166 | client.join(); 167 | close(sockfd); 168 | return 0; 169 | } -------------------------------------------------------------------------------- /Chapter 7/udp_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | constexpr auto PORT = 8072; 14 | constexpr auto BUF_SIZE = 16; 15 | 16 | auto die_roll() { 17 | auto min = 1; 18 | auto max = 6; 19 | auto roll = rand() % (max - min + 1) + min; 20 | return roll; 21 | } 22 | 23 | void process_creator() { 24 | auto sockfd = 0; 25 | array buffer{}; 26 | string_view stop{ "No more requests!" }; 27 | string_view request{ "Throw dice!" }; 28 | struct sockaddr_in servaddr {}; 29 | struct sockaddr_in cliaddr {}; 30 | 31 | servaddr.sin_family = AF_INET; // {1} 32 | servaddr.sin_addr.s_addr = INADDR_ANY; 33 | servaddr.sin_port = htons(PORT); 34 | 35 | if (int pid = fork(); pid == 0) { 36 | // Child 37 | if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) 38 | < 0) { 39 | const auto ecode 40 | { make_error_code(errc{errno}) }; 41 | cerr << "Error opening socket!"; 42 | system_error exception{ ecode }; 43 | throw exception; 44 | } // {2} 45 | 46 | if (bind(sockfd, 47 | (const struct sockaddr*)&servaddr, 48 | sizeof(servaddr)) < 0) { 49 | const auto ecode 50 | { make_error_code(errc{errno}) }; 51 | cerr << "Bind failed!"; 52 | system_error exception{ ecode }; 53 | throw exception; 54 | } // {3} 55 | 56 | socklen_t len = sizeof(cliaddr); 57 | for (;;) { 58 | if (auto bytes_received = 59 | recvfrom(sockfd, buffer.data(), 60 | buffer.size(), 61 | MSG_WAITALL, 62 | (struct sockaddr*)&cliaddr, 63 | &len); 64 | bytes_received >= 0) { // {4} 65 | buffer.data()[bytes_received] = '\0'; 66 | cout << "Request received: " 67 | << buffer.data() << endl; 68 | 69 | if (request.compare(0, 70 | bytes_received, 71 | buffer.data()) == 0) { // {5} 72 | string_view res_data 73 | { to_string(die_roll()) }; 74 | sendto(sockfd, res_data.data(), 75 | res_data.size(), 76 | MSG_WAITALL, 77 | (struct sockaddr*)&cliaddr, 78 | len); 79 | } 80 | else break; // {6} 81 | } 82 | else { 83 | const auto ecode 84 | { make_error_code(errc{errno}) }; 85 | cerr << "Receive failed!"; 86 | system_error exception{ ecode }; 87 | throw exception; 88 | } 89 | } 90 | if (auto res = close(sockfd); res == -1) { // {8} 91 | const auto ecode 92 | { make_error_code(errc{errno}) }; 93 | cerr << "Error closing socket!"; 94 | system_error exception{ ecode }; 95 | throw exception; 96 | } 97 | exit(EXIT_SUCCESS); 98 | } 99 | else if (pid > 0) { 100 | // Parent 101 | int32_t dice_rolls; 102 | cout << "Choose a number of dice throws" 103 | << " between 1 and 256." << endl; 104 | cin >> dice_rolls; 105 | if (dice_rolls <= 0 and dice_rolls > 256) { // {9} 106 | dice_rolls = 0; 107 | } 108 | 109 | if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) 110 | < 0) { 111 | const auto ecode 112 | { make_error_code(errc{errno}) }; 113 | cerr << "Error opening socket!"; 114 | system_error exception{ ecode }; 115 | throw exception; 116 | } // {10} 117 | 118 | socklen_t len = 0; 119 | for (auto i = 1; i <= dice_rolls; i++) { // {11} 120 | if (auto b_sent = sendto(sockfd, 121 | request.data(), 122 | request.size(), 123 | MSG_DONTWAIT, 124 | (const struct sockaddr*)&servaddr, 125 | sizeof(servaddr)); b_sent >= 0) { // {12} 126 | } 127 | else { 128 | const auto ecode 129 | { make_error_code(errc{errno}) }; 130 | cerr << "Send request failed!"; 131 | system_error exception{ ecode }; 132 | throw exception; 133 | } 134 | if (auto b_recv = 135 | recvfrom(sockfd, 136 | buffer.data(), 137 | buffer.size(), 138 | MSG_WAITALL, 139 | (struct sockaddr*)&servaddr, 140 | &len); b_recv > 0) { // {13} 141 | buffer.data()[b_recv] = '\0'; 142 | cout << "Dice roll result for throw number " 143 | << i << " is " 144 | << buffer.data() << endl; 145 | } 146 | } 147 | sendto(sockfd, 148 | stop.data(), 149 | stop.size(), 150 | MSG_CONFIRM, 151 | (const struct sockaddr*)&servaddr, 152 | sizeof(servaddr)); // {14} 153 | 154 | if (auto res = close(sockfd); res == -1) { 155 | const auto ecode 156 | { make_error_code(errc{errno}) }; 157 | cerr << "Error closing socket!"; 158 | system_error exception{ ecode }; 159 | throw exception; // {15} 160 | } 161 | exit(EXIT_SUCCESS); 162 | } 163 | else { 164 | const auto ecode 165 | { make_error_code(errc{errno}) }; 166 | cerr << "Process creation failed!"; 167 | system_error exception{ ecode }; 168 | throw exception; // {16} 169 | } 170 | } 171 | 172 | int main() { 173 | process_creator(); 174 | return 0; 175 | } 176 | --------------------------------------------------------------------------------