Sunday, September 15, 2024

Command Pattern in Rust - absolutely driven by intrinsic motivation...

Rust is a multi paradigm language. It supports Object Oriented Programming model (traits and dynamic binding),  functional programming model (closure, etc) and at the same time straight forward procedural programming model.

It won't force a developer to follow a specific paradigm, rather the developers are free to choose and pick any paradigm they want.

As I have come from a pure Object Oriented world, (C++, Java, Python), my tryst with Rust is mainly through the design patterns of Gang of Four book.

Today, i developed a sample example of Command Pattern using Rust.

Key Components of the Command Pattern:

  1. Command Interface:

    • Defines a common interface with a method (usually called execute) that all concrete commands must implement. This allows different types of commands to be invoked in a uniform way.
  2. Concrete Command:

    • Implements the command interface and defines the actual behavior. It binds the Receiver (the object that knows how to carry out the operation) to an action. The execute method is used to invoke the receiver's method(s).
  3. Invoker:

    • The invoker (e.g., a Waiter in a restaurant analogy) is responsible for triggering the command by calling the execute method. It does not know or care about the specifics of the command, only that it can execute it.
  4. Receiver:

    • The receiver (e.g., a Chef) is the object that performs the actual work. The command will delegate the action to the receiver.

Example Scenario: Restaurant

  • Invoker: The Waiter takes an order from the customer and passes it to the chef.
  • Command: The FoodCommand tells the chef what food to prepare.
  • Receiver: The Chef is responsible for actually preparing the food.

The advantage of the Command Pattern is that the Waiter (invoker) doesn’t need to know the details of how food is prepared. It just knows how to pass the command. You could have different kinds of commands (e.g., DrinkCommand, FoodCommand, DessertCommand), and the invoker can execute them all the same way.

Command Pattern Flow:

  1. The Invoker issues a command.
  2. The Concrete Command executes the command by calling methods on the Receiver.
  3. The Receiver performs the action.
Here is the source code of the command pattern in Rust...

#enjoy...

I must admit - I am yet to achieve the flow in Rust - the learning curve is really steep...

trait Command {
fn execute(&self);
}

//Receiver
struct Chef;

impl Chef {
pub(crate) fn prepare_food(&self) {
println!("Chef is preparing the food...");
}
}

struct FoodCommand{
chef : Chef,
}

impl Command for FoodCommand {
fn execute(&self) {
self.chef.prepare_food();
}
}
//Invoker
struct Waiter {
command : Box<dyn Command>,
}


impl Waiter {
fn new () -> Waiter{
let waiter = Waiter {
command : Box::new (FoodCommand {chef : Chef}),
};
waiter
}

fn pass_command_to_chef(&self){
self.command.execute();
}
}

fn main() {
let chef = Chef;
let food_command = FoodCommand {chef};
let waiter = Waiter {command : Box::new(food_command),};
waiter.pass_command_to_chef();
}

Friday, September 13, 2024

Should you learn Rust NOW? My contribution to the Rust community - State Pattern in Rust...

Key Reasons Why Now Is the Right Time to Learn Rust:

1. Growing Popularity and Industry Adoption

According to the 2024 Stack Overflow Developer Survey, Rust is the most admired programming language, with 83% of developers saying they want to use it again. This is the eighth year in a row that Rust has topped this chart. Major Companies Use Rust: Big tech companies like Microsoft, Amazon, Google, Facebook, Dropbox, and Mozilla use Rust in production systems. Learning Rust can open doors to career opportunities at companies that have adopted the language.

2. Memory Safety Without Garbage Collection

Rust’s borrow checker ensures that you manage memory safely without relying on a garbage collector. This makes Rust an ideal language for systems programming, embedded systems and performance-critical applications. If you want to work on low-level programming without common pitfalls like segmentation faults or data races, Rust is an excellent choice.

3. Concurrency Without Fear

Rust’s ownership model allows you to write highly concurrent programs while ensuring thread safety at compile-time. This is a game-changer for developers working on multi-threaded or parallel applications, and it's a good reason to pick up Rust now as more modern software is built with concurrency in mind.

4.Growing Ecosystem and Libraries

Rust's ecosystem is maturing. The package manager and build tool, Cargo, is widely praised for its ease of use.

5. Community and Support

The Rust community is known for being friendly, helpful, and inclusive. The language is also supported by an active working group and regular updates, making it a language that's constantly evolving but staying stable.

