Top Banner
Threadsafe signals in C++ Dmitry Koplyarov, 2016
74

Дмитрий Копляров , Потокобезопасные сигналы в C++

Apr 16, 2017

Download

Software

Sergey Platonov
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Дмитрий Копляров , Потокобезопасные сигналы в C++

Threadsafe signals in C++Dmitry Koplyarov, 2016

Page 2: Дмитрий Копляров , Потокобезопасные сигналы в C++

Some remarks about the code in these slides● The namespace names are omitted

● Sometimes the const references are omitted

● The example code has poor design for simplicity purposes

● The example classes only have methods that relate to signals

● TypePtr is a typedef for shared_ptr<Type>

Page 3: Дмитрий Копляров , Потокобезопасные сигналы в C++

boost::signals2

Page 4: Дмитрий Копляров , Потокобезопасные сигналы в C++

Declaring a boost signal

Page 5: Дмитрий Копляров , Потокобезопасные сигналы в C++

Connecting to a boost signal

Page 6: Дмитрий Копляров , Потокобезопасные сигналы в C++

Invoking a boost signal

Page 7: Дмитрий Копляров , Потокобезопасные сигналы в C++

What about the multithreaded use of boost::signals2?

Page 8: Дмитрий Копляров , Потокобезопасные сигналы в C++

Two major problems1. No natural way to connect to a signal and get the object’s state without a race

condition

2. The “disconnect” method does not guarantee that the execution flow is out of the

signal handler right after disconnecting

Page 9: Дмитрий Копляров , Потокобезопасные сигналы в C++

Two major problems1. No natural way to connect to a signal and get the object’s state without a race

condition

2. The “disconnect” method does not guarantee that the execution flow is out of the

signal handler right after disconnecting

Page 10: Дмитрий Копляров , Потокобезопасные сигналы в C++

The ProblemLet’s implement a StorageManager class that manages removable storages (USB sticks,

DVD disks, etc.)

Page 11: Дмитрий Копляров , Потокобезопасные сигналы в C++

The ProblemThere’s a race condition in the client code

Page 12: Дмитрий Копляров , Потокобезопасные сигналы в C++

The ProblemThere’s a race condition in the client code

Page 13: Дмитрий Копляров , Потокобезопасные сигналы в C++

Trying to solve itApparently, we need a mutex

Page 14: Дмитрий Копляров , Потокобезопасные сигналы в C++

Trying to solve itConnecting to a signal

Page 15: Дмитрий Копляров , Потокобезопасные сигналы в C++

Trying to solve itInvoking the signal

Page 16: Дмитрий Копляров , Потокобезопасные сигналы в C++

Drawbacks● A redundant interface with many degrees of freedom

● The StorageManager object is not actually thread-safe

● Mostly copy-pasted code to connect to any similar signal

Page 17: Дмитрий Копляров , Потокобезопасные сигналы в C++

Two major problems1. No natural way to connect to a signal and get the object’s state without a race

condition

2. The “disconnect” method does not guarantee that the execution flow is out of the

signal handler right after disconnecting

Page 18: Дмитрий Копляров , Потокобезопасные сигналы в C++

The ProblemLet’s implement a simple MediaScanner class

Page 19: Дмитрий Копляров , Потокобезопасные сигналы в C++

The Problem

Page 20: Дмитрий Копляров , Потокобезопасные сигналы в C++

Trying to solve itBoost suggests using slot_type::track() method, but we need a shared_ptr to the

tracked object

Page 21: Дмитрий Копляров , Потокобезопасные сигналы в C++

Trying to solve itClient code

Page 22: Дмитрий Копляров , Потокобезопасные сигналы в C++

Trying to solve it...and here’s the populating of the “already there” storages

Page 23: Дмитрий Копляров , Потокобезопасные сигналы в C++

WAT?● Too much code for nothing but creating of a MediaScanner object

● The interaction between MediaScanner and StorageManager objects spread

outside of the MediaScanner class

● Client code is forced to own the MediaScanner object via shared_ptr

● Again, all this code doesn’t make much sense, and is almost the same for all

similar cases

Page 24: Дмитрий Копляров , Потокобезопасные сигналы в C++

