Friday, March 1, 2024

C++ 20 - concurrency - stop_token in jthread - politely interrupting the thread...

 When I studied the Android's Asynctask mechanism many years ago, I was fascinated about the implementation of a nice state machine for different stages of an asynchronous task.

C++ multi threading was a bit raw, in the sense that it was not matured in the initial stage. It seems it is catching up.

So let's continue - just for fun...



A thread cannot be preempted while in the critical section. But what about in case the thread is not inside a critical section and the thread function in the background is doing a heavy duty task like downloading a large video files from a remote server which you want to cancel from the UI (the reason for such a system is when the network is slow, the download may take a very long time and hence you want to cancel it midway). 

In C++ 20, the stop_token header which has introduced a mechanism by which any thread (obviously not in the critical section) can be interrupted and made exit the thread function. The stop_token header is a nice way of implementing the co-operative thread preemption and cancelling an asynctask midway.

Here's a brief description of it...

std::stop_token:

  • Represents the cancellation state of an asynchronous task.
  • Doesn't directly control the task, but indicates if a cancellation request has been made.
  • Used to check if cancellation is requested within the running task.

std::stop_callback

  • An optional callback function registered with a stop_token.
  • Invoked when the associated task is stopped.

Here in the example, the main thread goes to sleep for certain time. In that time frame, the worker thread continues to do the background task.

But after the main thread awakes from the sleeping mode, it requests the worker thread to stop and exit.

Here we go with the source code

The thread function is written as


void printIncrementingValues(std::stop_token stopToken) {

while (!stopToken.stop_requested()) {

std::cout << startNumber++ << " " << std::flush;

std::this_thread::sleep_for(500ms);

}

std::cout << std::endl;

}


Look at the code line in the above code block.

while (!stopToken.stop_requested())

This line is the soul of the whole concept as this keeps listening to the event when the stop_request is issued.

The complete source code is here.


/*

* Worker.h

*

* Created on: Mar 2, 2024

* Author: som

*/


#ifndef WORKER_H_

#define WORKER_H_


#include <iostream>

#include <stop_token>

#include <thread>


using namespace std::literals::chrono_literals;

using namespace std;




class Worker {

private:

int startNumber;

public:

Worker(int start){

startNumber = start;

}

virtual ~Worker(){


}


void printIncrementingValues(std::stop_token stopToken) {

while (!stopToken.stop_requested()) {

std::cout << startNumber++ << " " << std::flush;

std::this_thread::sleep_for(500ms);

}

std::cout << std::endl;

}

};


#endif /* WORKER_H_ */




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

// Name : advanced_stop_token.cpp

// Author : som

// Version :

// Copyright : som-itsolutions

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

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


#include <iostream>

#include <stop_token>

#include <thread>

#include "Worker.h"


int main() {


Worker worker(1);

std::stop_source stopSource;

// creating stop_token object

std::stop_token stopToken = stopSource.get_token();


std::jthread ridit(&Worker::printIncrementingValues, &worker, stopToken);


// Register a callback


std::stop_callback callback(stopToken, []() {


std::cout << std::endl<<"Callback executed" << std::endl;


});


std::this_thread::sleep_for(10s);


stopSource.request_stop();


return 0;

}


And if we run this program, it will show the following

result.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Callback executed


As the main thread is in the sleep mode for 10 seconds and the

interval for printing the numbers is set to 500 ms, hence in the

output there will be 1 to 20.


The new C++ is changing very fast - keeping yourself abreast

with the changes is of utmost importance for the engineers who

are using modern C++.


Friday, February 16, 2024

My C++ exploration - concurrency - async and future - driven by the Intrinsic Motivation...

My whole professional life in the world of software was driven by Intrinsic Motivation.

Intrinsic motivation is the act of doing something without any obvious external rewards. You do it because it's enjoyable and interesting, rather than because of an outside incentive or pressure to do it, such as a reward or deadline.

An example of intrinsic motivation for a software engineer would be deciphering a piece of a framework code because you enjoy doing it and have an interest in the matter or subject, rather than doing it because you have to prove a point to others or pass an exam.

Intrinsic motivation comes from within, while extrinsic motivation arises from outside. When you’re intrinsically motivated, you engage in an activity solely because you enjoy it and get personal satisfaction from it.

Let's come back to the nitty gritty of C++.

Here's my exploration of the C++ async method from the future header.

C++ async is a function template from the <future> header that helps you execute functions asynchronously, potentially in separate threads. It returns a std::future object that you can use to track the progress and retrieve the result of the asynchronous task.

Key Concepts:

Asynchronous Execution: async launches the provided function in a different thread (or using other mechanisms) without blocking the calling thread. This allows your program to continue doing other work while the asynchronous task is running.

