For clearing the need for this pattern, I start this post with a problem statement.
Problem:
"suppose that we have a pair of actions that surrounds a sequence of statements and we need abstract that actions around the statements and have a error prone and exception safe code".
Let's start with a sample code snippet :
void F()
{
|----------------------------------------------------------------------------------------------------------|
|1. block of code which acquires lock, memory or other resources and some other statements |
|----------------------------------------------------------------------------------------------------------|
|----------------------------------------------------------------------------------------------------|
|--2. ---- Sequences of statements |
|----------------------------------------------------------------------------------------------------|
|---------------------------------------------------------------------------------------------------------|
|3. block of code which release lock, memory or other resources and some other statements |
|---------------------------------------------------------------------------------------------------------|
}
Solution:
Initial solution which may bloom in your mind may be following strategy :
void F()
{
|----------------------------------------------------------------------------------------------------------|
|1. block of code which acquires lock, memory or other resources and some other statements |
|----------------------------------------------------------------------------------------------------------|
try
{
|----------------------------------------------------------------------------------------------------|
|--2. ---- Sequences of statements |
|----------------------------------------------------------------------------------------------------|
} catch(...)
{
throw ;
}
finally
{
|---------------------------------------------------------------------------------------------------------|
|3. block of code which release lock, memory or other resources and some other statements |
|---------------------------------------------------------------------------------------------------------|
}
}
---------------- Code 2-----------------------
void F()
{
|----------------------------------------------------------------------------------------------------------|
|1. block of code which acquires lock, memory or other resources and some other statements |
|----------------------------------------------------------------------------------------------------------|
try
{
|----------------------------------------------------------------------------------------------------|
|--2. ---- Sequences of statements |
|----------------------------------------------------------------------------------------------------|
} catch(...)
{
|---------------------------------------------------------------------------------------------------------|
|3. block of code which release lock, memory or other resources and some other statements |
|---------------------------------------------------------------------------------------------------------|
throw ;
}
|---------------------------------------------------------------------------------------------------------|
|3'. block of code which release lock, memory or other resources and some other statements |
|---------------------------------------------------------------------------------------------------------|
}
~scoped_resource_manager()
{
block 3 : release lock, free memory , close connection ...
}
void F()
{
scoped_resource_manager resource_manager(resource_related_parameters);
|----------------------------------------------------------------------------------------------------|
|--2. ---- Sequences of statements |
|----------------------------------------------------------------------------------------------------|
}
Thanks for C++ class destructors.
"Finally" here is a simple real world code sample for you :
scoped_ptr<T> and scoped_lock ( from boost library ) are also implemented to help us in such problems.
Problem:
"suppose that we have a pair of actions that surrounds a sequence of statements and we need abstract that actions around the statements and have a error prone and exception safe code".
Let's start with a sample code snippet :
void F()
{
|----------------------------------------------------------------------------------------------------------|
|1. block of code which acquires lock, memory or other resources and some other statements |
|----------------------------------------------------------------------------------------------------------|
|----------------------------------------------------------------------------------------------------|
|--2. ---- Sequences of statements |
|----------------------------------------------------------------------------------------------------|
|---------------------------------------------------------------------------------------------------------|
|3. block of code which release lock, memory or other resources and some other statements |
|---------------------------------------------------------------------------------------------------------|
}
---------------- Code 1 -----------------------
What happens in "Code 1" if an exception occur in block 2?. Answer is easy; block 3 won't run! So acquired lock will not be unlocked and other threads will waiting for unlock forever, allocated memory in part 1 won't be freed and all resources acquired in part1 won't be released.Solution:
Initial solution which may bloom in your mind may be following strategy :
void F()
{
|----------------------------------------------------------------------------------------------------------|
|1. block of code which acquires lock, memory or other resources and some other statements |
|----------------------------------------------------------------------------------------------------------|
try
{
|----------------------------------------------------------------------------------------------------|
|--2. ---- Sequences of statements |
|----------------------------------------------------------------------------------------------------|
} catch(...)
{
throw ;
}
finally
{
|---------------------------------------------------------------------------------------------------------|
|3. block of code which release lock, memory or other resources and some other statements |
|---------------------------------------------------------------------------------------------------------|
}
}
But as you know, C++ doesn't have finally statement in exception handling. So we have to change the code as following :
void F()
{
|----------------------------------------------------------------------------------------------------------|
|1. block of code which acquires lock, memory or other resources and some other statements |
|----------------------------------------------------------------------------------------------------------|
try
{
|----------------------------------------------------------------------------------------------------|
|--2. ---- Sequences of statements |
|----------------------------------------------------------------------------------------------------|
} catch(...)
{
|---------------------------------------------------------------------------------------------------------|
|3. block of code which release lock, memory or other resources and some other statements |
|---------------------------------------------------------------------------------------------------------|
throw ;
}
|---------------------------------------------------------------------------------------------------------|
|3'. block of code which release lock, memory or other resources and some other statements |
|---------------------------------------------------------------------------------------------------------|
}
---------------- Code 3-----------------------
Code 3 is an acceptable solution but how about the case you are not allowed to use try/catch ?
( see C++ Coding Standard from Google ! ).
Considering this fact that destructor of C++ class will run automatically if object goes out of scope, guides us to write a class and put "block 1" of Code 1 into a constructor of class and "block 3" of Code 1 into destructor of that class. This way helps us to achieve an exception safe code which is named executive around sequence :
class scoped_resource_manager
{
public:
scoped_resource_manager(resource_related_parameters)
{
block 1 : acquires lock,allocate memory, open connections ....
}
{
block 3 : release lock, free memory , close connection ...
}
}
{
scoped_resource_manager resource_manager(resource_related_parameters);
|----------------------------------------------------------------------------------------------------|
|--2. ---- Sequences of statements |
|----------------------------------------------------------------------------------------------------|
}
---------------- Code 4-----------------------
In Code 4, we are not worry if exception occurs in block 2 or not; all allocated resources will be released when function returns or execution steps out of it's scope because of automatically running of scoped_resource_manager class destructor.!
Thanks for C++ class destructors.
"Finally" here is a simple real world code sample for you :
class ScopedConnection { private : BaseDBProvider *mDB; public: ScopedConnection(BaseDBProvider *pDB) { mDB=pDB; mDB->Open(); mDB->StartTransaction(); } ~ScopedConnection() { mDB->DetachTransaction(); mDB->Close(); } }; void F() { BaseDBProvider *bdp=DBFactory::GetDBProvider(); ScopedConnection scoped_con(bdp); DBDataReader *dr; dr=bdp->ExcecuteReader(dbs); while(dr->ReadNext()) ret->push_back(Load(dr)); return ret; }
scoped_ptr<T> and scoped_lock ( from boost library ) are also implemented to help us in such problems.
No comments :
Post a Comment