1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-12 10:04:14 +02:00

Wrap try in a do...while loop to make sure that no random else is attached to the main if block.

This commit is contained in:
David Steele 2017-11-19 16:30:23 -05:00
parent 9395ad7043
commit dc1a5c18ac
8 changed files with 56 additions and 25 deletions

View File

@ -72,7 +72,7 @@
</release-item>
<release-item>
<p>Simplify try..catch..finally names.</p>
<p>Simplify try..catch..finally names. Also wrap in a do...while loop to make sure that no random else is attached to the main if block.</p>
</release-item>
<release-item>

View File

@ -50,7 +50,7 @@ This turned out to be a dead end because Perl 5.10 does not support croak_sv(),
Error handling macros that throw a Perl error when a C error is caught
***********************************************************************************************************************************/
#define ERROR_XS_BEGIN() \
TRY()
TRY_BEGIN()
#define ERROR_XS() \
croak("PGBRCLIB:%d:%s:%d:%s", errorCode(), errorFileName(), errorFileLine(), errorMessage());
@ -59,7 +59,8 @@ Error handling macros that throw a Perl error when a C error is caught
CATCH_ANY() \
{ \
ERROR_XS(); \
}
} \
TRY_END();
/***********************************************************************************************************************************
Core context handling macros, only intended to be called from other macros
@ -72,7 +73,7 @@ Core context handling macros, only intended to be called from other macros
volatile bool MEM_CONTEXT_XS_croak = false; \
\
/* Try the statement block */ \
TRY()
TRY_BEGIN()
#define MEM_CONTEXT_XS_CORE_END() \
/* Set error to be croak to Perl later */ \
@ -84,7 +85,8 @@ Core context handling macros, only intended to be called from other macros
FINALLY() \
{ \
memContextSwitch(MEM_CONTEXT_XS_memContextOld); \
}
} \
TRY_END();
/***********************************************************************************************************************************
Simplifies creation of the memory context in contructors and includes error handling
@ -94,7 +96,7 @@ Simplifies creation of the memory context in contructors and includes error hand
/* Attempt to create the memory context */ \
MemContext *MEM_CONTEXT_XS_memContext = NULL; \
\
TRY() \
TRY_BEGIN() \
{ \
MEM_CONTEXT_XS_memContext = memContextNew(contextName); \
} \
@ -102,6 +104,7 @@ Simplifies creation of the memory context in contructors and includes error hand
{ \
ERROR_XS() \
} \
TRY_END(); \
\
MEM_CONTEXT_XS_CORE_BEGIN(MEM_CONTEXT_XS_memContext)

View File

@ -77,7 +77,7 @@ decodeToBinValid(EncodeType encodeType, const char *source)
{
volatile bool valid = true;
TRY()
TRY_BEGIN()
{
decodeToBinValidate(encodeType, source);
}
@ -85,6 +85,7 @@ decodeToBinValid(EncodeType encodeType, const char *source)
{
valid = false;
}
TRY_END();
return valid;
}

View File

@ -3,7 +3,7 @@ Error Handler
Implement a try ... catch ... finally error handler.
TRY()
TRY_BEGIN()
{
<Do something that might throw an error>
}
@ -23,11 +23,16 @@ FINALLY()
{
<Cleanup code that runs in all cases>
}
TRY_END();
The CATCH() and FINALLY() blocks are optional but at least one must be specified. There is no need for an TRY block by itself
The CATCH() and FINALLY() blocks are optional but at least one must be specified. There is no need for a TRY block by itself
because errors will automatically be propagated to the nearest try block in the call stack.
Never call return from within any of the error-handling blocks.
IMPORTANT: If a local variable of the function containing a TRY block is modified in the TRY_BEGIN() block and used later in the
function after an error is thrown, that variable must be declared "volatile" if the preserving the value is important. Beware that
gcc's -Wclobbered warnings are almost entirely useless for catching such issues.
IMPORTANT: Never call return from within any of the error-handling blocks.
***********************************************************************************************************************************/
#ifndef ERROR_H
#define ERROR_H
@ -51,8 +56,11 @@ bool errorInstanceOf(const ErrorType *errorTypeTest);
/***********************************************************************************************************************************
Begin a block where errors can be thrown
***********************************************************************************************************************************/
#define TRY() \
#define TRY_BEGIN() \
do \
{ \
if (errorInternalTry(__FILE__, __LINE__) && setjmp(*errorInternalJump()) >= 0) \
{ \
while (errorInternalProcess(false)) \
if (errorInternalStateTry())
@ -74,6 +82,13 @@ Code to run whether the try block was successful or not
#define FINALLY() \
else if (errorInternalStateFinal())
/***********************************************************************************************************************************
End the try block
***********************************************************************************************************************************/
#define TRY_END() \
} \
} while (0)
/***********************************************************************************************************************************
Throw an error

View File

@ -43,7 +43,7 @@ Memory context management functions
MemContext *context = memContextNew();
MemContext *contextOld = memContextSwitch(context);
TRY()
TRY_BEGIN()
{
<Do something with the memory context>
}
@ -58,6 +58,7 @@ FINALLY
{
memContextSwitch(context);
}
TRY_END();
Use the MEM_CONTEXT*() macros when possible rather than implement error-handling for every memory context block.
***********************************************************************************************************************************/
@ -100,7 +101,7 @@ MEM_CONTEXT_END();
MemContext *MEM_CONTEXT_memContextOld = memContextSwitch(memContext); \
\
/* Try the statement block */ \
TRY()
TRY_BEGIN()
#define MEM_CONTEXT_OLD() \
MEM_CONTEXT_memContextOld
@ -111,6 +112,7 @@ MEM_CONTEXT_END();
{ \
memContextSwitch(MEM_CONTEXT_OLD()); \
} \
TRY_END(); \
}
/***********************************************************************************************************************************

View File

@ -30,7 +30,7 @@ Test that an expected error is actually thrown and error when it isn't.
printf(" l%04d - expect error: %s\n", __LINE__, errorMessageExpected); \
fflush(stdout); \
\
TRY() \
TRY_BEGIN() \
{ \
statement; \
} \
@ -43,6 +43,7 @@ Test that an expected error is actually thrown and error when it isn't.
AssertError, "expected error %s, '%s' but got %s, '%s'", errorTypeName(&errorTypeExpected), errorMessageExpected, \
errorName(), errorMessage()); \
} \
TRY_END(); \
\
if (!TEST_ERROR_catch) \
THROW( \
@ -99,7 +100,7 @@ parameters.
/* Try to run the statement */ \
type TEST_RESULT_result; \
\
TRY() \
TRY_BEGIN() \
{ \
TEST_RESULT_result = (type)(statement); \
} \
@ -111,6 +112,7 @@ parameters.
AssertError, "statement '%s' threw error %s, '%s' but result <%s> expected", \
#statement, errorName(), errorMessage(), TEST_RESULT_resultExpectedStr); \
} \
TRY_END(); \
\
/* Test the type operator */ \
bool TEST_RESULT_resultOp = false; \

