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

Various MemContext callback improvements.

Rename memContextCallback() to memContextCallbackSet() to be more consistent with other parts of the code.

Free all context memory when an exception is thrown from a callback.  Previously only the child contexts would be freed and this resulted in some allocations being lost.  In practice this is probably not a big deal since the process will likely terminate shortly, but there may well be cases where that is not true.
This commit is contained in:
David Steele 2019-05-03 18:09:58 -04:00
parent 4a20d44c6b
commit 7ae96949f1
16 changed files with 60 additions and 29 deletions

View File

@ -67,6 +67,10 @@
<p>Add <file>common/macro.h</file> for general-purpose macros.</p>
</release-item>
<release-item>
<p>Various <code>MemContext</code> callback improvements.</p>
</release-item>
<release-item>
<p>Various <code>Buffer</code> improvements.</p>
</release-item>

View File

@ -193,7 +193,7 @@ gzipCompressNew(int level, bool raw)
gzipError(deflateInit2(driver->stream, level, Z_DEFLATED, gzipWindowBits(raw), MEM_LEVEL, Z_DEFAULT_STRATEGY));
// Set free callback to ensure gzip context is freed
memContextCallback(driver->memContext, (MemContextCallback)gzipCompressFree, driver);
memContextCallbackSet(driver->memContext, (MemContextCallback)gzipCompressFree, driver);
// Create filter interface
this = ioFilterNewP(

View File

@ -169,7 +169,7 @@ gzipDecompressNew(bool raw)
gzipError(driver->result = inflateInit2(driver->stream, gzipWindowBits(raw)));
// Set free callback to ensure gzip context is freed
memContextCallback(driver->memContext, (MemContextCallback)gzipDecompressFree, driver);
memContextCallbackSet(driver->memContext, (MemContextCallback)gzipDecompressFree, driver);
// Create filter interface
this = ioFilterNewP(

View File

@ -200,7 +200,7 @@ cipherBlockProcessBlock(CipherBlock *this, const unsigned char *source, size_t s
cryptoError(!(this->cipherContext = EVP_CIPHER_CTX_new()), "unable to create context");
// Set free callback to ensure cipher context is freed
memContextCallback(this->memContext, (MemContextCallback)cipherBlockFree, this);
memContextCallbackSet(this->memContext, (MemContextCallback)cipherBlockFree, this);
// Initialize cipher
cryptoError(

View File

@ -161,7 +161,7 @@ cryptoHashNew(const String *type)
cryptoError((driver->hashContext = EVP_MD_CTX_create()) == NULL, "unable to create hash context");
// Set free callback to ensure hash context is freed
memContextCallback(driver->memContext, (MemContextCallback)cryptoHashFreeCallback, driver);
memContextCallbackSet(driver->memContext, (MemContextCallback)cryptoHashFreeCallback, driver);
// Initialize context
cryptoError(!EVP_DigestInit_ex(driver->hashContext, driver->hashType, NULL), "unable to initialize hash context");

View File

@ -331,7 +331,7 @@ execOpen(Exec *this)
ioWriteOpen(this->ioWriteExec);
// Set a callback so the handles will get freed
memContextCallback(this->memContext, (MemContextCallback)execFree, this);
memContextCallbackSet(this->memContext, (MemContextCallback)execFree, this);
FUNCTION_LOG_RETURN_VOID();
}

View File

@ -94,7 +94,7 @@ tlsClientNew(
this->context = SSL_CTX_new(method);
cryptoError(this->context == NULL, "unable to create TLS context");
memContextCallback(this->memContext, (MemContextCallback)tlsClientFree, this);
memContextCallbackSet(this->memContext, (MemContextCallback)tlsClientFree, this);
// Exclude SSL versions to only allow TLS and also disable compression
SSL_CTX_set_options(this->context, (long)(SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION));

View File

@ -240,7 +240,7 @@ memContextNew(const char *name)
Register a callback to be called just before the context is freed
***********************************************************************************************************************************/
void
memContextCallback(MemContext *this, void (*callbackFunction)(void *), void *callbackArgument)
memContextCallbackSet(MemContext *this, void (*callbackFunction)(void *), void *callbackArgument)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(MEM_CONTEXT, this);
@ -595,8 +595,20 @@ memContextFree(MemContext *this)
this->state = memContextStateFreeing;
// Execute callback if defined
bool rethrow = false;
if (this->callbackFunction)
this->callbackFunction(this->callbackArgument);
{
TRY_BEGIN()
{
this->callbackFunction(this->callbackArgument);
}
CATCH_ANY()
{
rethrow = true;
}
TRY_END();
}
// Free child context allocations
if (this->contextChildListSize > 0)
@ -634,6 +646,10 @@ memContextFree(MemContext *this)
// Else reset the memory context so it can be reused
else
memset(this, 0, sizeof(MemContext));
// Rethrow the error that was caught in the callback
if (rethrow)
RETHROW();
}
FUNCTION_TEST_RETURN_VOID();

View File

@ -29,7 +29,7 @@ Space is reserved for this many allocations when a context is created. When mor
#define MEM_CONTEXT_ALLOC_INITIAL_SIZE 4
/***********************************************************************************************************************************
Memory context callback function type, useful for casts in memContextCallback()
Memory context callback function type, useful for casts in memContextCallbackSet()
***********************************************************************************************************************************/
typedef void (*MemContextCallback)(void *callbackArgument);
@ -60,7 +60,7 @@ Use the MEM_CONTEXT*() macros when possible rather than implement error-handling
***********************************************************************************************************************************/
MemContext *memContextNew(const char *name);
void memContextMove(MemContext *this, MemContext *parentNew);
void memContextCallback(MemContext *this, void (*callbackFunction)(void *), void *callbackArgument);
void memContextCallbackSet(MemContext *this, void (*callbackFunction)(void *), void *callbackArgument);
void memContextCallbackClear(MemContext *this);
MemContext *memContextSwitch(MemContext *this);
void memContextFree(MemContext *this);

View File

@ -65,7 +65,7 @@ regExpNew(const String *expression)
}
// Set free callback to ensure cipher context is freed
memContextCallback(this->memContext, (MemContextCallback)regExpFree, this);
memContextCallbackSet(this->memContext, (MemContextCallback)regExpFree, this);
}
MEM_CONTEXT_NEW_END();

View File

@ -402,7 +402,7 @@ xmlDocumentNew(const String *rootName)
this->xml = xmlNewDoc(BAD_CAST "1.0");
// Set callback to ensure xml document is freed
memContextCallback(this->memContext, (MemContextCallback)xmlDocumentFree, this);
memContextCallbackSet(this->memContext, (MemContextCallback)xmlDocumentFree, this);
this->root = xmlNodeNew(xmlNewNode(NULL, BAD_CAST strPtr(rootName)));
xmlDocSetRootElement(this->xml, this->root->node);
@ -440,7 +440,7 @@ xmlDocumentNewC(const unsigned char *buffer, size_t bufferSize)
THROW_FMT(FormatError, "invalid xml");
// Set callback to ensure xml document is freed
memContextCallback(this->memContext, (MemContextCallback)xmlDocumentFree, this);
memContextCallbackSet(this->memContext, (MemContextCallback)xmlDocumentFree, this);
// Get the root node
this->root = xmlNodeNew(xmlDocGetRootElement(this->xml));

View File

@ -110,7 +110,7 @@ protocolClientNew(const String *name, const String *service, IoRead *read, IoWri
protocolClientNoOp(this);
// Set a callback to shutdown the protocol
memContextCallback(this->memContext, (MemContextCallback)protocolClientFree, this);
memContextCallbackSet(this->memContext, (MemContextCallback)protocolClientFree, this);
}
MEM_CONTEXT_NEW_END();

View File

@ -107,7 +107,7 @@ storageReadPosixOpen(THIS_VOID)
// On success set free callback to ensure file handle is freed
if (this->handle != -1)
{
memContextCallback(this->memContext, (MemContextCallback)storageReadPosixFree, this);
memContextCallbackSet(this->memContext, (MemContextCallback)storageReadPosixFree, this);
result = true;
}

View File

@ -162,7 +162,7 @@ storageWritePosixOpen(THIS_VOID)
}
// Set free callback to ensure file handle is freed
memContextCallback(this->memContext, (MemContextCallback)storageWritePosixFree, this);
memContextCallbackSet(this->memContext, (MemContextCallback)storageWritePosixFree, this);
// Update user/group owner
if (this->interface.user != NULL || this->interface.group != NULL)

