mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 00:43:02 +01:00
Use correct way to initialize shared events.
Fixed few race conditions and hangs. Unlink file used for shared memory when it is gets unused.
This commit is contained in:
parent
79dfe14465
commit
ff0f3cf93f
@ -34,6 +34,8 @@
|
|||||||
#include "../jrd/pag_proto.h"
|
#include "../jrd/pag_proto.h"
|
||||||
#include "../jrd/tra_proto.h"
|
#include "../jrd/tra_proto.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
using namespace Jrd;
|
using namespace Jrd;
|
||||||
using namespace Firebird;
|
using namespace Firebird;
|
||||||
|
|
||||||
@ -50,10 +52,14 @@ namespace
|
|||||||
{
|
{
|
||||||
NOP = 0,
|
NOP = 0,
|
||||||
|
|
||||||
|
SERVER_STARTED,
|
||||||
|
SERVER_EXITED,
|
||||||
|
|
||||||
RESPONSE,
|
RESPONSE,
|
||||||
EXCEPTION,
|
EXCEPTION,
|
||||||
|
|
||||||
CANCEL_SESSION,
|
FIRST_CLIENT_OP,
|
||||||
|
CANCEL_SESSION = FIRST_CLIENT_OP,
|
||||||
DISCARD,
|
DISCARD,
|
||||||
FINISH_SESSION,
|
FINISH_SESSION,
|
||||||
FLUSH,
|
FLUSH,
|
||||||
@ -89,15 +95,16 @@ namespace
|
|||||||
event_t serverEvent;
|
event_t serverEvent;
|
||||||
event_t clientEvent;
|
event_t clientEvent;
|
||||||
USHORT bufferSize;
|
USHORT bufferSize;
|
||||||
Tag tag;
|
std::atomic<Tag> tag;
|
||||||
char userName[USERNAME_LENGTH + 1]; // \0 if has PROFILE_ANY_ATTACHMENT
|
char userName[USERNAME_LENGTH + 1]; // \0 if has PROFILE_ANY_ATTACHMENT
|
||||||
alignas(FB_ALIGNMENT) UCHAR buffer[4096];
|
alignas(FB_ALIGNMENT) UCHAR buffer[4096];
|
||||||
};
|
};
|
||||||
|
|
||||||
static const USHORT VERSION = 1;
|
static const USHORT VERSION = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ProfilerIpc(thread_db* tdbb, MemoryPool& pool, AttNumber aAttachmentId);
|
ProfilerIpc(thread_db* tdbb, MemoryPool& pool, AttNumber aAttachmentId, bool server = false);
|
||||||
|
~ProfilerIpc();
|
||||||
|
|
||||||
ProfilerIpc(const ProfilerIpc&) = delete;
|
ProfilerIpc(const ProfilerIpc&) = delete;
|
||||||
ProfilerIpc& operator=(const ProfilerIpc&) = delete;
|
ProfilerIpc& operator=(const ProfilerIpc&) = delete;
|
||||||
@ -138,10 +145,12 @@ namespace
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void internalSendAndReceive(thread_db* tdbb, Tag tag, const void* in, unsigned inSize, void* out, unsigned outSize);
|
void internalSendAndReceive(thread_db* tdbb, Tag tag, const void* in, unsigned inSize, void* out, unsigned outSize);
|
||||||
|
void initClient();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AutoPtr<SharedMemory<Header>> sharedMemory;
|
AutoPtr<SharedMemory<Header>> sharedMemory;
|
||||||
AttNumber attachmentId;
|
AttNumber attachmentId;
|
||||||
|
const bool isServer;
|
||||||
};
|
};
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
@ -748,8 +757,9 @@ ProfilerManager::Statement* ProfilerManager::getStatement(Request* request)
|
|||||||
//--------------------------------------
|
//--------------------------------------
|
||||||
|
|
||||||
|
|
||||||
ProfilerIpc::ProfilerIpc(thread_db* tdbb, MemoryPool& pool, AttNumber aAttachmentId)
|
ProfilerIpc::ProfilerIpc(thread_db* tdbb, MemoryPool& pool, AttNumber aAttachmentId, bool server)
|
||||||
: attachmentId(aAttachmentId)
|
: attachmentId(aAttachmentId),
|
||||||
|
isServer(server)
|
||||||
{
|
{
|
||||||
const auto database = tdbb->getDatabase();
|
const auto database = tdbb->getDatabase();
|
||||||
|
|
||||||
@ -767,7 +777,33 @@ ProfilerIpc::ProfilerIpc(thread_db* tdbb, MemoryPool& pool, AttNumber aAttachmen
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkHeader(sharedMemory->getHeader());
|
const auto header = sharedMemory->getHeader();
|
||||||
|
checkHeader(header);
|
||||||
|
|
||||||
|
if (isServer)
|
||||||
|
{
|
||||||
|
Guard guard(this);
|
||||||
|
|
||||||
|
if (sharedMemory->eventInit(&header->serverEvent) != FB_SUCCESS)
|
||||||
|
(Arg::Gds(isc_random) << "ProfilerIpc eventInit(serverEvent) failed").raise();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilerIpc::~ProfilerIpc()
|
||||||
|
{
|
||||||
|
Guard guard(this);
|
||||||
|
|
||||||
|
const auto header = sharedMemory->getHeader();
|
||||||
|
|
||||||
|
event_t* evnt = this->isServer ? &header->serverEvent : &header->clientEvent;
|
||||||
|
if (evnt->event_pid)
|
||||||
|
{
|
||||||
|
sharedMemory->eventFini(evnt);
|
||||||
|
evnt->event_pid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header->serverEvent.event_pid == 0 && header->clientEvent.event_pid == 0)
|
||||||
|
sharedMemory->removeMapFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProfilerIpc::initialize(SharedMemoryBase* sm, bool init)
|
bool ProfilerIpc::initialize(SharedMemoryBase* sm, bool init)
|
||||||
@ -778,15 +814,6 @@ bool ProfilerIpc::initialize(SharedMemoryBase* sm, bool init)
|
|||||||
|
|
||||||
// Initialize the shared data header.
|
// Initialize the shared data header.
|
||||||
initHeader(header);
|
initHeader(header);
|
||||||
|
|
||||||
if (sm->eventInit(&header->serverEvent) != FB_SUCCESS)
|
|
||||||
(Arg::Gds(isc_random) << "ProfilerIpc eventInit(serverEvent) failed").raise();
|
|
||||||
|
|
||||||
if (sm->eventInit(&header->clientEvent) != FB_SUCCESS)
|
|
||||||
{
|
|
||||||
sm->eventFini(&header->serverEvent);
|
|
||||||
(Arg::Gds(isc_random) << "ProfilerIpc eventInit(clientEvent) failed").raise();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -828,7 +855,34 @@ void ProfilerIpc::internalSendAndReceive(thread_db* tdbb, Tag tag,
|
|||||||
|
|
||||||
const auto header = sharedMemory->getHeader();
|
const auto header = sharedMemory->getHeader();
|
||||||
|
|
||||||
header->tag = tag;
|
initClient();
|
||||||
|
|
||||||
|
Cleanup finiClient([&] {
|
||||||
|
if (header->clientEvent.event_pid)
|
||||||
|
{
|
||||||
|
sharedMemory->eventFini(&header->clientEvent);
|
||||||
|
header->clientEvent.event_pid = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const SLONG value = sharedMemory->eventClear(&header->clientEvent);
|
||||||
|
|
||||||
|
const Tag oldTag = header->tag.exchange(tag);
|
||||||
|
switch (oldTag)
|
||||||
|
{
|
||||||
|
case Tag::NOP:
|
||||||
|
header->tag = oldTag;
|
||||||
|
(Arg::Gds(isc_random) << "Remote attachment failed to start listener thread").raise();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tag::SERVER_EXITED:
|
||||||
|
header->tag = oldTag;
|
||||||
|
(Arg::Gds(isc_random) << "Cannot start remote profile session - attachment exited").raise();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
if (attachment->locksmith(tdbb, PROFILE_ANY_ATTACHMENT))
|
if (attachment->locksmith(tdbb, PROFILE_ANY_ATTACHMENT))
|
||||||
header->userName[0] = '\0';
|
header->userName[0] = '\0';
|
||||||
@ -840,27 +894,88 @@ void ProfilerIpc::internalSendAndReceive(thread_db* tdbb, Tag tag,
|
|||||||
fb_assert(inSize <= sizeof(header->buffer));
|
fb_assert(inSize <= sizeof(header->buffer));
|
||||||
memcpy(header->buffer, in, inSize);
|
memcpy(header->buffer, in, inSize);
|
||||||
|
|
||||||
const SLONG value = sharedMemory->eventClear(&header->clientEvent);
|
if (sharedMemory->eventPost(&header->serverEvent) != FB_SUCCESS)
|
||||||
|
(Arg::Gds(isc_random) << "Cannot start remote profile session - attachment exited").raise();
|
||||||
sharedMemory->eventPost(&header->serverEvent);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
EngineCheckout cout(tdbb, FB_FUNCTION);
|
const SLONG TIMEOUT = 500 * 1000; // 0.5 sec
|
||||||
sharedMemory->eventWait(&header->clientEvent, value, 0);
|
|
||||||
|
const int serverPID = header->serverEvent.event_pid;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
EngineCheckout cout(tdbb, FB_FUNCTION);
|
||||||
|
if (sharedMemory->eventWait(&header->clientEvent, value, TIMEOUT) == FB_SUCCESS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (serverPID != getpid() && !ISC_check_process_existence(serverPID))
|
||||||
|
{
|
||||||
|
// Server process was died or exited
|
||||||
|
fb_assert((header->tag == tag) || header->tag == Tag::SERVER_EXITED);
|
||||||
|
|
||||||
|
if (header->tag == tag)
|
||||||
|
{
|
||||||
|
header->tag = Tag::SERVER_EXITED;
|
||||||
|
if (header->serverEvent.event_pid)
|
||||||
|
{
|
||||||
|
sharedMemory->eventFini(&header->serverEvent);
|
||||||
|
header->serverEvent.event_pid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JRD_reschedule(tdbb, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header->tag == Tag::RESPONSE)
|
switch (header->tag)
|
||||||
{
|
{
|
||||||
|
case Tag::SERVER_EXITED:
|
||||||
|
(Arg::Gds(isc_random) << "Cannot start remote profile session - attachment exited").raise();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tag::RESPONSE:
|
||||||
fb_assert(outSize == header->bufferSize);
|
fb_assert(outSize == header->bufferSize);
|
||||||
memcpy(out, header->buffer, header->bufferSize);
|
memcpy(out, header->buffer, header->bufferSize);
|
||||||
}
|
break;
|
||||||
else
|
|
||||||
{
|
case Tag::EXCEPTION:
|
||||||
fb_assert(header->tag == Tag::EXCEPTION);
|
|
||||||
(Arg::Gds(isc_random) << (char*) header->buffer).raise();
|
(Arg::Gds(isc_random) << (char*) header->buffer).raise();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fb_assert(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProfilerIpc::initClient()
|
||||||
|
{
|
||||||
|
// Shared memory mutex must be locked by caller
|
||||||
|
|
||||||
|
fb_assert(isServer == false);
|
||||||
|
|
||||||
|
const auto header = sharedMemory->getHeader();
|
||||||
|
|
||||||
|
// Here should not be event created by another alive client
|
||||||
|
|
||||||
|
if (header->clientEvent.event_pid)
|
||||||
|
{
|
||||||
|
fb_assert(header->clientEvent.event_pid != getpid());
|
||||||
|
|
||||||
|
if (header->clientEvent.event_pid != getpid())
|
||||||
|
{
|
||||||
|
if (ISC_check_process_existence(header->clientEvent.event_pid))
|
||||||
|
(Arg::Gds(isc_random) << "ProfilerIpc eventInit(clientEvent) failed").raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedMemory->eventFini(&header->clientEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sharedMemory->eventInit(&header->clientEvent) != FB_SUCCESS)
|
||||||
|
(Arg::Gds(isc_random) << "ProfilerIpc eventInit(clientEvent) failed").raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------
|
//--------------------------------------
|
||||||
|
|
||||||
@ -871,9 +986,10 @@ ProfilerListener::ProfilerListener(thread_db* tdbb)
|
|||||||
{
|
{
|
||||||
auto& pool = *attachment->att_pool;
|
auto& pool = *attachment->att_pool;
|
||||||
|
|
||||||
ipc = FB_NEW_POOL(pool) ProfilerIpc(tdbb, pool, attachment->att_attachment_id);
|
ipc = FB_NEW_POOL(pool) ProfilerIpc(tdbb, pool, attachment->att_attachment_id, true);
|
||||||
|
|
||||||
cleanupSync.run(this);
|
cleanupSync.run(this);
|
||||||
|
startupSemaphore.enter();
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfilerListener::~ProfilerListener()
|
ProfilerListener::~ProfilerListener()
|
||||||
@ -881,19 +997,14 @@ ProfilerListener::~ProfilerListener()
|
|||||||
exiting = true;
|
exiting = true;
|
||||||
|
|
||||||
// Terminate the watcher thread.
|
// Terminate the watcher thread.
|
||||||
startupSemaphore.tryEnter(5);
|
|
||||||
|
|
||||||
ProfilerIpc::Guard guard(ipc);
|
if (ipc)
|
||||||
|
{
|
||||||
|
auto& sharedMemory = ipc->sharedMemory;
|
||||||
|
sharedMemory->eventPost(&sharedMemory->getHeader()->serverEvent);
|
||||||
|
|
||||||
auto& sharedMemory = ipc->sharedMemory;
|
cleanupSync.waitForCompletion();
|
||||||
|
}
|
||||||
sharedMemory->eventPost(&sharedMemory->getHeader()->serverEvent);
|
|
||||||
cleanupSync.waitForCompletion();
|
|
||||||
|
|
||||||
const auto header = sharedMemory->getHeader();
|
|
||||||
|
|
||||||
sharedMemory->eventFini(&header->serverEvent);
|
|
||||||
sharedMemory->eventFini(&header->clientEvent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProfilerListener::exceptionHandler(const Exception& ex, ThreadFinishSync<ProfilerListener*>::ThreadRoutine*)
|
void ProfilerListener::exceptionHandler(const Exception& ex, ThreadFinishSync<ProfilerListener*>::ThreadRoutine*)
|
||||||
@ -904,23 +1015,32 @@ void ProfilerListener::exceptionHandler(const Exception& ex, ThreadFinishSync<Pr
|
|||||||
void ProfilerListener::watcherThread()
|
void ProfilerListener::watcherThread()
|
||||||
{
|
{
|
||||||
bool startup = true;
|
bool startup = true;
|
||||||
|
auto& sharedMemory = ipc->sharedMemory;
|
||||||
|
const auto header = sharedMemory->getHeader();
|
||||||
|
|
||||||
|
fb_assert(header->tag == ProfilerIpc::Tag::NOP);
|
||||||
|
header->tag = ProfilerIpc::Tag::SERVER_STARTED;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!exiting)
|
while (!exiting)
|
||||||
{
|
{
|
||||||
auto& sharedMemory = ipc->sharedMemory;
|
|
||||||
const auto header = sharedMemory->getHeader();
|
|
||||||
|
|
||||||
const SLONG value = sharedMemory->eventClear(&header->serverEvent);
|
const SLONG value = sharedMemory->eventClear(&header->serverEvent);
|
||||||
|
|
||||||
if (header->tag != ProfilerIpc::Tag::NOP)
|
if (startup)
|
||||||
{
|
{
|
||||||
FbLocalStatus statusVector;
|
startup = false;
|
||||||
EngineContextHolder tdbb(&statusVector, attachment->getInterface(), FB_FUNCTION);
|
startupSemaphore.release();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fb_assert(header->tag >= ProfilerIpc::Tag::FIRST_CLIENT_OP);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
FbLocalStatus statusVector;
|
||||||
|
EngineContextHolder tdbb(&statusVector, attachment->getInterface(), FB_FUNCTION);
|
||||||
|
|
||||||
processCommand(tdbb);
|
processCommand(tdbb);
|
||||||
header->tag = ProfilerIpc::Tag::RESPONSE;
|
header->tag = ProfilerIpc::Tag::RESPONSE;
|
||||||
}
|
}
|
||||||
@ -950,12 +1070,6 @@ void ProfilerListener::watcherThread()
|
|||||||
sharedMemory->eventPost(&header->clientEvent);
|
sharedMemory->eventPost(&header->clientEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startup)
|
|
||||||
{
|
|
||||||
startup = false;
|
|
||||||
startupSemaphore.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exiting)
|
if (exiting)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -967,6 +1081,13 @@ void ProfilerListener::watcherThread()
|
|||||||
iscLogException("Error in profiler watcher thread\n", ex);
|
iscLogException("Error in profiler watcher thread\n", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ProfilerIpc::Tag oldTag = header->tag.exchange(ProfilerIpc::Tag::SERVER_EXITED);
|
||||||
|
if (oldTag >= ProfilerIpc::Tag::FIRST_CLIENT_OP)
|
||||||
|
{
|
||||||
|
fb_assert(header->clientEvent.event_pid);
|
||||||
|
sharedMemory->eventPost(&header->clientEvent);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (startup)
|
if (startup)
|
||||||
|
Loading…
Reference in New Issue
Block a user