std::future: The std::future object returned by async serves as a placeholder for the result of the asynchronous task. You can use methods like get(), wait(), valid(), and ready() to manage and access the result.

Common Use Cases:

Performing I/O-bound operations (e.g., network requests, file reading/writing) without blocking the main thread.

Source Code:

/*


* Callback.h


*


* Created on: 28-Dec-2023


* Author: ridit


*/


#ifndef CALLBACK_H_

#define CALLBACK_H_


class CallBack {


public:


virtual void onStartTask() = 0;


virtual void onFinishTask() = 0;


};


#endif /* CALLBACK_H_ */


/*

* Caller.h

*

* Created on: Feb 15, 2024

* Author: som

*/


#ifndef CALLER_H_

#define CALLER_H_


#include <iostream>

#include "Callee.h"


using namespace std;


//forward declaration

class Callee;


class Caller : public CallBack{


private:


Callee* callee;


public:


Caller(){


    callee = new Callee(this);


}


void doOwnTask(){


    cout<<"Main thread id = " <<this_thread::get_id()<<endl;


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


cout<<"Caller is doing its own task in the main thread..."<<endl;


}


}


void delegateTaskToCallee(){


callee->doBackgroundTask();


}


virtual ~Caller(){


}


void onStartTask(){


cout<<"The background task is starting"<<endl;


}


void onFinishTask(){

cout<<"The background task is finished. Thank you, Callee, for taking my burden"<<endl;


}


};


#endif /* CALLER_H_ */




/*

* Callee.h

*

* Created on: Feb 15, 2024

* Author: som

*/


#ifndef CALLEE_H_

#define CALLEE_H_


#include <iostream>

#include <future>

#include "CallBack.h"


using namespace std;


class Callee {


private:

CallBack* cb;


public:


Callee(CallBack* cb){


this->cb = cb;


}


virtual ~Callee(){


}


static bool task(){


cout<<"Background thread id = " <<this_thread::get_id()<<endl;


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

    cout<<"Callee is doing the background task"<<endl;

}


return true;


}


void doBackgroundTask(){


this->cb->onStartTask();


future<bool> futureTask = async(launch::async, Callee::task);


if(futureTask.get() == true){

    this->cb->onFinishTask();

    }


}


};


#endif /* CALLEE_H_ */




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

// Name : Callback.cpp

// Author : Som

// Version :

// Copyright : som-itsolutions

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

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


#include <iostream>


#include "CallBack.h"


#include "Caller.h"


#include "Callee.h"


using namespace std;


int main() {


Caller* caller = new Caller();


caller->doOwnTask();


caller->delegateTaskToCallee();



return 0;


}



You may like my other research study vis-a-vis how the

evenlistener pattern has been implemented in Android UI input

events.


Here we go...


Tuesday, February 13, 2024

Are we heading for a doomsday - HAARP - the earthquake weapon of USA...

 HAARP, which stands for High-frequency Active Auroral Research Program, is a research facility located in Gakona, Alaska. Its primary purpose is to study the ionosphere, the uppermost layer of Earth's atmosphere. The ionosphere is ionized by radiation from the Sun, and it plays an important role in radio communications and navigation.


HAARP uses a powerful radio transmitter to send beams of radio waves into the ionosphere. These beams can cause changes in the ionosphere, which can be studied by other instruments at the facility. This research helps scientists to understand how the ionosphere works and how it can be affected by natural and man-made phenomena.

Now the crux...

Conspiracy theories !!! Universe you decide...



Here's another video - technical



READ... READ...



Sunday, February 4, 2024

jthread in C++ 20 concurrency - a wrapper around std::thread...

jthread is a new entrant in C++ 20. It's a simple wrapper around std::thread which is based upon RAII objective - that is resource acquisition is resource initialization.

The j in the jthread stands for automatic joining.

This means that there is no need to call join on the newly created thread object - the program won't terminate abnormally.

When a jthread object goes out of scope or is otherwise destroyed, it automatically calls join() to ensure the thread completes before the object is destroyed. This prevents potential resource leaks or undefined behavior that could occur with std::thread.

The other aspect of jthread is the cooperative interruption.

We will discuss it in the next post.

Saturday, January 20, 2024

The Barrier in C++ 20 concurrency - the programmer in me is still thriving...

Enjoy my training video on the C++ barrier...


The std::barrier class is a synchronization primitive introduced in C++20. It allows a set of threads to synchronize at a certain point in their execution. It is similar to the std::latch class, but it can be reused multiple times.

A std::barrier object is initialized with a count, which specifies the number of threads that must reach the barrier before any of them can proceed. When a thread reaches the barrier, it calls the wait() method. If the count is not yet zero, the thread will be blocked until the count reaches zero. Once the count reaches zero, all of the threads that are waiting on the barrier will be released and can proceed.