Rust is the right choice for Systems programmers. It is a great alternative to C/C++ for writing system-level code (operating systems, device drivers, embedded systems).

So, here we go...

My contribution to the developers' community - State Design Pattern in Rust.

The State Design Pattern is a behavioral design pattern that allows an object to change its behavior when its internal state changes. In other words, the object behaves differently depending on its current state, and the state transitions are handled internally. This pattern helps avoid complex conditional statements (like if-else or match in Rust) and organizes the code to make it more maintainable.

Here is the source code of the implementation of State Pattern in Rust.

use std::thread;
use std::time::Duration;

struct Mamma {
state: Option<Box<dyn State>>,
}
trait State {
fn wash(self: Box<Self>) -> Box<dyn State>;
fn marinate(self: Box<Self>) -> Box<dyn State>;
fn cook( self : Box<Self>)-> Box<dyn State>;
fn serve(self : Box <Self>) -> Box<dyn State>;
}

impl Mamma {
fn new() -> Mamma {
let mamma = Mamma {
state: Some(Box::new(UncleanedState)),
};
mamma
}

fn startcooking(self) -> () {
self.state.unwrap().wash().marinate().cook().serve();
}
}

struct UncleanedState;
struct CleanedState;

struct MarinatedState;
struct CookState;


impl State for UncleanedState {
fn wash(self: Box<Self>) -> Box<dyn State>{
println!("The Chicken is in Uncleaned state. It's being washed...");
thread::sleep(Duration::from_secs(5)); // Pause for 2 seconds
Box::new(CleanedState{})
}
fn marinate(self: Box<Self>)->Box<dyn State>{
self
}

fn cook(self: Box<Self>)->Box<dyn State>{
self
}

fn serve(self : Box<Self>) ->Box<dyn State>{
self
}

}

impl State for CleanedState {
fn wash(self: Box<Self>) -> Box<dyn State> {
self
}
fn marinate(self: Box<Self>) -> Box<dyn State> {
println!("The chicken is in the Cleanedstate. It's being marinated...");
thread::sleep(Duration::from_secs(5));
Box::new(MarinatedState {})
}

fn cook(self: Box<Self>) -> Box<dyn State> {
self
}

fn serve(self: Box<Self>) -> Box<dyn State> {
self
}
}


impl State for MarinatedState {
fn wash(self: Box<Self>) -> Box<dyn State> {
self
}
fn marinate(self: Box<Self>) -> Box<dyn State> {
self
}

fn cook(self: Box<Self>) -> Box<dyn State> {
println!("The chicken is in the Marinatedstate state. It will be cooked now...");
thread::sleep(Duration::from_secs(5));
Box::new(CookState {})
}

fn serve(self: Box<Self>) -> Box<dyn State> {
self
}
}

impl State for CookState {
fn wash(self: Box<Self>) -> Box<dyn State> {
self
}
fn marinate(self: Box<Self>) -> Box<dyn State> {
self
}

fn cook(self: Box<Self>) -> Box<dyn State> {
self

}

fn serve(self: Box<Self>) -> Box<dyn State> {
println!("The chicken is in the Cookedstate");
println!("This is the last state. I m sure the guests will enjoy the chicken...");
self
}
}

fn main() {
let mamma = Mamma::new();
mamma.startcooking();
}

And here's the video for the output of the above source code.


Sunday, September 8, 2024

Will adoption of Rust for mission critical systems popularise GTK-RS and Linux overall?

 For so many years we have been fed with the infamous FUD that Linux is not good for desktops. However, as more and more organizations adopt Rust for mission-critical systems, I think GTK-RS (for developing cross-platform GUI using Rust) and Linux desktops will see a surge in numbers. At least my gut feeling is saying this.

The inherent memory safety system of Rust and the security system of Linux will make a perfect choice for mission-critical and embedded domains. 

Rust’s adoption in mission-critical systems, many of which might run on Linux, could lead to a resurgence of interest in Linux as a development platform. Developers working on Rust projects might choose Linux for its compatibility, performance, and open-source nature. This could also lead to greater investment in Linux desktop environments, where GTK-RS could play a role.

