Showing posts with label concurrency. Show all posts
Showing posts with label concurrency. Show all posts

Sunday, February 16, 2014

C++14 is here

Today we finished the Issaquah meeting from the ISO C++ Standards Committee.


C++14 International Standard

The most relevant news is that we have approved C++14 for DIS. Formally this means that we send the document to ISO for a final ballot. There is an unlikely possibility that and additional ballot is needed. In practice, it means that C++14 is ready. There is a huge possibility that it is published during 2014 and a small possibility that is published in early 2015. So, this time we got it for the year we were expecting to. Wow!

More on C++ Technical Specifications

We also approved the initial drafts for some technical specifications. In particular:
  • A working paper of the Concepts Lite technical specification. In essence, this will allow to constrain templates with predicates. When concepts are out there, you will get (finally) meaningful error messages for most generic code.
  • A working paper for the Parallelism technical specification.
  • A working paper for the Concurrency technical specification.
We decided to have one more technical specification on Transactional Memory.

A significant progress has been made for the Library Fundamentals technical specification.

Besides, the File System technical specification has gone through the resolution of many national body comments. It is highly likely that we will have a final ballot of this technical specification after the next committee meeting (to be held in June in Switzerland).

C++14 last minute modifications

You may be aware of the status of C++14, so here you will only find an incomplete list of changes.

First of all, many corrections from defect reports and comments issued by National Bodies have been made, both to the library and to the language. In this post I do not enumerate those minor changes.

Perhaps, you already have seen the excelent explanation from Stephan T. Lavavej about why we should not use the rand() function. If you didn't, please have a look at this video. C++14 is not deprecating rand() but it will contain a strong note discouraging the use of rand() because of its questionable quality and performance.

A new metafunction alias called tuple_element_t has been introduced. This allows a simpler writing when extracting a type from a tuple, and

typename tuple_element<I, tuple<T...>>::type

becomes

tuple_element_t<I,tuple<T...>>

There are also a number of chages related to concurrency in C++14:

  • shared_timed_mutex is the new name for the old shared_mutex. This makes naming of mutexes more consistent and leaves room for a future extension.
  • A set of clarifications has been introduced about the interactions of a signal handler and threads that are running.
  • The concept of lock-free execution has also been clarified.

The Library Fundamentals Technical Specification

A collection of components has been incorporated in the Library Fundamentals TS. Some of them are:
    • string_view: A ligthweight type to reference a sequence of inmutable char-like objects. Simliar types have been previously developed by Google, LLVM, and Bloomberg, justifying the need for standardization.
    • any: A type safe container for a single value. This class is heavily based on boost::any.
    • A set of facilities for allocators that make easy that allocator is not part of types.
    • Faster string search algorithms (Boyer-Moore and Boyer-Moore-Horspool).
    • sample(): An algorithm for randomly sampling elements from a sequence.
    • A set of variable templates that allow a simple way of spelling type traits (is_arithmetic_v<T> instead of is_arithmetic<T>::value).
    • apply(): Allows applyinf an arbitrary function to a tuple.
    • A reformulation of iterator_traits so that the do not generate a hard error when its template argument does not have the expected member types. This allows to use this trait in an SFINAE context.
    • A reformulation of the type trait common_type to avoid a hard error when there is no common type. This allows to use this trait in an SFINAE context.
    • shared_ptr extensions to support arrays (this allows both shared_ptr<T[]> and shared_ptr<T[N]>).
    • A library solution for performing byte ordering conversions (through hton<T>(T x) and ntoh<T>(T x)). These functions were originally planned for the networking technical specification, but have been finally moved here.
    • optional: A class to support types that may or may not hold a value. This type is highly influenced by boost::opetional.
    • invocation_type and raw_invocation_type: Type traits for callable objects.

The Parallelism Technical Specification

An initial working draft has been approved. This technical specification contains a library interface for a set of algorithms that can be excuted under different execution policies (including sequential, parallel, vector).

The Concurrency Technical Specification

An initial working draft has been approved.

The techcnical specification will include executors, which are objects that can execute unit of work in the form of function objects. The concept of thread pool is included here.

Besides, this TS also includes improvements to std::future.




Tuesday, July 2, 2013

Passing arguments to std::thread

