8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 18:03:04 +01:00

Fixed #7197: Segfault in linux CS after successful detach from database

This commit is contained in:
AlexPeshkoff 2022-06-01 20:33:32 +03:00
parent fec49660b2
commit 4a3649dd9f
3 changed files with 237 additions and 121 deletions

138
src/jrd/ThreadCollect.h Normal file
View File

@ -0,0 +1,138 @@
/*
* PROGRAM: JRD threading support
* MODULE: ThreadCollect.h
* DESCRIPTION: Threads' group completion handling
*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.firebirdsql.org/en/initial-developer-s-public-license-version-1-0/
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Alexander Peshkov
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2018, 2022 Alexander Peshkov <peshkoff@mail.ru>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
*
*/
#ifndef JRD_THREADCOLLECT_H
#define JRD_THREADCOLLECT_H
#include "../common/ThreadStart.h"
#include "../common/classes/alloc.h"
#include "../common/classes/array.h"
#include "../common/classes/locks.h"
namespace Jrd {
class ThreadCollect
{
public:
ThreadCollect(MemoryPool& p)
: threads(p)
{ }
void join()
{
if (!threads.hasData())
return;
waitFor(threads);
}
void ending(Thread::Handle& h)
{
// put thread into completion wait queue when it finished running
Firebird::MutexLockGuard g(threadsMutex, FB_FUNCTION);
for (unsigned n = 0; n < threads.getCount(); ++n)
{
if (threads[n].hndl == h)
{
threads[n].ending = true;
return;
}
}
Thrd t = {h, true};
threads.add(t);
}
void running(Thread::Handle& h)
{
// put thread into completion wait queue when it starts running
Firebird::MutexLockGuard g(threadsMutex, FB_FUNCTION);
Thrd t = {h, false};
threads.add(t);
}
void houseKeeping()
{
if (!threads.hasData())
return;
// join finished threads
AllThreads t;
{ // mutex scope
Firebird::MutexLockGuard g(threadsMutex, FB_FUNCTION);
for (unsigned n = 0; n < threads.getCount(); )
{
if (threads[n].ending)
{
t.add(threads[n]);
threads.remove(n);
}
else
++n;
}
}
waitFor(t);
}
private:
struct Thrd
{
Thread::Handle hndl;
bool ending;
};
typedef Firebird::HalfStaticArray<Thrd, 4> AllThreads;
void waitFor(AllThreads& thr)
{
Firebird::MutexLockGuard g(threadsMutex, FB_FUNCTION);
while (thr.hasData())
{
FB_SIZE_T n = thr.getCount() - 1;
Thrd& t = thr[n];
{
Firebird::MutexUnlockGuard u(threadsMutex, FB_FUNCTION);
Thread::waitForCompletion(t.hndl);
fb_assert(t.ending);
}
thr.remove(n);
}
}
AllThreads threads;
Firebird::Mutex threadsMutex;
};
} // namespace Jrd
#endif // JRD_THREADCOLLECT_H

View File

