From e0fb39c92ae071a444cb92fbb2d0c1977fa7af51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Sat, 7 Sep 2019 00:00:03 +0000 Subject: [PATCH] userland: move more multithreading from cpp-cheat! Convert infinite_loop.c into loop.c. Keep all examples fast by default! --- README.adoc | 17 +++++- path_properties.py | 2 +- userland/c/atomic.c | 50 ++++++++++++++++ userland/c/{infinite_loop.c => loop.c} | 16 +++-- userland/cpp/atomic.cpp | 2 +- userland/cpp/thread_get_id.cpp | 29 ++++++++++ userland/cpp/thread_hardware_concurrency.cpp | 2 + userland/cpp/thread_return_value.cpp | 61 ++++++++++++++++++++ 8 files changed, 165 insertions(+), 14 deletions(-) create mode 100644 userland/c/atomic.c rename userland/c/{infinite_loop.c => loop.c} (73%) create mode 100644 userland/cpp/thread_get_id.cpp create mode 100644 userland/cpp/thread_return_value.cpp diff --git a/README.adoc b/README.adoc index c8fbafd..0718021 100644 --- a/README.adoc +++ b/README.adoc @@ -12845,7 +12845,7 @@ Programs under link:userland/c/[] are examples of https://en.wikipedia.org/wiki/ *** File IO **** link:userland/c/file_write_read.c[] * Fun -** link:userland/c/infinite_loop.c[] +** link:userland/c/loop.c[] ==== malloc @@ -12949,6 +12949,16 @@ It then walks over every page and writes a value in it to ensure that it is used Algorithm used by the OOM: https://unix.stackexchange.com/questions/153585/how-does-the-oom-killer-decide-which-process-to-kill-first +==== C multithreading + +Added in C11! + +* link:userland/c/atomic.c[]: `atomic_int` and `thrd_create` + +Bibliography: + +* https://stackoverflow.com/questions/3908031/how-to-multithread-c-code/52453354#52453354 + ==== GCC C extensions ===== C empty struct @@ -12986,8 +12996,6 @@ Programs under link:userland/cpp/[] are examples of https://en.wikipedia.org/wik ** link:userland/cpp/template.cpp[]: basic example ** link:userland/cpp/template_class_with_static_member.cpp[]: https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template ** link:userland/cpp/if_constexpr.cpp[]: C++17 `if constexpr` -*** https://stackoverflow.com/questions/12160765/if-else-at-compile-time-in-c/54647315#54647315 -*** https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co [[cpp-multithreading]] ==== C++ multithreading @@ -12995,6 +13003,8 @@ Programs under link:userland/cpp/[] are examples of https://en.wikipedia.org/wik * https://en.cppreference.com/w/cpp/header/thread[``] ** link:userland/cpp/count.cpp[] Exemplifies: `std::this_thread::sleep_for` ** link:userland/cpp/thread_hardware_concurrency.cpp[] `std::thread::hardware_concurrency` +** link:userland/cpp/thread_get_id.cpp[] `std::thread::get_id` +** link:userland/cpp/thread_return_value.cpp[]: how to return a value from a thread * https://en.cppreference.com/w/cpp/header/atomic[``]: <> 32 "Atomic operations library" ** link:userland/cpp/atomic.cpp[] @@ -13093,6 +13103,7 @@ Bibliography: https://stackoverflow.com/questions/6988487/what-does-the-brk-syst The following sections are related to multithreading in userland: * language topics: +** <> ** <> ** <> * ISA topics: diff --git a/path_properties.py b/path_properties.py index 8f53ae3..5d60a1c 100644 --- a/path_properties.py +++ b/path_properties.py @@ -475,6 +475,7 @@ path_properties_tuples = ( }, { 'abort.c': {'signal_received': signal.Signals.SIGABRT}, + 'atomic.c': {'baremetal': False}, 'assert_fail.c': {'signal_received': signal.Signals.SIGABRT}, # This has complex failure modes, too hard to assert. 'smash_stack.c': {'skip_run_unclassified': True}, @@ -483,7 +484,6 @@ path_properties_tuples = ( 'false.c': {'exit_status': 1}, 'file_write_read.c': {'baremetal': False}, 'getchar.c': {'interactive': True}, - 'infinite_loop.c': {'test_run_args': {'userland_args': '1 10'}}, 'malloc_max.c': {'disrupts_system': True}, 'return1.c': {'exit_status': 1}, 'return2.c': {'exit_status': 2}, diff --git a/userland/c/atomic.c b/userland/c/atomic.c new file mode 100644 index 0000000..d826951 --- /dev/null +++ b/userland/c/atomic.c @@ -0,0 +1,50 @@ +/* https://cirosantilli.com/linux-kernel-module-cheat#c-multithreading */ + +#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) +#include +#include +#include +#include +#include + +atomic_int acnt; +int cnt; +size_t niters; + +int f(void *thr_data) { + (void)thr_data; + for (size_t i = 0; i < niters; ++i) { + ++cnt; + ++acnt; + } + return 0; +} +#endif + +int main(int argc, char **argv) { +#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + size_t nthreads; + thrd_t *threads; + if (argc > 1) { + nthreads = strtoull(argv[1], NULL, 0); + } else { + nthreads = 2; + } + if (argc > 2) { + niters = strtoull(argv[2], NULL, 0); + } else { + niters = 10; + } + threads = malloc(sizeof(thrd_t) * nthreads); + for(size_t i = 0; i < nthreads; ++i) + thrd_create(threads + i, f, NULL); + for(size_t i = 0; i < nthreads; ++i) + thrd_join(threads[i], NULL); + free(threads); + printf("atomic %u\n", acnt); + printf("non-atomic %u\n", cnt); +#else + (void)argc; + (void)argv; +#endif +} diff --git a/userland/c/infinite_loop.c b/userland/c/loop.c similarity index 73% rename from userland/c/infinite_loop.c rename to userland/c/loop.c index eb7e314..c6a16d6 100644 --- a/userland/c/infinite_loop.c +++ b/userland/c/loop.c @@ -3,7 +3,7 @@ * Loop and print an integer whenever a period is reached: * * .... - * ./infinite_loop [period=100000000 [max=0]] + * ./loop.out [max=10 [period=1]] * .... * * * period: period for printing integers to stdout @@ -19,27 +19,25 @@ int main(int argc, char **argv) { uintmax_t i, j, period, max; - int max_given; if (argc > 1) { - period = strtoumax(argv[1], NULL, 10); + max = strtoumax(argv[1], NULL, 10); } else { - period = 100000000; + max = 10; } if (argc > 2) { - max = strtoumax(argv[2], NULL, 10); - max_given = 1; + period = strtoumax(argv[2], NULL, 10); } else { - max_given = 0; + period = 1; } i = 0; j = 0; while (1) { - i++; if (period != 0 && i % period == 0) { printf("%ju\n", j); j++; } - if (max_given && i == max) + i++; + if (i == max) break; } } diff --git a/userland/cpp/atomic.cpp b/userland/cpp/atomic.cpp index 8daba6a..c891d7f 100644 --- a/userland/cpp/atomic.cpp +++ b/userland/cpp/atomic.cpp @@ -79,7 +79,7 @@ int main(int argc, char **argv) { if (argc > 2) { niters = std::stoull(argv[2], NULL, 0); } else { - niters = 10000; + niters = 10; } std::vector threads(nthreads); for (size_t i = 0; i < nthreads; ++i) diff --git a/userland/cpp/thread_get_id.cpp b/userland/cpp/thread_get_id.cpp new file mode 100644 index 0000000..3d45e97 --- /dev/null +++ b/userland/cpp/thread_get_id.cpp @@ -0,0 +1,29 @@ +// https://cirosantilli.com/linux-kernel-module-cheat#cpp-multithreading +// +// Spawn some threads and print their ID. +// +// On Ubuntu 19.04, they ar large possibly non-consecutive numbers. + +#include +#include +#include +#include + +std::mutex mutex; + +void myfunc(int i) { + mutex.lock(); + std::cout << i << " " << std::this_thread::get_id() << std::endl; + mutex.unlock(); +} + +int main() { + std::cout << "main " << std::this_thread::get_id() << std::endl; + std::vector threads; + for (unsigned int i = 0; i < 4; ++i) { + threads.push_back(std::thread(myfunc, i)); + } + for (auto& thread : threads) { + thread.join(); + } +} diff --git a/userland/cpp/thread_hardware_concurrency.cpp b/userland/cpp/thread_hardware_concurrency.cpp index 4b4afda..2c21f70 100644 --- a/userland/cpp/thread_hardware_concurrency.cpp +++ b/userland/cpp/thread_hardware_concurrency.cpp @@ -1,3 +1,5 @@ +// https://cirosantilli.com/linux-kernel-module-cheat#cpp-multithreading +// // http://stackoverflow.com/questions/150355/programmatically-find-the-number-of-cores-on-a-machine // // Not affected by taskset: https://stackoverflow.com/questions/1006289/how-to-find-out-the-number-of-cpus-using-python/55423170#55423170 diff --git a/userland/cpp/thread_return_value.cpp b/userland/cpp/thread_return_value.cpp new file mode 100644 index 0000000..988eac1 --- /dev/null +++ b/userland/cpp/thread_return_value.cpp @@ -0,0 +1,61 @@ +// http://stackoverflow.com/questions/7686939/c-simple-return-value-from-stdthread +// http://stackoverflow.com/questions/28950835/c-error-no-type-named-type-in-class-stdresult-ofvoid-stdunordered +// http://stackoverflow.com/questions/21048906/stdthread-pass-by-reference-calls-copy-constructor +// http://stackoverflow.com/questions/8299545/passing-arguments-to-thread-function +// http://stackoverflow.com/questions/5116756/difference-between-pointer-and-reference-as-thread-parameter + +#include +#include +#include +#include +#include + +int myfunc(int i) { + return i + 1; +} + +void myfunc_reference(int& i) { + i = myfunc(i); +} + +int main() { + unsigned int nthreads = 4; + std::vector inputs{1, 2, 3, 4}; + std::vector outputs_expect{2, 3, 4, 5}; + + // future and sync. Nirvana. When you are not fighting to death with types: + // https://stackoverflow.com/questions/10620300/can-stdasync-be-use-with-template-functions + { + std::vector> futures(nthreads); + std::vector outputs(nthreads); + for (decltype(futures)::size_type i = 0; i < nthreads; ++i) { + futures[i] = std::async( + myfunc, + inputs[i] + ); + } + for (decltype(futures)::size_type i = 0; i < nthreads; ++i) { + outputs[i] = futures[i].get(); + } + assert(outputs_expect == outputs); + } + + // Reference arguments. + // + // Annoying because requires: + // + // - wrapping the return function to accept references + // - keeping an array of outputs + // - std::ref + { + std::vector threads(nthreads); + std::vector inouts(inputs); + for (decltype(threads)::size_type i = 0; i < nthreads; ++i) { + threads[i] = std::thread(myfunc_reference, std::ref(inouts[i])); + } + for (auto& thread : threads) { + thread.join(); + } + assert(outputs_expect == inouts); + } +}