View File

@ -11,7 +11,7 @@ bool testTryRecurseFinally = false;
void testTryRecurse()
{
TRY()
TRY_BEGIN()
{
testTryRecurseTotal++;
assert(errorContext.tryTotal == testTryRecurseTotal + 1);
@ -26,6 +26,7 @@ void testTryRecurse()
{
testTryRecurseFinally = true;
}
TRY_END();
} // {uncoverable - function throws error, never returns}
/***********************************************************************************************************************************
@ -40,13 +41,13 @@ void testRun()
}
// -----------------------------------------------------------------------------------------------------------------------------
if (testBegin("TRY() with no errors"))
if (testBegin("TRY with no errors"))
{
bool tryDone = false;
bool catchDone = false;
bool finallyDone = false;
TRY()
TRY_BEGIN()
{
assert(errorContext.tryTotal == 1);
tryDone = true;
@ -60,6 +61,7 @@ void testRun()
assert(errorContext.tryList[1].state == errorStateFinal);
finallyDone = true;
}
TRY_END();
assert(tryDone);
assert(!catchDone);
@ -68,31 +70,32 @@ void testRun()
}
// -----------------------------------------------------------------------------------------------------------------------------
if (testBegin("TRY() with multiple catches"))
if (testBegin("TRY with multiple catches"))
{
bool tryDone = false;
bool catchDone = false;
bool finallyDone = false;
TRY()
TRY_BEGIN()
{
assert(errorContext.tryTotal == 1);
TRY()
TRY_BEGIN()
{
assert(errorContext.tryTotal == 2);
TRY()
TRY_BEGIN()
{
assert(errorContext.tryTotal == 3);
TRY()
TRY_BEGIN()
{
assert(errorContext.tryTotal == 4);
tryDone = true;
THROW(AssertError, BOGUS_STR);
}
TRY_END();
}
CATCH(AssertError)
{
@ -103,11 +106,13 @@ void testRun()
{
finallyDone = true;
}
TRY_END();
}
CATCH_ANY()
{
RETHROW();
}
TRY_END();
}
CATCH(MemoryError)
{
@ -120,6 +125,7 @@ void testRun()
catchDone = true;
}
TRY_END();
assert(tryDone);
assert(catchDone);
@ -134,7 +140,7 @@ void testRun()
bool catchDone = false;
bool finallyDone = false;
TRY()
TRY_BEGIN()
{
tryDone = true;
testTryRecurse();
@ -155,6 +161,7 @@ void testRun()
{
finallyDone = true;
}
TRY_END();
assert(tryDone);
assert(catchDone);

View File

@ -263,7 +263,7 @@ void testRun()
memContextTestName = "test-new-failed-block";
bool bCatch = false;
TRY()
TRY_BEGIN()
{
MEM_CONTEXT_NEW_BEGIN(memContextTestName)
{
@ -277,6 +277,7 @@ void testRun()
{
bCatch = true;
}
TRY_END();
TEST_RESULT_BOOL(bCatch, true, "new context error was caught");
TEST_RESULT_PTR(memContextCurrent(), memContextTop(), "context is now 'TOP'");