I am sure in the near future, use of GTK-RS for SCADA system's HMI development is unavoidable. Supervisory Control and Data Acquisition (SCADA) systems are crucial in industries like manufacturing, utilities, and energy management. They monitor and control industrial processes through a combination of hardware and software. SCADA systems require robust, reliable, and high-performance software for their Human-Machine Interface (HMI), and GTK-RS offers several advantages for these applications - the first and foremost among them is safety and security - making the HMI foolproof from being hacked.

Not only from security perspective, but there is a strong reasoning of using GTK-RS in SCADA system for performance. SCADA systems need to process multiple data streams concurrently. Rust’s strengths in concurrency and parallelism ensure that GTK-RS-based applications can handle these demands efficiently, providing a responsive interface even under heavy load.

Keeping this in mind, I was just playing around with GTK-RS, and here's a simple GTK-RS-based application called Skip Counter.



You need GTK 3 to run this...

Here is the source code...

extern crate gtk;
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button, Entry, Label, Box};
use std::rc::Rc;
use std::cell::RefCell;

fn main() {
let application = Application::builder()
.application_id("com.example.skip-counter")
.build();

application.connect_activate(|app| {
// Create a new window
let window = ApplicationWindow::new(app);
window.set_title("Skip Counter");
window.set_default_size(300, 150);

// Create a vertical box to hold the label, entry, and button
let vbox = Box::new(gtk::Orientation::Vertical, 5);
window.set_child(Some(&vbox));

// Create a label to display the counter
let label = Label::new(Some("Counter: 0"));
vbox.pack_start(&label, false, false, 0); // Use pack_start in GTK3

// Create an entry for the skip value
let entry = Entry::new();
entry.set_placeholder_text(Some("Enter skip value"));
vbox.pack_start(&entry, false, false, 0); // Use pack_start in GTK3

// Initial counter value
let counter = Rc::new(RefCell::new(0));

// Create a button to increment the counter
let increment_button = Button::with_label("Increment");
vbox.pack_start(&increment_button, false, false, 0); // Use pack_start in GTK3

// Create a button to increment the counter
let reset_button = Button::with_label("Reset");
vbox.pack_start(&reset_button, false, false, 0); // Use pack_start in GTK3


// Connect button click event
let counter_clone = Rc::clone(&counter);
let label_clone = label.clone();
let entry_clone = entry.clone();
increment_button.connect_clicked(move |_| {
let skip_value = entry_clone.text().parse::<i32>().unwrap_or(1);
*counter_clone.borrow_mut() += skip_value;
label_clone.set_text(&format!("Counter: {}", *counter_clone.borrow()));
});

// Connect the reset button click event
let counter_clone = Rc::clone(&counter);
let label_clone = label.clone();
reset_button.connect_clicked(move |_| {
*counter_clone.borrow_mut() = 0;
label_clone.set_text("Counter: 0");
});
// Show the window
window.show_all();
});

application.run();
}

Thursday, September 5, 2024

My first UI based Rust application - A Skip Counter - created using iced UI library...

It's rightly said that when someone teaches another person, actually two people learn.

Here goes my contribution on Teacher's day.

Happy Teacher's day to all the Gurus of the Universe.

Here we go... 

Rust is primarily known for System programming. However, with growing popularity, eventually may be UI based application will be developed using Rust. Rust's UI libraries are still maturing in comparison to C++ (QT) and other languages, however, it's inbuilt memory safety issue may force global companies to consider Rust for UI based mission critical system like SCADA HMI and similar application areas.

Pros of Using Rust for UI Applications:

  1. Memory Safety: Rust's ownership model and memory safety features make it excellent for avoiding memory-related bugs, which is crucial for large-scale UI applications.
  2. Performance: Rust's performance is comparable to C and C++, which can be beneficial in resource-intensive UI applications.
  3. Concurrency: Rust's built-in concurrency model helps in writing fast and reliable multi-threaded applications, which is useful for interactive UIs.
  4. Cross-Platform: Libraries like Iced offer cross-platform capabilities, so you can build desktop applications for different operating systems.

Cons of Using Rust for UI Applications:

  1. Immature Ecosystem: Although libraries like Iced, and Slint are promising, they are still not as mature or feature-rich as UI toolkits available in other languages (e.g., Qt for C++, Flutter for Dart).
  2. Steep Learning Curve: The Rust programming model, particularly its strict borrow checker and ownership system, may pose a challenge for developers who are new to the language, making rapid UI prototyping slower.
  3. Fewer Libraries and Frameworks: While libraries like Iced, GTK exist for Rust, they lack the polish and comprehensive documentation of more established UI frameworks. Integrating features like animations or sophisticated layouts can be more difficult.