The C++11 standard library has a powerful mecanism for managing threads: class std::thread, which follows the principle of providing a simple interface for doing simple things.

Let's start with a very simple and useless example:


void f() {
  do_some_work();
}

void g() {
  do_some_more_work();
}

void h() {
  std::thread t1{f}; // Launch f in a thread
  std::thread t2{g}; // Launch g in a thread

  t1.join(); // Blocks until t1 finishes
  t2.join(); // Blocks until t2 finishes
}

I say it is useless, beause for most uses of concurrency one needs to send arguments to the threads. However it is simple to follow. Function h() launches two threads: t1 and t2. Thread t1 runs function f() and thread t2 runs function g().

Now let's add to our example arguments to be passed to our threads.

void f(int n) {
  for (int i=0; i<n; ++i) {
    do_some_work();
  }
}

void g(int a, int b) {
  for (int i=a; i<=b; ++i) {
    do_some_more_work();
  }
}

void h() {
  std::thread t1{f, 10}; // Launch f(10) in a thread
  std::thread t2{g, 15, 25}; // Launch g(15,25) in a thread

  t1.join(); // Blocks until t1 finishes
  t2.join(); // Blocks until t2 finishes
}

Now, we can launch threads with function calls to functions with any number of arguments in a simple way. At this point, you may remember how long you spent doing type casts with your old good pthreads or similar API.

Looks wonderful, isn't it? Well, now is when the tricky part comes into scene.

What is the real signature of std::thread constructor?

template <typename F, typename ... Args> 
explicit thread(F&& f, Args&&... args);

What area this ellipsis in this template constructor? This is a variadic template, which is a C++11 mechanism to provide a template with a variable number of template arguments. You can see this declaration as the following family of declarations:

template <typename F, typename A1> 
explicit thread(F&& f, A1&& a1);

template <typename F, typename A1, typename A2> 
explicit thread(F&& f, A1&& a1, A2&& a2);

template <typename F, typename A1, typename A2, typename A3> 
explicit thread(F&& f, A1&& a1, A2&& a2, A3&& a3);

And so on.

If you are not (still) quite used to C++11, you may be thinking about what are those double ampersands. You probably already know that, in C++ there is no reference-to-reference. So, what is this? In general, a double ampersand denotes an r-value reference. However, in combination with templates they have some special rules. Scott Meyers named this combination a universal reference. In practice, this means that if you pass an l-value (e.g. a variable) you ar passing by reference, but if you pass an r-value (e.g. an expression) you are passing by value.

With this in mind, we can pass variables to a thread:

void f(int n) {
  for (int i=0; i<n; ++i) {
    do_some_work();
  }
}

void g(int a, int b) {
  for (int i=a; i<=b; ++i) {
    do_some_more_work();
  }
}

void h() {
  int x = 10, y = 15, z = 25;
  std::thread t1{f, x}; // Launch f(x) in a thread
  std::thread t2{g, y, z}; // Launch g(y,z) in a thread

  t1.join(); // Blocks until t1 finishes
  t2.join(); // Blocks until t2 finishes
}

Then. Can I use functions tanking arguments by reference? Well, yes and no. I mean, it is not so easy.

Let's start with a broken example:

void f(int & n) {
  ++n;
}

void g() {
  int x =10;
  std::thread t1(f, x);
  t1.join();
  std::cout << x << std::endl;
}

If you compile that piece of code, you may find that you get a compiler error. What is the problem?

The standard requires that a copy of your parameter is passed to the new thread. Thus it will only work with parameters either by value or by const reference, but not by reference.

Magically, the solution comes with std::ref() (and its colleague std::cref). Both are means for making a reference_wrapper from a variable.

void f(int & n) {
  ++n;
}

void g() {
  int x =10;
  std::thread t1(f, std::ref(x));
  t1.join();
  std::cout << x << std::endl;
}

So, remember it. If you want to pass something by reference to a thread, you need to use std::ref(). If you want to pass something by const reference to a thread, you need to use std::cref().

However, in most cases there are better approaches than passing references to threads. Please, think if you absolutely need to pass a reference to a thread before doing so. They could be a source for data races or lead to strange dangling references if the referenced value happens to be destroyed. In summary, you should be very sure of what you are doing before passing by reference to a thread.

Next time, I will try to write something about returning values frome threads.