Make things easier for the client codeOK, let’s hide all the details inside the MediaScanner class

Page 25: Дмитрий Копляров , Потокобезопасные сигналы в C++

Make things easier for the client code

Page 26: Дмитрий Копляров , Потокобезопасные сигналы в C++

Looks betterClient code

Page 27: Дмитрий Копляров , Потокобезопасные сигналы в C++

Drawbacks● Requires an internal Impl class

● Still a lot of “almost copy-pasted” code to connect to a signal

Page 28: Дмитрий Копляров , Потокобезопасные сигналы в C++

Trying to get rid of the Impl classWell, there is enable_shared_from_this template in boost

Page 29: Дмитрий Копляров , Потокобезопасные сигналы в C++

Trying to get rid of the Impl classBut it does not work in the constructor, so we need a separate public method

Page 30: Дмитрий Копляров , Потокобезопасные сигналы в C++

Trying to get rid of the Impl classClient code

Page 31: Дмитрий Копляров , Потокобезопасные сигналы в C++

Drawbacks● Client code is forced to own the MediaScanner object via shared_ptr

● Client code is responsible for invoking MediaScanner::ConnectToSignals

● Still a lot of “almost copy-pasted” code to connect to a signal

Page 32: Дмитрий Копляров , Потокобезопасные сигналы в C++

Factory method

Page 33: Дмитрий Копляров , Потокобезопасные сигналы в C++

Factory method

Page 34: Дмитрий Копляров , Потокобезопасные сигналы в C++

Factory methodClient code

Page 35: Дмитрий Копляров , Потокобезопасные сигналы в C++

Drawbacks● Client code is forced to own the MediaScanner object via shared_ptr

● Still a lot of “almost copy-pasted” code to connect to a signal

Page 36: Дмитрий Копляров , Потокобезопасные сигналы в C++

Designing better signals

Page 37: Дмитрий Копляров , Потокобезопасные сигналы в C++

Designing better signalsProblem:

No natural way to connect to a signal and get the object’s state without a race

condition

Solution:

The mutex and the collection were pretty good, but all that code might be hidden

inside the “connect” method

Page 38: Дмитрий Копляров , Потокобезопасные сигналы в C++

Designing better signalsProblem:

No natural way to connect to a signal and get the object’s state without a race

condition

Solution:

The mutex and the collection were pretty good, but all that code might be hidden

inside the “connect” method

Also:

The way we obtain the object state depends on the state nature. It is different for a

collection state and for a single object state. Thus, we need an abstraction here

Page 39: Дмитрий Копляров , Потокобезопасные сигналы в C++

The Populator

Page 40: Дмитрий Копляров , Потокобезопасные сигналы в C++

The Populator

Page 41: Дмитрий Копляров , Потокобезопасные сигналы в C++

How to use

Page 42: Дмитрий Копляров , Потокобезопасные сигналы в C++

How to useThe signal constructor and the populator

Page 43: Дмитрий Копляров , Потокобезопасные сигналы в C++

How to useYou may use a lambda function as a populator instead of a separate method

Page 44: Дмитрий Копляров , Потокобезопасные сигналы в C++

How to useOr even a lambda function with an auto argument type if you use C++14

Page 45: Дмитрий Копляров , Потокобезопасные сигналы в C++

How to useInvoking signal

Page 46: Дмитрий Копляров , Потокобезопасные сигналы в C++

How to useClient code

Page 47: Дмитрий Копляров , Потокобезопасные сигналы в C++

Problem:

The “disconnect” method does not guarantee that the execution flow is out of the

signal handler right after disconnecting

Solution:

We need a way to mark the handler as “dying” and wait for the end of its

execution if necessary

Designing better signals

Page 48: Дмитрий Копляров , Потокобезопасные сигналы в C++

Problem:

The “disconnect” method does not guarantee that the execution flow is out of the

signal handler right after disconnecting

Solution:

We need a way to mark the handler as “dying” and wait for the end of its

execution if necessary

Also:

The SignalConnection destructor should wait on some object, and the handler

should lock that object for the execution time

Designing better signals

Page 49: Дмитрий Копляров , Потокобезопасные сигналы в C++