The std::barrier class can be used to implement a variety of synchronization patterns, such as producer-consumer queues, parallel algorithms, and race condition prevention.

Here's my application in which I used barrier to showcase how it can be used.

Class Student

/*

* Student.h

*

* Created on: Jan 10, 2024

* Author: som

*/


#ifndef STUDENT_H_

#define STUDENT_H_



#include <iostream>

#include <string>

#include <thread>

#include <barrier>


using namespace std;



class Student {


private:

string name;

int timeLapseBeforeStarting;

int timeToFinish;


public:

Student(string name, int timeLapseBeforeStarting, int timeToFinish){

this->name = name;

this->timeLapseBeforeStarting = timeLapseBeforeStarting;

this->timeToFinish = timeToFinish;


}

virtual ~Student(){


}


void task(barrier<> &b){


this_thread::sleep_for(chrono::milliseconds(timeLapseBeforeStarting));


b.arrive_and_wait();


const auto now = std::chrono::system_clock::now();


const std::time_t t_c = std::chrono::system_clock::to_time_t(now);


cout<< this-> name << " is Starting the task at " << std::ctime(&t_c);


this_thread::sleep_for(chrono::milliseconds(timeToFinish));


cout<< this-> name <<" finished the task"<<endl;


}

};


#endif /* STUDENT_H_ */


Class classETC

/*

* classETC.h

*

* Created on: Jan 10, 2024

* Author: som

*/


#ifndef CLASSETC_H_

#define CLASSETC_H_


#include <thread>

#include <barrier>

#include "Student.h"


using namespace std;


class classETC {


public:

classETC(){


}

virtual ~classETC(){


}


auto giveTaskToStudent(barrier<> &b){

thread riditT(&Student::task, new Student ("Ridit", 1000,2000), ref(b));

thread ishanT(&Student::task, new Student("Ishan", 3000, 1000), ref(b));

thread rajdeepT(&Student::task, new Student("Rajdeep", 900, 1500), ref(b));


riditT.join();

ishanT.join();

rajdeepT.join();

}

};





#endif /* CLASSETC_H_ */



The Main method


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

// Name : BarrierC++.cpp

// Author : som

// Version :

// Copyright : som-itsolutions

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

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


// C++ Program to demonstrate use of use std::barrier in

#include <iostream>

#include <barrier>

#include <thread>


#include "classETC.h"


using namespace std;



int main() {


cout << "C++ Version" << endl; // prints C++ Version

if (__cplusplus == 202101L)

cout << "C++23";

else if(__cplusplus == 202002L)

cout << "C++20\n";

else if (__cplusplus == 201703L)

cout << "C++17\n";

else if (__cplusplus == 201402L)

cout << "C++14\n";

else if (__cplusplus == 201103L)

cout << "C++11\n";

else if (__cplusplus == 199711L)

cout << "C++98\n";

else cout << "pre-standard C++\n";


barrier b ( 3);


classETC etc;


etc.giveTaskToStudent(b);





return 0;

}


The Output:


C++ Version C++20 Ishan is Starting the task at RiditRajdeep is Starting the task at is Starting the task at Sat Jan 20 17:41:30 2024 Sat Jan 20 17:41:30 2024 Sat Jan 20 17:41:30 2024 Ishan finished the task Rajdeep finished the task Ridit finished the task

Have a look at the time when the three different threads are starting - all of them start at the
same time - 17:41:30 - why?
As you have guessed it correctly - it's because of the barrier.
Enjoy...


Friday, January 12, 2024

National Youth Day - here's is a clarion call to the engineers of Bharat - wake up and embrace Sanskrit...

 


Nikola Tesla and Swami Vivekananda

Mr. Toby Grotz, President, Wireless Engineering

Swami Vivekananda, late in the year l895 wrote in a letter to an English friend, "Mr. Tesla thinks he can demonstrate mathematically that force and matter are reducible to potential energy. I am to go and see him next week to get this new mathematical demonstration. In that case, the Vedantic cosmology will be placed on the surest of foundations. I am working a good deal now on the cosmology and eschatology of the Vedanta. I clearly see their perfect union with modern science, and the elucidation of the one will be followed by that of the other." (Complete Works, Vol. V, Fifth Edition, 1347, p. 77).

