Resource allocation is initialization: This is an important design pattern mostly followed to operate on threading safely.
To understand the criticality of usage of RAII pattenn, we can see the code below.
#include "stdafx.h"
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int i; //Global variable
mutex m;
void F1(){
m.lock();
i++;
cout << "F1: " << i << endl;
i++;
cout << "F1: " << i << endl;
return; // Here exception may occur
m.unlock();
}
void F2(){
cout << "I am starving" << endl;
m.lock();
i++;
cout << "F2: " << i << endl;
i++;
cout << "F2: " << i << endl;
m.unlock();
}
int main()
{
thread th1(F1);
F2();
th1.join();
return 0;
}
Above, we see that the main thread can go in infinite starvation.
To gaurantee that no such starvation happens, compiler has to gaurantee that unlock happens. This is possible in the deconstrutor call. When object goes out-of-the-scope, the deconstructor is automatically called, and this is where the unlock on mutex should be done.
Below code implements the RAII pattern, to safegurad the main program against such starvation.
#include "stdafx.h"
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int i; //Global variable
mutex m;
//Here we are implementing RAII pattern
// It needs a reference variable and initialize it.
template <class T>
class guard
{
private:
T &m; // reference variable (this reference passing is important)
public:
guard(T &m) : m(m) // Initialize the reference
{
m.lock();
}
//Destructor, this is guaranteed by the compiler that will call it
~guard()
{
m.unlock();
}
};
void F1(){
//m.lock();
guard<mutex> g1(m);
i++;
cout << "F1: " << i << endl;
i++;
cout << "F1: " << i << endl;
return; // Here exception may occur
//m.unlock();
}
void F2(){
//m.lock();
guard<mutex> g2(m);
i++;
cout << "F2: " << i << endl;
i++;
cout << "F2: " << i << endl;
//m.unlock();
}
int main()
{
thread th1(F1);
F2();
th1.join();
return 0;
}
In the above code, when F1() exits, the objects (gaurd object) goes out of scope and the deconstructor is called. So, if an exception happens in the F1(), the proper unlocking if the mutes is guaranteed.