C++20: semaphores

There are two types of semaphores:

  • std::binary_semaphore
  • std::counting_semaphore

Semaphore alllows multiple access to shared resources. Unlike std::mutex, semaphore can be locked in one thread, and unlocked in another.

std::binary_semaphore

std::binary_semaphore is just alias for std::counting_semaphore.

using binary_semaphore = std::counting_semaphore<1>;

std::counting_semaphore

Semaphore has in implementation counter, which defines, how many threads can enter to locked section.

For example:

std::counting_semaphore<3> sem{ 0 };

In this case, maximum counter values can be 3. And counter is initialized to 0.

acquire() – decrements value, and if counter is already 0, will block until counter is larger than 0.

release() – increments counter

For example:

#include <iostream>

#include <thread>

#include <semaphore>

std::counting_semaphore<1> sem{ 0 };

void thread_func1()

{

sem.acquire();

std::cout << "analyze data()" << std::endl;

}

void thread_func2()

{

std::cout << "getting data()" << std::endl;

sem.release();

}

int main()

{

std::jthread thread1{ thread_func1 };

std::jthread thread2{ thread_func2 };

}

In this case, first we need to wait for data in first thread.

C++20: std::jthread

auto join() in destructor

In C++20 there is new thread type std::jthread, which means joined thread. Previous std::thread if destructor will be called without join() function, in this case will be called std::terminate(). But in std::jthread in destructor is called join() function.

std::jthread object is located in <thread> header.

void _internal_thread()
{

}

int main()
{
	std::jthread _thread{ _internal_thread };
	// no exception here
}

std::stop_token

Also in std::jthread there is possibility to stop thread when it’s needed. Thread function can have parameter std::stop_token which can be checked in while loop inside thread.

For example:

#include <iostream>
#include <thread>

using namespace std::chrono_literals;

void _internal_thread(std::stop_token stop)
{
	while (!stop.stop_requested())
	{
		std::cout << "work..." << std::endl;
		std::this_thread::sleep_for(0.5s);
	}
}

int main()
{
	std::jthread _thread{ _internal_thread };
	std::this_thread::sleep_for(2s);
}

In case when we need to pass additional parameters to thread function, std::stop_token need to be first parameter.

void _internal_thread(std::stop_token stop, int x) // ok
{
	while (!stop.stop_requested())
	{
		std::cout << "work..." << std::endl;
		std::this_thread::sleep_for(0.5s);
	}
}

// error
void _internal_thread(int x, std::stop_token stop)
{
	while (!stop.stop_requested())
	{
		std::cout << "work..." << std::endl;
		std::this_thread::sleep_for(0.5s);
	}
}

If needed we can get std::stop_source and call request_stop() when it needed.

std::jthread _thread{ _internal_thread, 10 };

bool result = _thread.get_stop_source().request_stop();

std::stop_callback

Also there is new std::stop_callback object, which can execute in case when request_stop() is called.

For example:

#include <iostream>
#include <thread>

using namespace std::chrono_literals;

void _internal_thread(std::stop_token stop, int x)
{
	while (!stop.stop_requested())
	{
		std::cout << "work..." << std::endl;
		std::this_thread::sleep_for(0.5s);
	}
}

int main()
{
  std::jthread _thread{ _internal_thread, 10 };

  std::stop_callback 
  _stop{_thread.get_stop_token(), []() {
     std::cout << "show stop" << std::endl;
  }};

  _thread.get_stop_source().request_stop();

  std::this_thread::sleep_for(2s);
}

C++17: global changes

Exception specification as part of the type system

A prvalue of type “pointer to noexcept function” can be converted to a prvalue of type “pointer to function”. The result is a pointer to the function. A prvalue of type “pointer to member of type noexcept function” can be converted to a prvalue of type “pointer to member of type function”. The result points to the member function.

Example:

Continue reading “C++17: global changes”

C++17: Guaranted copy elision

From C++17 compilers are required to omit copy/move construction resulting in zero-copy pass-by-value semantics. Objects will be created directly in storage where they would copy/move.

  • In return statement when operand is prvalue:

  • In the initialization of a variable, when initialization expression is a prvalue of the same type:

C++17 core language specification of prvalues and temporaries is fundamentally different from that of the earlier C++ revisions: there is no longer a temporary to copy/move from. Another way to describe C++17 mechanics is “unmaterialized value passing”: prvalues are returned and used without ever materializing a temporary.

Continue reading “C++17: Guaranted copy elision”

C++17: Deprecated features

Deprecate redeclaration of static constexpr class members

When inline variables are presented in C++17, static constexpr redeclaration is not needed.

 

Deprecate C library headers

List of C libraries which are deprecated from C++17:

<ccomplex> – simply deleting the requirement for a <ccomplex> header is simpler, and may be better, than making it optional. Of course an implementation would not need to change in order to conform to the new state of affairs, but programs that use <ccomplex> instead of <complex> would cease to be strictly conforming.

<ctgmath> – simply includes header <ccomplex> and <cmath>

<cstdalign> – mentioned as pointless in C++

<cstdbool> – mentioned as pointless in C++

Continue reading “C++17: Deprecated features”

C++17: std::invoke & std::apply

std::invoke

std::invoke is used for calling function objects, like lambdas, methods, functions without knowing about type this function.

For example , before we needed to use pointer to function, pointer to method:

Continue reading “C++17: std::invoke & std::apply”

C++17: std::shared_mutex, interface_sizes, void_t, std::bool_constant, logical operation metafunctions

Class shared_mutex

On some platforms shared_mutex is more effecient then shared_timed_mutex.

In C++17 was added shared_mutex.

Example of usage:

Continue reading “C++17: std::shared_mutex, interface_sizes, void_t, std::bool_constant, logical operation metafunctions”

C++17: Modifications to existing features (Part 2)

Construction for values of fixed enums

Before C++17, variable of fixed enumeration was initialized like this:

if we will try to initialize like this:

we will have error: cannot convert ‘int’ to ‘E’ in initialization.

In C++17 it’s allowed to build.

Continue reading “C++17: Modifications to existing features (Part 2)”

C++17: Modifications to existing features (Part 1)

Single-argument static_assert

In C++17 static_assert message was changed to optional, and it’s possible create static_assert without message.

For example:

Continue reading “C++17: Modifications to existing features (Part 1)”