View File

@ -119,7 +119,7 @@ storageWriteRemoteOpen(THIS_VOID)
protocolClientExecute(this->client, command, false);
// Set free callback to ensure remote file is freed
memContextCallback(this->memContext, (MemContextCallback)storageWriteRemoteFree, this);
memContextCallbackSet(this->memContext, (MemContextCallback)storageWriteRemoteFree, this);
}
MEM_CONTEXT_TEMP_END();

View File

@ -6,22 +6,26 @@ Test Memory Contexts
testFree - test callback function
***********************************************************************************************************************************/
MemContext *memContextCallbackArgument = NULL;
bool testFreeThrow = false;
void
testFree(MemContext *this)
testFree(void *thisVoid)
{
MemContext *this = thisVoid;
TEST_RESULT_INT(this->state, memContextStateFreeing, "state should be freeing before memContextFree() in callback");
memContextFree(this);
TEST_RESULT_INT(this->state, memContextStateFreeing, "state should still be freeing after memContextFree() in callback");
TEST_ERROR(
memContextCallback(this, (MemContextCallback)testFree, this),
AssertError, "cannot assign callback to inactive context");
TEST_ERROR(memContextCallbackSet(this, testFree, this), AssertError, "cannot assign callback to inactive context");
TEST_ERROR(memContextSwitch(this), AssertError, "cannot switch to inactive context");
TEST_ERROR(memContextName(this), AssertError, "cannot get name for inactive context");
memContextCallbackArgument = this;
if (testFreeThrow)
THROW(AssertError, "error in callback");
}
/***********************************************************************************************************************************
@ -236,24 +240,31 @@ testRun(void)
}
// *****************************************************************************************************************************
if (testBegin("memContextCallback()"))
if (testBegin("memContextCallbackSet()"))
{
TEST_ERROR(
memContextCallback(memContextTop(), (MemContextCallback)testFree, NULL), AssertError,
"top context may not have a callback");
memContextCallbackSet(memContextTop(), testFree, NULL), AssertError, "top context may not have a callback");
MemContext *memContext = memContextNew("test-callback");
memContextCallback(memContext, (MemContextCallback)testFree, memContext);
memContextCallbackSet(memContext, testFree, memContext);
TEST_ERROR(
memContextCallback(memContext, (MemContextCallback)testFree, memContext),
AssertError, "callback is already set for context 'test-callback'");
memContextCallbackSet(memContext, testFree, memContext), AssertError,
"callback is already set for context 'test-callback'");
// Clear and reset it
memContextCallbackClear(memContext);
memContextCallback(memContext, (MemContextCallback)testFree, memContext);
memContextCallbackSet(memContext, testFree, memContext);
memContextFree(memContext);
TEST_RESULT_PTR(memContextCallbackArgument, memContext, "callback argument is context");
// Now test with an error
// -------------------------------------------------------------------------------------------------------------------------
TEST_ASSIGN(memContext, memContextNew("test-callback-error"), "new mem context");
testFreeThrow = true;
TEST_RESULT_VOID(memContextCallbackSet(memContext, testFree, memContext), " set callback");
TEST_ERROR(memContextFree(memContext), AssertError, "error in callback");
TEST_RESULT_UINT(memContext->state, memContextStateFree, " check that mem context was completely freed");
}
// *****************************************************************************************************************************
@ -327,7 +338,7 @@ testRun(void)
TEST_RESULT_BOOL(bCatch, true, "new context error was caught");
TEST_RESULT_PTR(memContextCurrent(), memContextTop(), "context is now 'TOP'");
TEST_RESULT_BOOL(memContext->state == memContextStateFree, true, "new mem context is not active");
TEST_RESULT_UINT(memContext->state == memContextStateFree, true, "new mem context is not active");
}
// *****************************************************************************************************************************