Danger of exclusion of anti-pattern .. with some changes

Suppose I have a library that runs on specific 24x7 machines. Even if the code is strong, a hardware error may sooner or later throw an exception. I would like to have some kind of fault tolerant position for such events. One approach would be to write wrapper functions that encapsulate each api a:

returnCode=DEFAULT; try { returnCode=libraryAPI1(); } catch(...) { returnCode=BAD; } return returnCode; 

Then the calling side of the library restarts the entire stream, reinitializes the module if returnCode is bad.

Things can go horribly wrong. For instance.

if the try block (or libraryAPI1 ()) had:

  func1(); char *x=malloc(1000); func2(); 

if func2 () throws an exception, x will never be thrown. In a similar vein, file corruption is a possible result.

Could you tell me what may not be all in this scenario?

+4
source share
4 answers

This code:

 func1(); char *x=malloc(1000); func2(); 

Not a C ++ code. This is what people call C with classes. This is a program style that is similar to C ++, but does not match the way C ++ is used in real life. The reason is that safe C ++ security code almost never requires the use of a pointer (directly) in the code, since pointers are always contained within a class specifically designed to control their lifespan in a safe exception estate (usually smart pointers or containers).

C ++ equivalent of this code:

 func1(); std::vector<char> x(1000); func2(); 
+4
source

A hardware failure cannot lead to a C ++ exception. On some systems, hardware exceptions are a completely different mechanism than C ++ exceptions. In other cases, C ++ exceptions are built on top of the hardware exception mechanism. So this is not a question of general design.

If you want to restore, you need to be transactional - every state change must be completed before completion or canceled completely. RAII is part of this. As Chris Becke notes in another answer, there is more information than resource acquisition.

There is the idiom copy-modify-swap, which has used a lot for transactions, but it can be too hard if you are trying to adapt the working code to handle this "one million" case.

If you really need reliability, isolate the code in the process. If a hardware error kills the process, you can restart its watchdog timer. The OS will return the lost resources. Your code will only need to worry about a transactional state with a constant state, for example, in a file stored in files.

+3
source

Do you have control over the implementation of libraryAPI?

If it can fit into the OO model, you need to create it using the RAII template, which ensures that the destructor (who will release the acquired resources) is called into the exception.

using an auxiliary helper resource, such as a smart pointer, also helps *

 try { someNormalFunction(); cSmartPtr<BYTE> pBuf = malloc(1000); someExceptionThrowingFunction(); } catch(...) { // Do logging and other necessary actions // but no cleaning required for <pBuf> } 
+2
source

The problem with exceptions - even if you reverse engineer using RAiI - is still easy to make code that becomes desynchronized:

 void SomeClass::SomeMethod() { this->stateA++; SomeOtherMethod(); this->stateB++; } 

Now the example may look artificial, but if you replace stateA ++ and stateB ++ for operations that somehow change the state of the class, the expected result of this class is to keep the states in sync. RAII can solve some state problems when using exceptions, but all it does is provide a false sense of security. If SomeOtherMethod () throws an exception, ALL surrounding code needs to be analyzed to ensure that the message conditions (stateA. Delta == stateB.delta).

+2
source

Source: https://habr.com/ru/post/1310320/


All Articles