Yesterday I was developing a small UI based application called Skip Counter using the iced library for Rust. Here's what it looks like.



And here we go... the source code for this simple application.

use iced::{widget::{Button, Column, Text, TextInput},
Sandbox, Settings, Application, Command, executor,
Theme};
#[derive(Debug, Clone)]
enum Message {
SkipValueChanged(String),
Increment,
Reset,
}

struct SkipCounter {
count: i32,
skip_value: i32,
input_value: String,
}

impl SkipCounter {
fn new() -> Self {
SkipCounter {
count: 0,
skip_value: 1, // Default skip value
input_value: String::new(),
}
}
}
impl Application for SkipCounter {

type Executor = executor::Default; // Here you specify the type for Executor
type Flags = (); // Replace with your actual flags type
type Message = Message;

fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) {
(SkipCounter::new(), Command::none())
}

fn title(&self) -> String {
String::from("Skip Counter")
}

fn update(&mut self, message: Message) -> iced::Command<Message> {
match message {
Message::SkipValueChanged(value) => {
self.input_value = value.clone();
self.skip_value = value.parse::<i32>().unwrap_or(1);
Command::none()
}
Message::Increment => {
self.count += self.skip_value;
Command::none()
}
Message::Reset => {
self.count = 0;
Command::none()
}
}
}

fn view(&self) -> iced::Element<Message> {
let input = TextInput::new(
"Enter skip value",
&self.input_value,
//Message::SkipValueChanged
).on_input(Message::SkipValueChanged);

let increment_button = Button::new(Text::new("Increment"))
.on_press(Message::Increment);

let reset_button = Button::new(Text::new("Reset"))
.on_press(Message::Reset);

let count_text = Text::new(format!("Current Count: {}", self.count));

Column::new()
.padding(100)
.align_items(iced::Alignment::Center)
.push(input)
.push(increment_button)
.push(reset_button)
.push(count_text)
.into()
}

type Theme = iced::Theme;
}

fn main() -> iced::Result {
SkipCounter::run(iced::Settings::default())
}
 You must have got the point - Rust UI toolkits are still maturing. It will take time, but I am sure it will flourish in the near future.

Tuesday, August 27, 2024

My journey of learning Rust - Static dispatch vs Dynamic dispatch...

 In Rust, the Static dispatch means the compiler determines which method to call at compile time. This is in contrast to Dynamic dispatch in which the decision is made during runtime. Static dispatch is the default behaviour in Rust. Static dispatch is used with generics and trait bounds without using the keyword dyn.

Static dispatch is usually faster than dynamic dispatch because there is no need for a runtime lookup in the VTble of the method to be called.

Example of Static Dispatch and Dynamic Dispatch

Please have a look at the below code which has used both Static dispatch as well as Dynamic Dispatch.

trait Animal {
fn make_sound(&self);
fn wag_tail(&self){

println!("i don't have a tail..."); //default behavior
}
}

struct Human;

impl Animal for Human {
fn make_sound(&self) {
println!("Human is speaking...");
}
}

struct Dog;

impl Animal for Dog {

fn make_sound(&self) {
println!("Dog barks...");
}

fn wag_tail(&self) {
println!("The dog is waging it's tail");
}
}

fn static_dispatch_makesound<T : Animal> (animal:T){
animal.make_sound();
}

fn dynamic_dispatch_makesound(animal : &dyn Animal) {
animal.make_sound()
}

fn static_dispatch_wagtail<T : Animal> (animal:T){
animal.wag_tail();
}

fn dynamic_dispatch_wagtail(animal : &dyn Animal) {
animal.wag_tail();
}

fn main() {

let dog = Dog;
let human = Human;

dynamic_dispatch_wagtail(&dog);
static_dispatch_makesound(dog);

dynamic_dispatch_makesound(&human);
static_dispatch_wagtail(human);
}

Please note how static_dispatch has used trait bound function 

fn static_dispatch_makesound<T : Animal> (animal:T){
animal.make_sound();
}

and dynamic dispatch has used the keyword dyn.