@ -110,6 +110,7 @@
#include "../yvalve/why_proto.h" #include "../yvalve/why_proto.h"
#include "../jrd/flags.h" #include "../jrd/flags.h"
#include "../jrd/Mapping.h" #include "../jrd/Mapping.h"
#include "../jrd/ThreadCollect.h"
#include "../jrd/Database.h" #include "../jrd/Database.h"
@ -126,6 +127,7 @@
#include "../common/classes/ClumpletWriter.h" #include "../common/classes/ClumpletWriter.h"
#include "../common/classes/RefMutex.h" #include "../common/classes/RefMutex.h"
#include "../common/classes/ParsedList.h" #include "../common/classes/ParsedList.h"
#include "../common/classes/semaphore.h"
#include "../common/utils_proto.h" #include "../common/utils_proto.h"
#include "../jrd/DebugInterface.h" #include "../jrd/DebugInterface.h"
#include "../jrd/CryptoManager.h" #include "../jrd/CryptoManager.h"
@ -474,6 +476,16 @@ namespace
{ {
using Jrd::Attachment; using Jrd::Attachment;
// Required to sync attachment shutdown threads with provider shutdown
GlobalPtr<ThreadCollect> shutThreadCollect;
struct AttShutParams
{
Semaphore thdStartedSem;
Thread::Handle thrHandle;
AttachmentsRefHolder* attachments;
};
// Flag engineShutdown guarantees that no new attachment is created after setting it // Flag engineShutdown guarantees that no new attachment is created after setting it
// and helps avoid more than 1 shutdown threads running simultaneously. // and helps avoid more than 1 shutdown threads running simultaneously.
bool engineShutdown = false; bool engineShutdown = false;
@ -4558,6 +4570,7 @@ void JProvider::shutdown(CheckStatusWrapper* status, unsigned int timeout, const
**************************************/ **************************************/
try try
{ {
{ // scope
MutexLockGuard guard(shutdownMutex, FB_FUNCTION); MutexLockGuard guard(shutdownMutex, FB_FUNCTION);
if (engineShutdown) if (engineShutdown)
@ -4614,6 +4627,10 @@ void JProvider::shutdown(CheckStatusWrapper* status, unsigned int timeout, const
TraceManager::shutdown(); TraceManager::shutdown();
Mapping::shutdownIpc(); Mapping::shutdownIpc();
} }
// Wait for completion of all attacment shutdown threads
shutThreadCollect->join();
}
catch (const Exception& ex) catch (const Exception& ex)
{ {
ex.stuffException(status); ex.stuffException(status);
@ -8672,22 +8689,26 @@ namespace
ThreadModuleRef thdRef(attachmentShutdownThread, &engineShutdown); ThreadModuleRef thdRef(attachmentShutdownThread, &engineShutdown);
#endif #endif
AttShutParams* params = static_cast<AttShutParams*>(arg);
AttachmentsRefHolder* attachments = params->attachments;
Thread::Handle th = params->thrHandle;
fb_assert(th);
try try
{ {
MutexLockGuard guard(shutdownMutex, FB_FUNCTION); shutThreadCollect->running(th);
if (engineShutdown) params->thdStartedSem.release();
{
// Shutdown was done, all attachmnets are gone
return 0;
}
shutdownAttachments(static_cast<AttachmentsRefHolder*>(arg), isc_att_shut_db_down); MutexLockGuard guard(shutdownMutex, FB_FUNCTION);
if (!engineShutdown)
shutdownAttachments(attachments, isc_att_shut_db_down);
} }
catch (const Exception& ex) catch (const Exception& ex)
{ {
iscLogException("attachmentShutdownThread", ex); iscLogException("attachmentShutdownThread", ex);
} }
shutThreadCollect->ending(th);
return 0; return 0;
} }
} // anonymous namespace } // anonymous namespace
@ -9474,13 +9495,18 @@ void JRD_shutdown_attachment(Attachment* attachment)
fb_assert(attachment->att_flags & ATT_shutdown); fb_assert(attachment->att_flags & ATT_shutdown);
MemoryPool& pool = *getDefaultMemoryPool(); MemoryPool& pool = *getDefaultMemoryPool();
AttachmentsRefHolder* queue = FB_NEW_POOL(pool) AttachmentsRefHolder(pool); AutoPtr<AttachmentsRefHolder> queue(FB_NEW_POOL(pool) AttachmentsRefHolder(pool));
fb_assert(attachment->getStable()); fb_assert(attachment->getStable());
attachment->getStable()->addRef(); attachment->getStable()->addRef();
queue->add(attachment->getStable()); queue->add(attachment->getStable());
Thread::start(attachmentShutdownThread, queue, THREAD_high); AttShutParams params;
params.attachments = queue;
Thread::start(attachmentShutdownThread, &params, THREAD_high, &params.thrHandle);
queue.release();
shutThreadCollect->houseKeeping();
params.thdStartedSem.enter();
} }
catch (const Exception&) catch (const Exception&)
{} // no-op {} // no-op
@ -9529,7 +9555,14 @@ void JRD_shutdown_attachments(Database* dbb)
} }
if (queue.hasData()) if (queue.hasData())
Thread::start(attachmentShutdownThread, queue.release(), THREAD_high); {
AttShutParams params;
params.attachments = queue;
Thread::start(attachmentShutdownThread, &params, THREAD_high, &params.thrHandle);
queue.release();
shutThreadCollect->houseKeeping();
params.thdStartedSem.enter();
}
} }
catch (const Exception&) catch (const Exception&)
{} // no-op {} // no-op