Here Swamiji uses the terms force and matter for the Sanskrit terms Prana and Akasha. Tesla used the Sanskrit terms and apparently understood them as energy and mass. (In Swamiji's day, as in many dictionaries published in the first half of the present century, force and energy were not always clearly differentiated. Energy is a more proper translation of the Sanskrit term Prana.)

Tesla apparently failed in his effort to show the identity of mass and energy. Apparently, he understood that when speed increases, mass must decrease. He seems to have thought that mass might be "converted" to energy and vice versa, rather than that they were identical in some way, as is pointed out in Einstein's equations. At any rate, Swamiji seems to have sensed where the difficulty lay in joining the maps of European science and Advaita Vedanta and set Tesla to solve the problem. It is apparently in the hope that Tesla would succeed in this that Swamiji says "In that case, the Vedantic cosmology will be placed on the surest of foundations."

Unfortunately, Tesla failed and the solution did not come till ten years later, in a paper by Albert Einstein. But by then Swamiji was gone and the connecting of the maps was delayed.

Engineers of Bharat - don't spend your life as a wage slave.



Reclaim your true identity... 

Embrace #Sanskrit

Read... Read...



Here's why we must declare Sanskrit as the national language of Bharat...


Let's discard Unity in diversity and embrace Unity in Unison - let's all embrace Sanskrit...




Here's my wife Reema reciting her own poem on wisdom


Sunday, January 7, 2024

Latches in C++ 20 concurrency - just like the CountdownLatch of Java concurrency package...

Multithreaded programming is inherently difficult. One of the reasons is that we can't have control over how a thread will start and finish - in which order - it all depends upon the thread scheduling algorithm of the OS. This makes the reproduction of test cases difficult. Moreover, there are race conditions and deadlocks.

When I was teaching the Countdown latch - a thread synchronization technique used in the Java Concurrency package, there was none like that available in C++. I  am happy to see that the concept of latch is introduced in C++20.

So...

What is a Latch in C++?

  • A synchronization primitive was introduced in C++20.
  • It allows one or more threads to wait for a certain number of operations to complete before proceeding.
  • Acts like a countdown counter that blocks threads until it reaches zero.
Good to know that the C++ team is trying to catch up with Java...

Here we go...

My experimentation with C++ latches.




I taught my young son Ridit about the Java Countdown latch three years ago.

Good to see the C++ team is making the standard library more powerful day by day.

Here is the C++ source code of my experimentation.

The following C++ code needs C++20 to compile and execute.



/*

* Student.h

*

* Created on: Jan 7, 2024

* Author: som

*/


#ifndef STUDENT_H_

#define STUDENT_H_


#include <iostream>

#include <string>

#include <thread>

#include <latch>


using namespace std;



class Student {


private:

string name;

int timeToFinish;



public:

Student(string name, int timeToFinish){

this->name = name;

this->timeToFinish = timeToFinish;

}

virtual ~Student(){


}


void task(std::latch& l){


cout<< this->name << " is Starting the task"<<endl;


this_thread::sleep_for(chrono::milliseconds(this->timeToFinish));


cout<< this->name <<" finished the task"<<endl;


l.count_down();

}

};


#endif /* STUDENT_H_ */



/*

* classETC.h

*

* Created on: Jan 7, 2024

* Author: som

*/


#ifndef CLASSETC_H_

#define CLASSETC_H_


#include <thread>

#include <latch>

#include "Student.h"



class classETC {


public:

classETC(){

}

virtual ~classETC(){


}


void giveTaskToStudent(latch& l){

thread riditT(&Student::task, new Student("Ridit", 1000),ref(l));

thread ishanT(&Student::task, new Student("Ishan", 3000), ref(l));

thread rajdeepT(&Student::task, new Student("Rajdeep", 900), ref(l));


cout<<"Teacher is waiting for all the students to finish their task"<<endl;



riditT.join();

ishanT.join();

rajdeepT.join();


l.wait();


cout<<"All students submitted their task... Teacher is leaving the class"<<endl;



}

};


#endif /* CLASSETC_H_ */



Main:


#include <iostream>

#include <latch>

#include <thread>

#include <chrono>


#include "classETC.h"


using namespace std;


int main() {

cout << "C++ Version" << endl; // prints C++ Version

if (__cplusplus == 202101L)

cout << "C++23";

else if(__cplusplus == 202002L)

cout << "C++20\n";

else if (__cplusplus == 201703L)

cout << "C++17\n";

else if (__cplusplus == 201402L)

cout << "C++14\n";

else if (__cplusplus == 201103L)

cout << "C++11\n";

else if (__cplusplus == 199711L)

cout << "C++98\n";

else cout << "pre-standard C++\n";


latch l(3);


classETC etc;

etc.giveTaskToStudent(l);


return 0;

}


Explanation of the code:


Have a look at the Student class. Each student will do his work in a
background thread. As there are 3 students, the latch will be initiated
with 3.

After completing the task, a student will reduce the latch counter by 1.
So when all the students will complete the task, the latch - on which
the main thread was blocked, will become zero and it will unblock the
main thread.

The C++ latches work exactly the way the Java Countdown Latch
works.


For your reference, here's my son Ridit on Java CountdownLatch.


#Enjoy