fn dynamic_dispatch_wagtail(animal : &dyn Animal){ animal.wag_tail();

Static dispatch is fast and allows function calls to be inlined, which is key for optimization. 

However, Static dispatch can lead to "code bloat" because the binary contains multiple copies of the same function, one for each type.

Sunday, August 25, 2024

From Object Oriented Analysis and Design to Language like Rust - a major paradigm shift in the programming world...


चरैवेति, चरैवेति - Charaiveti, Charaiveti - keep walking...

because the motion is the life - we need to move on to keep the balance of life...

We must not stop.

The movement is essential.

Life is like a bicycle.

The moment it stops - the balance goes for a toss.

So here we go... moving forward from C++/Java/OOAD to Rust...

In Rust... i keep my Trust...

The first language that I learned in the software world was C++ - that was back in mid '90s. From that time onwards, almost 30 years have passed by. During this time the software world was dominated by C++, Java and other OOAD stuffs.

I experienced the use cases of Unified Modelling Language and it's usefulness in the design process of an object oriented software project.

From C to C++ and then Java, it was the first major paradigm shift.

And now, it seems we are going to experience another paradigm shift in the world of software.

With language like Rust picking up the popularity, will demand from the programmers to shift the gear - from maybe C++ or Java to Rust.

Rust is neither a pure object oriented like Java nor an absolute functional language. Rather it has taken the good stuffs from both Object oriented domain and functional domain to offer an unique experience to the programmers.

It's definitely a major paradigm shift in the world of computer programming.

Let us try to understand some features and you will feel the differences.

The first major difference is that Rust has avoided the class based inheritance model of standard OOAD domain. In the OOAD domain we encapsulate the state and the behaviour together in a class and we extend that class, which is known as inheritance to offer versatility in the behaviours.

In Rust, we define Traits and use Traits (which are similar to Interface in the OOAD world) and composition rather than inheritance. This approach encourages composition over inheritance, meaning that instead of creating deep hierarchies, you build functionality by combining smaller, focused pieces.

The next obvious difference is the way we handle memory related issues in C++ and Java. In Java, we have garbage collection and in modern C++, we handle it through Boost's library and pointers.

However, Rust uses its unique ownership model. Each value in Rust has a single owner, and memory is automatically reclaimed when the owner goes out of scope. Additionally, Rust uses borrowing and lifetimes to ensure that references to data do not outlive the data itself, preventing many common errors like null pointer dereferences or data races.

The other paradigm shift is the way Rust handles polymorphism. Polymorphism in standard OOAD is done using inheritance. The shift in Rust involves moving from a default of dynamic dispatch and inheritance to a system that favours static dispatch (monomorphization) with the option for dynamic dispatch when necessary. This results in more performant code but requires more deliberate design decisions about when to use dynamic behaviour. Static dispatch may increase the code foot print but it will be much faster than dynamic dispatch and concepts like VTble.

There are also differences between a standard OOAD exception handling and the way Rust handles it using Result and Option.

I have heard that the way Rust manages concurrency is also kind of a paradigm shift in the thought processing of an OOAD engineer. However, I am yet to cover all those things and hence no comment. for the time being.

Now let us try to understand some of the nitty gritties of Rust vis-a-vis  UML as this was the back bone in the world of OOAD design.

You will be surprised to know that we can't get similar effects of Aggregation in Rust because of it's lifetime management. We can simulate the Agrregation in Rust. Nevertheless it won't be exact aggregation.

Look at the following piece of code.

struct Engine {
    horsepower: u32,
}
struct Car<'a> {
    engine: &'a Engine,  // Aggregation-like relationship
}
fn main() {
    let engine = Engine { horsepower: 300 };
    let car = Car { engine: &engine };
    println!("Car has an engine with {} horsepower.",             car.engine.horsepower);
}


Explanation:

&'a Engine: The Car struct contains a reference to an Engine. This setup means that the Engine can exist independently of the Car, similar to how aggregation works in UML.

Ownership and Borrowing: Unlike aggregation in traditional OOPS, Rust's borrowing rules ensure that you cannot accidentally leave a reference dangling (i.e., referencing an object that no longer exists). That means once the car is destroyed, it will take away the engine with it - unlike the traditional OOAD aggregation.

So what is the alternative? Use Ownership - much like Composition in UML. Look at the following piece of code.

struct Engine {
    horsepower: u32,
}

struct Car {
    engine: Engine,  // Composition-like relationship
}