View File

@ -74,6 +74,7 @@
#include "../utilities/nbackup/nbkswi.h" #include "../utilities/nbackup/nbkswi.h"
#include "../jrd/trace/traceswi.h" #include "../jrd/trace/traceswi.h"
#include "../jrd/val_proto.h" #include "../jrd/val_proto.h"
#include "../jrd/ThreadCollect.h"
// Service threads // Service threads
#include "../burp/burp_proto.h" #include "../burp/burp_proto.h"
@ -120,6 +121,7 @@ int main_gstat(Firebird::UtilSvc* uSvc);
using namespace Firebird; using namespace Firebird;
using namespace Jrd;
const int SVC_user_dba = 2; const int SVC_user_dba = 2;
const int SVC_user_any = 1; const int SVC_user_any = 1;
@ -138,64 +140,9 @@ namespace {
GlobalPtr<Mutex> globalServicesMutex; GlobalPtr<Mutex> globalServicesMutex;
// All that we need to shutdown service threads when shutdown in progress // All that we need to shutdown service threads when shutdown in progress
typedef Array<Jrd::Service*> AllServices; typedef Array<Service*> AllServices;
GlobalPtr<AllServices> allServices; // protected by globalServicesMutex GlobalPtr<AllServices> allServices; // protected by globalServicesMutex
volatile bool svcShutdown = false; volatile bool svcShutdown = false;
class ThreadCollect
{
public:
ThreadCollect(MemoryPool& p)
: threads(p)
{ }
void join()
{
// join threads to be sure they are gone when shutdown is complete
// no need locking something cause this is expected to run when services are closing
waitFor(threads);
}
void add(const Thread::Handle& h)
{
// put thread into completion wait queue when it finished running
MutexLockGuard g(threadsMutex, FB_FUNCTION);
fb_assert(h);
threads.add(h);
}
void houseKeeping()
{
if (!threads.hasData())
return;
// join finished threads
AllThreads t;
{ // mutex scope
MutexLockGuard g(threadsMutex, FB_FUNCTION);
t.assign(threads);
threads.clear();
}
waitFor(t);
}
private:
typedef Array<Thread::Handle> AllThreads;
static void waitFor(AllThreads& thr)
{
while (thr.hasData())
{
Thread::Handle h(thr.pop());
Thread::waitForCompletion(h);
}
}
AllThreads threads;
Mutex threadsMutex;
};
GlobalPtr<ThreadCollect> threadCollect; GlobalPtr<ThreadCollect> threadCollect;
void spbVersionError() void spbVersionError()
@ -207,8 +154,6 @@ namespace {
} // anonymous namespace } // anonymous namespace
using namespace Jrd;
namespace { namespace {
const serv_entry services[] = const serv_entry services[] =
{ {
@ -1975,7 +1920,7 @@ THREAD_ENTRY_DECLARE Service::run(THREAD_ENTRY_PARAM arg)
svc->unblockQueryGet(); svc->unblockQueryGet();
svc->finish(SVC_finished); svc->finish(SVC_finished);
threadCollect->add(thrHandle); threadCollect->ending(thrHandle);
} }
catch (const Exception& ex) catch (const Exception& ex)
{ {