The LifeTokenLifeToken

an object that controls the handler lifetime, and has a blocking Release method

LifeToken::Checker

stored inside the handler info and references the LifeToken

LifeToken::Checker::ExecutionGuard

a local object that locks the LifeToken for the handler execution time

Page 50: Дмитрий Копляров , Потокобезопасные сигналы в C++

The LifeToken

Page 51: Дмитрий Копляров , Потокобезопасные сигналы в C++

The LifeToken

Page 52: Дмитрий Копляров , Потокобезопасные сигналы в C++

The LifeToken

Page 53: Дмитрий Копляров , Потокобезопасные сигналы в C++

The LifeToken

Page 54: Дмитрий Копляров , Потокобезопасные сигналы в C++

The LifeToken

Page 55: Дмитрий Копляров , Потокобезопасные сигналы в C++

What is inside the LifeToken?● Some OS primitive for waiting (I use Mutex in these slides)

● Alive flag

● Lock counter (omitted here to make the code simpler)

● ExecutionGuard constructor should block the OS primitive

● ExecutionGuard destructor should unblock the OS primitive

● LifeToken destructor should wait until the OS primitive is unblocked

Page 56: Дмитрий Копляров , Потокобезопасные сигналы в C++

What is inside the LifeToken?

Page 57: Дмитрий Копляров , Потокобезопасные сигналы в C++

What is inside the LifeToken?

Page 58: Дмитрий Копляров , Потокобезопасные сигналы в C++

What is inside the LifeToken?

Page 59: Дмитрий Копляров , Потокобезопасные сигналы в C++

What is inside the LifeToken?

Page 60: Дмитрий Копляров , Потокобезопасные сигналы в C++

What is inside the LifeToken?

Page 61: Дмитрий Копляров , Потокобезопасные сигналы в C++

So, what does MediaScanner with these signals look like?

Page 62: Дмитрий Копляров , Потокобезопасные сигналы в C++

New MediaScanner

Page 63: Дмитрий Копляров , Потокобезопасные сигналы в C++

New MediaScanner

Page 64: Дмитрий Копляров , Потокобезопасные сигналы в C++

New MediaScannerClient code

Page 65: Дмитрий Копляров , Потокобезопасные сигналы в C++

SummaryThere are two key features that result in much clearer client code:

1. Populators send the object state via the same handler that is used for further

updates tracking

2. A blocking disconnect method results in better incapsulation, without a need for a

nested Impl class or factory methods

Page 66: Дмитрий Копляров , Потокобезопасные сигналы в C++

Is there a ready-to-use solution?

Page 67: Дмитрий Копляров , Потокобезопасные сигналы в C++

The wigwag library● A header-only library

● Fast and lightweight signals implementation

● Template policies for threading, exception handling and everything

● Async handlers

● Listeners

https://github.com/koplyarov/wigwag

Page 68: Дмитрий Копляров , Потокобезопасные сигналы в C++

Wigwag vs boost comparisonCPU:

Intel Core i7-3517U @ 1.90GHz

Operating system:

Ubuntu 15.10

https://github.com/koplyarov/wigwag

Page 69: Дмитрий Копляров , Потокобезопасные сигналы в C++

Wigwag vs boost comparisonui_signal:

The most lightweight wigwag signal version. No threading, no populators, no

handler life control. Great for UI components.

signal:

A default wigwag signal configuration. Suits most developers.

boost:

Signals from boost::signals2 library. Both tracking and non-tracking slot versions

are used in handler comparison.

https://github.com/koplyarov/wigwag

Page 70: Дмитрий Копляров , Потокобезопасные сигналы в C++

Signals comparison

https://github.com/koplyarov/wigwag

Page 71: Дмитрий Копляров , Потокобезопасные сигналы в C++

Signal handlers comparison

https://github.com/koplyarov/wigwag

Page 72: Дмитрий Копляров , Потокобезопасные сигналы в C++

https://github.com/koplyarov/wigwag

Page 73: Дмитрий Копляров , Потокобезопасные сигналы в C++

Questions?

Page 74: Дмитрий Копляров , Потокобезопасные сигналы в C++

Thank you!