fn main() {
    let engine = Engine { horsepower: 300 };
    let car = Car { engine };  // The Car now owns the engine

    println!("Car has an engine with {} horsepower.", car.engine.horsepower);


This is a Work In Progress.

Will gradually add other aspects of Rust in the near future.

For all the engineers who embraced the Gang of Four design pattern book as their skill set, will have to embrace the paradigm shift in the world of programming.

It's time to embrace changes - and keep this book as a great influencer for the OOAD programmers.



Thursday, August 15, 2024

C++ Concurrent profiling using Helgrind - a tool of Valgrind

 On 15th August - my contribution to the learning community...

Concurrency profiling in C++ is essential for optimizing the performance of multi-threaded applications by identifying and addressing bottlenecks, inefficiencies, and issues like race conditions and deadlocks.

I am using the Helgrind tool of Valgrind in eclipse to do the experimentation on C++ data race condition in a multithreaded application.

A data race occurs in a multithreaded application when two or more threads access shared data concurrently, and at least one of these accesses is a write operation without proper synchronization (e.g., without locks). This can lead to unpredictable behavior, crashes, or incorrect program output.

Here is some information about Helgrind.

Helgrind:

- Detects data races, potential deadlocks, and lock-order violations.

- Useful for debugging multi-threaded applications where data consistency is crucial.

Data Race Detection: 

If Helgrind detects that two threads are accessing the same memory location concurrently without proper synchronization, and at least one of these accesses is a write, it flags this as a data race.

Please have a look at my video - it's all explained here.


Concurrency profiling in C++ is crucial for developing high-performance multi-threaded applications.

Valgrind is a versatile tool for detecting a wide range of memory-related issues in C++ applications. Tools like Memcheck, Helgrind, DRD, and Massif provide comprehensive coverage of memory leaks, invalid accesses, uninitialized memory usage, threading issues, and memory management inefficiencies.

Using Valgrind in the development cycle can significantly improve the stability and performance of your application by identifying and allowing you to fix these memory-related issues.

Another important task that Helgrind does is to check whether there is any deadlock in a multithreaded C++ application - like Cyclic dependency.

Cyclic dependency deadlock occurs when two or more locks are acquired in a different order in two task executions, potentially leading to a deadlock when the program's tasks execute in parallel.

A Lock order violation problem indicates the following timeline:

Task 1

Acquire lock A.

Acquire lock B.

Release lock B.

Release lock A.

Task 2

Acquire lock B.

Acquire lock A.

Release lock A.

Release lock B.

If these time lines are interleaved when the two tasks execute in parallel, a Deadlock occurs:

Task 1: Acquire lock A.

Task 2: Acquire lock B.

Task 1: Try to acquire lock B; wait until task 2 releases it.

Task 2: Try to acquire lock A; wait until task 1 releases it.

For example consider the following piece of code

//============================================================================

// Name : CyclicDependencyDeadLock.cpp

// Author : Som

// Version :

// Copyright : som-itsolutions

// Description : Hello World in C++, Ansi-style

//============================================================================


#include <iostream>

#include <thread>

#include <mutex>


using namespace std;


std::mutex lock1, lock2;


void threadA() {

int count = 0;

std::lock_guard<std::mutex> guard1(lock1);

for (int i = 0; i<100000; i++){

count++;

}

std::lock_guard<std::mutex> guard2(lock2);

//cout<<"Thread id " <<this_thread::get_id()<<" this is okay now because of correct lock order " <<count<<endl;

cout<<"Thread id " <<this_thread::get_id()<< " this will never be printed..."<<count<<endl;

}


void threadB() {

std::lock_guard<std::mutex> guard2(lock2);

int count = 0;

for (int i = 0; i<100000; i++){

count++;

}

std::lock_guard<std::mutex> guard1(lock1);

//cout<<"Thread id " <<this_thread::get_id()<<" this is okay now because of correct lock order " <<count<<endl;

cout<<"Thread id " <<this_thread::get_id()<< " this will never be printed..."<<count<<endl;

}


int main() {

std::thread t1(threadA);

std::thread t2(threadB);


t1.join();

t2.join();


return 0;

}



And if we profile the above piece of code using Helgrind, it will show

Thread #3: lock order "0x10E160 before 0x10E1A0" violated...

Please have a look at the following video...


To avoid the lock order violation we must go for consistent global order for all the threads.

That's all for today...

I hope this exploration will help the inquisitive minds of software engineers.

Jai Hind.... Jai Bharat...