8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 18:43:02 +01:00

Merge branch 'master' into work/parallel_v5

This commit is contained in:
Vlad Khorsun 2022-06-17 10:41:29 +03:00
commit 5edefaba91
21 changed files with 511 additions and 225 deletions

View File

@ -61,8 +61,6 @@ namespace
} }
DATABASE DB = STATIC FILENAME "yachts.lnk";
#define DB tdgbl->db_handle #define DB tdgbl->db_handle
#define fbTrans tdgbl->tr_handle #define fbTrans tdgbl->tr_handle
#define gds_trans tdgbl->tr_handle #define gds_trans tdgbl->tr_handle
@ -70,6 +68,12 @@ DATABASE DB = STATIC FILENAME "yachts.lnk";
#define isc_status (&tdgbl->status_vector) #define isc_status (&tdgbl->status_vector)
#define gds_status (&tdgbl->status_vector) #define gds_status (&tdgbl->status_vector)
// unused
#define fbProvider
#define fbStatus2
DATABASE DB = STATIC FILENAME "yachts.lnk";
void detectRuntimeODS() void detectRuntimeODS()
{ {

View File

@ -72,8 +72,6 @@ using namespace Burp;
// GPRE. This is to avoid multiple threading problems with module // GPRE. This is to avoid multiple threading problems with module
// level statics. // level statics.
DATABASE DB = STATIC FILENAME "yachts.lnk" RUNTIME * dbb_file;
#define DB tdgbl->db_handle #define DB tdgbl->db_handle
#define fbTrans tdgbl->tr_handle #define fbTrans tdgbl->tr_handle
#define gds_trans tdgbl->tr_handle #define gds_trans tdgbl->tr_handle
@ -81,6 +79,12 @@ DATABASE DB = STATIC FILENAME "yachts.lnk" RUNTIME * dbb_file;
#define isc_status (&tdgbl->status_vector) #define isc_status (&tdgbl->status_vector)
#define gds_status (&tdgbl->status_vector) #define gds_status (&tdgbl->status_vector)
// unused
#define fbProvider
#define fbStatus2
DATABASE DB = STATIC FILENAME "yachts.lnk" RUNTIME * dbb_file;
namespace // unnamed, private namespace // unnamed, private
{ {

View File

@ -73,8 +73,6 @@ using namespace Burp;
// GPRE. This is to avoid multiple threading problems with module // GPRE. This is to avoid multiple threading problems with module
// level statics. // level statics.
DATABASE DB = STATIC FILENAME "yachts.lnk";
#define DB tdgbl->db_handle #define DB tdgbl->db_handle
#define fbTrans tdgbl->tr_handle #define fbTrans tdgbl->tr_handle
#define gds_trans tdgbl->tr_handle #define gds_trans tdgbl->tr_handle
@ -82,6 +80,12 @@ DATABASE DB = STATIC FILENAME "yachts.lnk";
#define isc_status (&tdgbl->status_vector) #define isc_status (&tdgbl->status_vector)
#define gds_status (&tdgbl->status_vector) #define gds_status (&tdgbl->status_vector)
// unused
#define fbProvider
#define fbStatus2
DATABASE DB = STATIC FILENAME "yachts.lnk";
namespace // unnamed, private namespace // unnamed, private
{ {
@ -1682,16 +1686,22 @@ void get_array(BurpGlobals* tdgbl, burp_rel* relation, UCHAR* record_buffer)
burp_fld* field = NULL; burp_fld* field = NULL;
FbLocalStatus status_vector; FbLocalStatus status_vector;
USHORT count, field_number, field_length = 0; USHORT count, field_number, field_length = 0;
UCHAR* buffer = NULL; UCHAR** buffer = NULL;
UCHAR* p = NULL; UCHAR* p = NULL;
UCHAR blr_buffer[200]; // enough for a sdl with 16 dimensions UCHAR blr_buffer[200]; // enough for a sdl with 16 dimensions
lstring xdr_slice; lstring xdr_slice;
// don't free something you don't allocate
lstring xdr_buffer; lstring xdr_buffer;
xdr_buffer.lstr_allocated = 0; xdr_buffer.lstr_allocated = 0;
xdr_buffer.lstr_address = NULL; xdr_buffer.lstr_address = NULL;
Firebird::Cleanup datClean( [&] {
if (buffer && *buffer)
BURP_free(*buffer);
if (tdgbl->gbl_sw_transportable && xdr_buffer.lstr_allocated)
BURP_free(xdr_buffer.lstr_address);
} );
// Pick up attributes // Pick up attributes
SLONG fld_ranges[2 * MAX_DIMENSION]; SLONG fld_ranges[2 * MAX_DIMENSION];
SLONG slice_length = 0; SLONG slice_length = 0;
@ -1990,7 +2000,6 @@ void get_array(BurpGlobals* tdgbl, burp_rel* relation, UCHAR* record_buffer)
if (data_at == 0) if (data_at == 0)
{ {
buffer = BURP_alloc (return_length);
SLONG lcount = 0; SLONG lcount = 0;
if (tdgbl->gbl_sw_transportable) if (tdgbl->gbl_sw_transportable)
{ {
@ -2005,19 +2014,23 @@ void get_array(BurpGlobals* tdgbl, burp_rel* relation, UCHAR* record_buffer)
lcount |= get(tdgbl) << 8; lcount |= get(tdgbl) << 8;
lcount |= get(tdgbl) << 16; lcount |= get(tdgbl) << 16;
lcount |= get(tdgbl) << 24; lcount |= get(tdgbl) << 24;
xdr_buffer.lstr_length = xdr_buffer.lstr_allocated = lcount; xdr_buffer.lstr_length = xdr_buffer.lstr_allocated = lcount;
xdr_buffer.lstr_address = BURP_alloc(lcount); p = xdr_buffer.lstr_address = BURP_alloc(lcount);
xdr_slice.lstr_allocated = xdr_slice.lstr_length = return_length; xdr_slice.lstr_allocated = xdr_slice.lstr_length = return_length;
xdr_slice.lstr_address = buffer; buffer = &xdr_slice.lstr_address;
p = xdr_buffer.lstr_address;
} }
} }
else else
{ {
p = buffer; buffer = &p;
lcount = return_length; lcount = return_length;
} }
fb_assert(buffer);
*buffer = BURP_alloc(return_length);
if (lcount) if (lcount)
get_block(tdgbl, p, lcount); get_block(tdgbl, p, lcount);
@ -2027,7 +2040,7 @@ void get_array(BurpGlobals* tdgbl, burp_rel* relation, UCHAR* record_buffer)
DB->putSlice(&status_vector, gds_trans, blob_id, blr_length, blr_buffer, DB->putSlice(&status_vector, gds_trans, blob_id, blr_length, blr_buffer,
0, NULL, // parameters for subset of an array handling 0, NULL, // parameters for subset of an array handling
elements_written * field->fld_length, buffer + data_at); elements_written * field->fld_length, (*buffer) + data_at);
if (status_vector->hasData()) if (status_vector->hasData())
{ {
BURP_print (false, 81, field->fld_name); BURP_print (false, 81, field->fld_name);
@ -2116,7 +2129,6 @@ void get_array(BurpGlobals* tdgbl, burp_rel* relation, UCHAR* record_buffer)
const USHORT blr_length = blr - blr_buffer; const USHORT blr_length = blr - blr_buffer;
buffer = BURP_alloc (return_length);
SLONG lcount = 0; SLONG lcount = 0;
if (tdgbl->gbl_sw_transportable) if (tdgbl->gbl_sw_transportable)
{ {
@ -2132,18 +2144,21 @@ void get_array(BurpGlobals* tdgbl, burp_rel* relation, UCHAR* record_buffer)
xdr_buffer.lstr_allocated |= get(tdgbl) << 16; xdr_buffer.lstr_allocated |= get(tdgbl) << 16;
xdr_buffer.lstr_allocated |= get(tdgbl) << 24; xdr_buffer.lstr_allocated |= get(tdgbl) << 24;
lcount = xdr_buffer.lstr_length = xdr_buffer.lstr_allocated; lcount = xdr_buffer.lstr_length = xdr_buffer.lstr_allocated;
xdr_buffer.lstr_address = BURP_alloc (xdr_buffer.lstr_allocated); p = xdr_buffer.lstr_address = BURP_alloc(xdr_buffer.lstr_allocated);
xdr_slice.lstr_allocated = xdr_slice.lstr_length = return_length; xdr_slice.lstr_allocated = xdr_slice.lstr_length = return_length;
xdr_slice.lstr_address = buffer; buffer = &xdr_slice.lstr_address;
p = xdr_buffer.lstr_address;
} }
} }
else else
{ {
p = buffer; buffer = &p;
lcount = return_length; lcount = return_length;
} }
fb_assert(buffer);
*buffer = BURP_alloc (return_length);
if (lcount) if (lcount)
get_block(tdgbl, p, lcount); get_block(tdgbl, p, lcount);
@ -2153,7 +2168,7 @@ void get_array(BurpGlobals* tdgbl, burp_rel* relation, UCHAR* record_buffer)
DB->putSlice(&status_vector, gds_trans, blob_id, blr_length, blr_buffer, DB->putSlice(&status_vector, gds_trans, blob_id, blr_length, blr_buffer,
0, NULL, // parameters for subset of an array handling 0, NULL, // parameters for subset of an array handling
return_length, buffer); return_length, *buffer);
if (status_vector->hasData()) if (status_vector->hasData())
{ {
BURP_print (false, 81, field->fld_name); BURP_print (false, 81, field->fld_name);
@ -2165,10 +2180,6 @@ void get_array(BurpGlobals* tdgbl, burp_rel* relation, UCHAR* record_buffer)
return; return;
} }
} }
BURP_free (buffer);
if (tdgbl->gbl_sw_transportable && xdr_buffer.lstr_allocated)
BURP_free (xdr_buffer.lstr_address);
} }
@ -3199,9 +3210,13 @@ void get_data(BurpGlobals* tdgbl, burp_rel* relation, WriteRelationReq* req)
lstring data; lstring data;
data.lstr_allocated = 0; data.lstr_allocated = 0;
data.lstr_address = NULL; data.lstr_address = NULL;
Array<UCHAR> dataBuffer;
RCRD_LENGTH old_length = 0;
Firebird::Cleanup datClean( [&] {
if (data.lstr_address)
BURP_free(data.lstr_address);
} );
ULONG old_length = 0;
IBatch* batch = req->getBatch(); IBatch* batch = req->getBatch();
UCHAR* sql = batch ? sql = req->getBatchMsgData() : nullptr; UCHAR* sql = batch ? sql = req->getBatchMsgData() : nullptr;
tdgbl->batchInlineBlobLimit = batch ? req->getBatchInlineBlobLimit() : 0; tdgbl->batchInlineBlobLimit = batch ? req->getBatchInlineBlobLimit() : 0;
@ -3247,7 +3262,9 @@ void get_data(BurpGlobals* tdgbl, burp_rel* relation, WriteRelationReq* req)
if (len > data.lstr_allocated) if (len > data.lstr_allocated)
{ {
data.lstr_allocated = len; data.lstr_allocated = len;
data.lstr_address = dataBuffer.getBuffer(data.lstr_allocated); if (data.lstr_address)
BURP_free (data.lstr_address);
data.lstr_address = BURP_alloc(data.lstr_allocated);
} }
p = data.lstr_address; p = data.lstr_address;
} }

View File

@ -32,6 +32,7 @@
#define CLASSES_AUTO_PTR_H #define CLASSES_AUTO_PTR_H
#include <stdio.h> #include <stdio.h>
#include <functional>
namespace Firebird { namespace Firebird {
@ -317,6 +318,22 @@ private:
}; };
class Cleanup
{
public:
Cleanup(std::function<void()> clFunc)
: clean(clFunc)
{ }
~Cleanup()
{
clean();
}
private:
std::function<void()> clean;
};
} //namespace Firebird } //namespace Firebird
#endif // CLASSES_AUTO_PTR_H #endif // CLASSES_AUTO_PTR_H

View File

@ -27,6 +27,7 @@
#include "firebird.h" #include "firebird.h"
#include "init.h" #include "init.h"
#include "alloc.h" #include "alloc.h"
#include "auto.h"
#include "../common/SimpleStatusVector.h" #include "../common/SimpleStatusVector.h"
#include "../common/dllinst.h" #include "../common/dllinst.h"
#include "../common/os/os_utils.h" #include "../common/os/os_utils.h"
@ -142,17 +143,10 @@ namespace
} }
#ifndef DEBUG_INIT #ifndef DEBUG_INIT
// This class with it's single instance ensures global cleanup
class Cleanup
{
public:
~Cleanup()
{
allClean();
}
};
Cleanup global; // This instance ensures dtors run when program exits
Firebird::Cleanup global(allClean);
#endif //DEBUG_INIT #endif //DEBUG_INIT
void init() void init()

View File

@ -1413,6 +1413,16 @@ static int gen_cursor_open( const act* action, const gpre_req* request, int colu
// Generate insertion text for the database statement. // Generate insertion text for the database statement.
// //
static void ifndef(const char* nm, const char* nm2 = "")
{
fprintf(gpreGlob.out_file, "\n#ifndef %s%s", nm, nm2);
}
static void endif()
{
fprintf(gpreGlob.out_file, "\n#endif");
}
static void gen_database(int column) static void gen_database(int column)
{ {
if (global_first_flag) if (global_first_flag)
@ -1427,8 +1437,10 @@ static void gen_database(int column)
fprintf(gpreGlob.out_file, "#define CAST_CONST_MSG(A) (reinterpret_cast<const unsigned char*>(A))\n"); fprintf(gpreGlob.out_file, "#define CAST_CONST_MSG(A) (reinterpret_cast<const unsigned char*>(A))\n");
fprintf(gpreGlob.out_file, "#define CAST_MSG(A) (reinterpret_cast<unsigned char*>(A))\n"); fprintf(gpreGlob.out_file, "#define CAST_MSG(A) (reinterpret_cast<unsigned char*>(A))\n");
ifndef("fbBlobNull");
printa(column, "static %sISC_QUAD", CONST_STR); printa(column, "static %sISC_QUAD", CONST_STR);
printa(column + INDENT, "fbBlobNull = {0, 0};\t/* initializer for blobs */"); printa(column + INDENT, "fbBlobNull = {0, 0};\t/* initializer for blobs */");
endif();
const TEXT* scope = ""; const TEXT* scope = "";
@ -1440,6 +1452,8 @@ static void gen_database(int column)
{ {
all_static = all_static && (db->dbb_scope == DBB_STATIC); all_static = all_static && (db->dbb_scope == DBB_STATIC);
all_extern = all_extern && (db->dbb_scope == DBB_EXTERN); all_extern = all_extern && (db->dbb_scope == DBB_EXTERN);
ifndef(db->dbb_name->sym_string);
if (db->dbb_scope == DBB_STATIC) if (db->dbb_scope == DBB_STATIC)
scope = "static "; scope = "static ";
else if (db->dbb_scope == DBB_EXTERN) else if (db->dbb_scope == DBB_EXTERN)
@ -1449,6 +1463,7 @@ static void gen_database(int column)
printa(column + INDENT, "%s = 0;\t\t/* database handle */\n", db->dbb_name->sym_string); printa(column + INDENT, "%s = 0;\t\t/* database handle */\n", db->dbb_name->sym_string);
else else
printa(column + INDENT, "%s;\t\t/* database handle */\n", db->dbb_name->sym_string); printa(column + INDENT, "%s;\t\t/* database handle */\n", db->dbb_name->sym_string);
endif();
} }
if (all_static) if (all_static)
@ -1456,6 +1471,7 @@ static void gen_database(int column)
else if (all_extern) else if (all_extern)
scope = "extern "; scope = "extern ";
ifndef(gpreGlob.transaction_name);
printa(column, "%sFirebird::ITransaction*", scope); printa(column, "%sFirebird::ITransaction*", scope);
if (!all_extern) if (!all_extern)
printa(column + INDENT, "%s = 0;\t\t/* default transaction handle */", printa(column + INDENT, "%s = 0;\t\t/* default transaction handle */",
@ -1463,33 +1479,43 @@ static void gen_database(int column)
else else
printa(column + INDENT, "%s;\t\t/* default transaction handle */", printa(column + INDENT, "%s;\t\t/* default transaction handle */",
gpreGlob.transaction_name); gpreGlob.transaction_name);
endif();
ifndef("fbMaster");
printa(column, "%sFirebird::IMaster* fbMaster%s;\t\t/* master interface */", printa(column, "%sFirebird::IMaster* fbMaster%s;\t\t/* master interface */",
scope, all_extern ? "" : " = Firebird::fb_get_master_interface()"); scope, all_extern ? "" : " = Firebird::fb_get_master_interface()");
endif();
ifndef("fbProvider");
printa(column, "%sFirebird::IProvider* fbProvider%s;\t\t/* provider interface */", printa(column, "%sFirebird::IProvider* fbProvider%s;\t\t/* provider interface */",
scope, all_extern ? "" : " = fbMaster->getDispatcher()"); scope, all_extern ? "" : " = fbMaster->getDispatcher()");
endif();
ifndef(global_status_name);
printa(column, "%sFirebird::CheckStatusWrapper %sObj%s;\t/* status vector */", printa(column, "%sFirebird::CheckStatusWrapper %sObj%s;\t/* status vector */",
scope, global_status_name, all_extern ? "" : "(fbMaster->getStatus())"); scope, global_status_name, all_extern ? "" : "(fbMaster->getStatus())");
printa(column, "%sFirebird::CheckStatusWrapper %s2Obj%s;\t/* status vector */",
scope, global_status_name, all_extern ? "" : "(fbMaster->getStatus())");
if (all_extern) if (all_extern)
{
printa(column, "%sFirebird::CheckStatusWrapper* %s;\t/* status vector */", printa(column, "%sFirebird::CheckStatusWrapper* %s;\t/* status vector */",
scope, global_status_name); scope, global_status_name);
printa(column, "%sFirebird::CheckStatusWrapper* %s2;\t/* status vector */",
scope, global_status_name);
}
else else
{
printa(column, "%sFirebird::CheckStatusWrapper* %s = &%sObj;\t/* status vector */", printa(column, "%sFirebird::CheckStatusWrapper* %s = &%sObj;\t/* status vector */",
scope, global_status_name, global_status_name); scope, global_status_name, global_status_name);
endif();
ifndef(global_status_name, "2");
printa(column, "%sFirebird::CheckStatusWrapper %s2Obj%s;\t/* status vector */",
scope, global_status_name, all_extern ? "" : "(fbMaster->getStatus())");
if (all_extern)
printa(column, "%sFirebird::CheckStatusWrapper* %s2;\t/* status vector */",
scope, global_status_name);
else
printa(column, "%sFirebird::CheckStatusWrapper* %s2 = &%s2Obj;\t/* status vector */", printa(column, "%sFirebird::CheckStatusWrapper* %s2 = &%s2Obj;\t/* status vector */",
scope, global_status_name, global_status_name); scope, global_status_name, global_status_name);
} endif();
ifndef("fbIStatus");
printa(column, "%sint fbIStatus;\t/* last completion code */", scope); printa(column, "%sint fbIStatus;\t/* last completion code */", scope);
endif();
for (db = gpreGlob.isc_databases; db; db = db->dbb_next) for (db = gpreGlob.isc_databases; db; db = db->dbb_next)
for (const tpb* tpb_iterator = db->dbb_tpbs; tpb_iterator; for (const tpb* tpb_iterator = db->dbb_tpbs; tpb_iterator;

View File

@ -493,6 +493,15 @@ namespace Jrd
// Database::GlobalObjectHolder class implementation // Database::GlobalObjectHolder class implementation
int Database::GlobalObjectHolder::release() const
{
// Release should be executed under g_mutex protection
// in order to modify reference counter & hash table atomically
MutexLockGuard guard(g_mutex, FB_FUNCTION);
return RefCounted::release();
}
Database::GlobalObjectHolder* Database::GlobalObjectHolder::init(const string& id, Database::GlobalObjectHolder* Database::GlobalObjectHolder::init(const string& id,
const PathName& filename, const PathName& filename,
RefPtr<const Config> config) RefPtr<const Config> config)
@ -512,17 +521,18 @@ namespace Jrd
Database::GlobalObjectHolder::~GlobalObjectHolder() Database::GlobalObjectHolder::~GlobalObjectHolder()
{ {
// here we cleanup what should not be globally protected // dtor is executed under g_mutex protection
if (m_replMgr)
m_replMgr->shutdown();
MutexLockGuard guard(g_mutex, FB_FUNCTION);
Database::GlobalObjectHolder::DbId* entry = g_hashTable->lookup(m_id); Database::GlobalObjectHolder::DbId* entry = g_hashTable->lookup(m_id);
if (!g_hashTable->remove(m_id)) if (!g_hashTable->remove(m_id))
fb_assert(false); fb_assert(false);
// these objects should be deleted under g_mutex protection { // scope
// here we cleanup what should not be globally protected
MutexUnlockGuard guard(g_mutex, FB_FUNCTION);
if (m_replMgr)
m_replMgr->shutdown();
}
m_lockMgr = nullptr; m_lockMgr = nullptr;
m_eventMgr = nullptr; m_eventMgr = nullptr;
m_replMgr = nullptr; m_replMgr = nullptr;

View File

@ -306,6 +306,8 @@ class Database : public pool_alloc<type_dbb>
const Firebird::PathName& filename, const Firebird::PathName& filename,
Firebird::RefPtr<const Firebird::Config> config); Firebird::RefPtr<const Firebird::Config> config);
int release() const override;
~GlobalObjectHolder(); ~GlobalObjectHolder();
LockManager* getLockManager(); LockManager* getLockManager();

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

@ -3,16 +3,16 @@
*** DO NOT EDIT *** *** DO NOT EDIT ***
TO CHANGE ANY INFORMATION IN HERE PLEASE TO CHANGE ANY INFORMATION IN HERE PLEASE
EDIT src/misc/writeBuildNum.sh EDIT src/misc/writeBuildNum.sh
FORMAL BUILD NUMBER:501 FORMAL BUILD NUMBER:516
*/ */
#define PRODUCT_VER_STRING "5.0.0.501" #define PRODUCT_VER_STRING "5.0.0.516"
#define FILE_VER_STRING "WI-T5.0.0.501" #define FILE_VER_STRING "WI-T5.0.0.516"
#define LICENSE_VER_STRING "WI-T5.0.0.501" #define LICENSE_VER_STRING "WI-T5.0.0.516"
#define FILE_VER_NUMBER 5, 0, 0, 501 #define FILE_VER_NUMBER 5, 0, 0, 516
#define FB_MAJOR_VER "5" #define FB_MAJOR_VER "5"
#define FB_MINOR_VER "0" #define FB_MINOR_VER "0"
#define FB_REV_NO "0" #define FB_REV_NO "0"
#define FB_BUILD_NO "501" #define FB_BUILD_NO "516"
#define FB_BUILD_TYPE "T" #define FB_BUILD_TYPE "T"
#define FB_BUILD_SUFFIX "Firebird 5.0 Initial" #define FB_BUILD_SUFFIX "Firebird 5.0 Initial"

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"
#include "../jrd/WorkerAttachment.h" #include "../jrd/WorkerAttachment.h"
@ -127,6 +128,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"
@ -475,6 +477,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;
@ -1329,7 +1341,7 @@ static JAttachment* initAttachment(thread_db*, const PathName&, const PathName&,
const DatabaseOptions&, RefMutexUnlock&, IPluginConfig*, JProvider*); const DatabaseOptions&, RefMutexUnlock&, IPluginConfig*, JProvider*);
static JAttachment* create_attachment(const PathName&, Database*, JProvider* provider, const DatabaseOptions&, bool newDb); static JAttachment* create_attachment(const PathName&, Database*, JProvider* provider, const DatabaseOptions&, bool newDb);
static void prepare_tra(thread_db*, jrd_tra*, USHORT, const UCHAR*); static void prepare_tra(thread_db*, jrd_tra*, USHORT, const UCHAR*);
static void release_attachment(thread_db*, Attachment*); static void release_attachment(thread_db*, Attachment*, XThreadEnsureUnlock* = nullptr);
static void start_transaction(thread_db* tdbb, bool transliterate, jrd_tra** tra_handle, static void start_transaction(thread_db* tdbb, bool transliterate, jrd_tra** tra_handle,
Jrd::Attachment* attachment, unsigned int tpb_length, const UCHAR* tpb); Jrd::Attachment* attachment, unsigned int tpb_length, const UCHAR* tpb);
static void rollback(thread_db*, jrd_tra*, const bool); static void rollback(thread_db*, jrd_tra*, const bool);
@ -1541,7 +1553,7 @@ static void trace_warning(thread_db* tdbb, FbStatusVector* userStatus, const cha
static void trace_failed_attach(TraceManager* traceManager, const char* filename, static void trace_failed_attach(TraceManager* traceManager, const char* filename,
const DatabaseOptions& options, bool create, FbStatusVector* status) const DatabaseOptions& options, bool create, FbStatusVector* status, ICryptKeyCallback* callback)
{ {
// Report to Trace API that attachment has not been created // Report to Trace API that attachment has not been created
const char* origFilename = filename; const char* origFilename = filename;
@ -1556,15 +1568,24 @@ static void trace_failed_attach(TraceManager* traceManager, const char* filename
ITracePlugin::RESULT_UNAUTHORIZED : ITracePlugin::RESULT_FAILED; ITracePlugin::RESULT_UNAUTHORIZED : ITracePlugin::RESULT_FAILED;
const char* func = create ? "JProvider::createDatabase" : "JProvider::attachDatabase"; const char* func = create ? "JProvider::createDatabase" : "JProvider::attachDatabase";
Attachment* att = traceManager ? traceManager->getAttachment() : nullptr;
if (traceManager && (! traceManager->isActive()))
traceManager = nullptr;
if (!traceManager) if (!traceManager)
{ {
TraceManager tempMgr(origFilename); if (!options.dpb_map_attach)
{
EngineCheckout guard(att, FB_FUNCTION, true);
if (tempMgr.needs(ITraceFactory::TRACE_EVENT_ATTACH)) TraceManager tempMgr(origFilename, callback);
tempMgr.event_attach(&conn, create, result);
if (tempMgr.needs(ITraceFactory::TRACE_EVENT_ERROR)) if (tempMgr.needs(ITraceFactory::TRACE_EVENT_ATTACH))
tempMgr.event_error(&conn, &traceStatus, func); tempMgr.event_attach(&conn, create, result);
if (tempMgr.needs(ITraceFactory::TRACE_EVENT_ERROR))
tempMgr.event_error(&conn, &traceStatus, func);
}
} }
else else
{ {
@ -1696,7 +1717,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
catch (const Exception& ex) catch (const Exception& ex)
{ {
ex.stuffException(user_status); ex.stuffException(user_status);
trace_failed_attach(NULL, filename, options, false, user_status); trace_failed_attach(NULL, filename, options, false, user_status, cryptCallback);
throw; throw;
} }
@ -1704,7 +1725,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
const VdnResult vdn = verifyDatabaseName(expanded_name, tdbb->tdbb_status_vector, is_alias); const VdnResult vdn = verifyDatabaseName(expanded_name, tdbb->tdbb_status_vector, is_alias);
if (!is_alias && vdn == VDN_FAIL) if (!is_alias && vdn == VDN_FAIL)
{ {
trace_failed_attach(NULL, filename, options, false, tdbb->tdbb_status_vector); trace_failed_attach(NULL, filename, options, false, tdbb->tdbb_status_vector, cryptCallback);
status_exception::raise(tdbb->tdbb_status_vector); status_exception::raise(tdbb->tdbb_status_vector);
} }
@ -2275,9 +2296,8 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
} }
else else
{ {
trace_failed_attach(attachment && attachment->att_trace_manager && trace_failed_attach(attachment ? attachment->att_trace_manager : NULL,
attachment->att_trace_manager->isActive() ? attachment->att_trace_manager : NULL, filename, options, false, user_status, cryptCallback);
filename, options, false, user_status);
} }
mapping.clearMainHandle(); mapping.clearMainHandle();
@ -2878,7 +2898,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch
catch (const Exception& ex) catch (const Exception& ex)
{ {
ex.stuffException(user_status); ex.stuffException(user_status);
trace_failed_attach(NULL, filename, options, true, user_status); trace_failed_attach(NULL, filename, options, true, user_status, cryptCallback);
throw; throw;
} }
@ -2886,7 +2906,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch
const VdnResult vdn = verifyDatabaseName(expanded_name, tdbb->tdbb_status_vector, is_alias); const VdnResult vdn = verifyDatabaseName(expanded_name, tdbb->tdbb_status_vector, is_alias);
if (!is_alias && vdn == VDN_FAIL) if (!is_alias && vdn == VDN_FAIL)
{ {
trace_failed_attach(NULL, filename, options, true, tdbb->tdbb_status_vector); trace_failed_attach(NULL, filename, options, true, tdbb->tdbb_status_vector, cryptCallback);
status_exception::raise(tdbb->tdbb_status_vector); status_exception::raise(tdbb->tdbb_status_vector);
} }
@ -3213,9 +3233,8 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch
catch (const Exception& ex) catch (const Exception& ex)
{ {
ex.stuffException(user_status); ex.stuffException(user_status);
trace_failed_attach(attachment && attachment->att_trace_manager && trace_failed_attach(attachment ? attachment->att_trace_manager : NULL,
attachment->att_trace_manager->isActive() ? attachment->att_trace_manager : NULL, filename, options, true, user_status, cryptCallback);
filename, options, true, user_status);
mapping.clearMainHandle(); mapping.clearMainHandle();
unwindAttach(tdbb, ex, user_status, false); unwindAttach(tdbb, ex, user_status, false);
@ -3425,6 +3444,7 @@ void JAttachment::internalDropDatabase(CheckStatusWrapper* user_status)
// Prepare to set ODS to 0 // Prepare to set ODS to 0
WIN window(HEADER_PAGE_NUMBER); WIN window(HEADER_PAGE_NUMBER);
Ods::header_page* header = NULL; Ods::header_page* header = NULL;
XThreadEnsureUnlock threadGuard(dbb->dbb_thread_mutex, FB_FUNCTION);
try try
{ {
@ -3450,6 +3470,13 @@ void JAttachment::internalDropDatabase(CheckStatusWrapper* user_status)
ERR_post(Arg::Gds(isc_att_shutdown)); ERR_post(Arg::Gds(isc_att_shutdown));
} }
// try to block special threads before taking exclusive lock on database
if (!threadGuard.tryEnter())
{
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_obj_in_use) << Arg::Str("DATABASE"));
}
if (!CCH_exclusive(tdbb, LCK_PW, WAIT_PERIOD, NULL)) if (!CCH_exclusive(tdbb, LCK_PW, WAIT_PERIOD, NULL))
{ {
ERR_post(Arg::Gds(isc_lock_timeout) << ERR_post(Arg::Gds(isc_lock_timeout) <<
@ -3505,7 +3532,7 @@ void JAttachment::internalDropDatabase(CheckStatusWrapper* user_status)
} }
// Unlink attachment from database // Unlink attachment from database
release_attachment(tdbb, attachment); release_attachment(tdbb, attachment, &threadGuard);
att = NULL; att = NULL;
attachment = NULL; attachment = NULL;
guard.leave(); guard.leave();
@ -4581,62 +4608,66 @@ void JProvider::shutdown(CheckStatusWrapper* status, unsigned int timeout, const
**************************************/ **************************************/
try try
{ {
MutexLockGuard guard(shutdownMutex, FB_FUNCTION);
if (engineShutdown)
{
return;
}
{ // scope { // scope
MutexLockGuard guard(newAttachmentMutex, FB_FUNCTION); MutexLockGuard guard(shutdownMutex, FB_FUNCTION);
engineShutdown = true;
if (engineShutdown)
{
return;
}
{ // scope
MutexLockGuard guard(newAttachmentMutex, FB_FUNCTION);
engineShutdown = true;
}
ThreadContextHolder tdbb;
WorkerAttachment::shutdown();
EDS::Manager::shutdown();
ULONG attach_count, database_count, svc_count;
JRD_enum_attachments(NULL, attach_count, database_count, svc_count);
if (attach_count > 0 || svc_count > 0)
{
gds__log("Shutting down the server with %d active connection(s) to %d database(s), "
"%d active service(s)",
attach_count, database_count, svc_count);
}
if (reason == fb_shutrsn_exit_called)
{
// Starting threads may fail when task is going to close.
// This happens at least with some microsoft C runtimes.
// If people wish to have timeout, they should better call fb_shutdown() themselves.
// Therefore:
timeout = 0;
}
if (timeout)
{
Semaphore shutdown_semaphore;
Thread::Handle h;
Thread::start(shutdown_thread, &shutdown_semaphore, THREAD_medium, &h);
if (!shutdown_semaphore.tryEnter(0, timeout))
waitForShutdown(shutdown_semaphore);
Thread::waitForCompletion(h);
}
else
{
shutdown_thread(NULL);
}
// Do not put it into separate shutdown thread - during shutdown of TraceManager
// PluginManager wants to lock a mutex, which is sometimes already locked in current thread
TraceManager::shutdown();
Mapping::shutdownIpc();
} }
ThreadContextHolder tdbb; // Wait for completion of all attacment shutdown threads
shutThreadCollect->join();
WorkerAttachment::shutdown();
EDS::Manager::shutdown();
ULONG attach_count, database_count, svc_count;
JRD_enum_attachments(NULL, attach_count, database_count, svc_count);
if (attach_count > 0 || svc_count > 0)
{
gds__log("Shutting down the server with %d active connection(s) to %d database(s), "
"%d active service(s)",
attach_count, database_count, svc_count);
}
if (reason == fb_shutrsn_exit_called)
{
// Starting threads may fail when task is going to close.
// This happens at least with some microsoft C runtimes.
// If people wish to have timeout, they should better call fb_shutdown() themselves.
// Therefore:
timeout = 0;
}
if (timeout)
{
Semaphore shutdown_semaphore;
Thread::Handle h;
Thread::start(shutdown_thread, &shutdown_semaphore, THREAD_medium, &h);
if (!shutdown_semaphore.tryEnter(0, timeout))
waitForShutdown(shutdown_semaphore);
Thread::waitForCompletion(h);
}
else
{
shutdown_thread(NULL);
}
// Do not put it into separate shutdown thread - during shutdown of TraceManager
// PluginManager wants to lock a mutex, which is sometimes already locked in current thread
TraceManager::shutdown();
Mapping::shutdownIpc();
} }
catch (const Exception& ex) catch (const Exception& ex)
{ {
@ -7605,7 +7636,7 @@ static void prepare_tra(thread_db* tdbb, jrd_tra* transaction, USHORT length, co
} }
void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment) void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment, XThreadEnsureUnlock* dropGuard)
{ {
/************************************** /**************************************
* *
@ -7686,8 +7717,14 @@ void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment)
Sync sync(&dbb->dbb_sync, "jrd.cpp: release_attachment"); Sync sync(&dbb->dbb_sync, "jrd.cpp: release_attachment");
// avoid races with special threads // avoid races with special threads
// take into an account lock earlier taken in DROP DATABASE
XThreadEnsureUnlock threadGuard(dbb->dbb_thread_mutex, FB_FUNCTION); XThreadEnsureUnlock threadGuard(dbb->dbb_thread_mutex, FB_FUNCTION);
threadGuard.enter(); XThreadEnsureUnlock* activeThreadGuard = dropGuard;
if (!activeThreadGuard)
{
threadGuard.enter();
activeThreadGuard = &threadGuard;
}
sync.lock(SYNC_EXCLUSIVE); sync.lock(SYNC_EXCLUSIVE);
@ -7721,7 +7758,7 @@ void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment)
} }
// Notify special threads // Notify special threads
threadGuard.leave(); activeThreadGuard->leave();
// Sync with special threads // Sync with special threads
if (!other) if (!other)
@ -8725,22 +8762,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
@ -9527,13 +9568,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
@ -9582,7 +9628,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

@ -1092,10 +1092,10 @@ namespace Jrd {
m_ref->getSync()->leave(); m_ref->getSync()->leave();
} }
EngineCheckout(Attachment* att, const char* from) EngineCheckout(Attachment* att, const char* from, bool optional = false)
: m_tdbb(nullptr), m_from(from) : m_tdbb(nullptr), m_from(from)
{ {
fb_assert(att); fb_assert(optional || att);
if (att && att->att_use_count) if (att && att->att_use_count)
{ {

View File

@ -200,7 +200,7 @@ Sort::Sort(Database* dbb,
m_longs = record_size >> SHIFTLONG; m_longs = record_size >> SHIFTLONG;
m_min_alloc_size = record_size * MIN_RECORDS_TO_ALLOC; m_min_alloc_size = record_size * MIN_RECORDS_TO_ALLOC;
m_max_alloc_size = MAX(record_size * MIN_RECORDS_TO_ALLOC, MAX_SORT_BUFFER_SIZE); m_max_alloc_size = MAX(m_min_alloc_size, MAX_SORT_BUFFER_SIZE);
m_dup_callback = call_back; m_dup_callback = call_back;
m_dup_callback_arg = user_arg; m_dup_callback_arg = user_arg;
@ -619,6 +619,7 @@ void Sort::allocateBuffer(MemoryPool& pool)
// The sort buffer cache has at least one big block, let's use it // The sort buffer cache has at least one big block, let's use it
m_size_memory = MAX_SORT_BUFFER_SIZE; m_size_memory = MAX_SORT_BUFFER_SIZE;
m_memory = m_dbb->dbb_sort_buffers.pop(); m_memory = m_dbb->dbb_sort_buffers.pop();
m_flags |= scb_reuse_buffer;
return; return;
} }
} }
@ -635,6 +636,10 @@ void Sort::allocateBuffer(MemoryPool& pool)
{ {
m_size_memory = m_max_alloc_size; m_size_memory = m_max_alloc_size;
m_memory = FB_NEW_POOL(*m_dbb->dbb_permanent) UCHAR[m_size_memory]; m_memory = FB_NEW_POOL(*m_dbb->dbb_permanent) UCHAR[m_size_memory];
// Mark the buffer as cacheable for future reuse
if (m_size_memory == MAX_SORT_BUFFER_SIZE)
m_flags |= scb_reuse_buffer;
} }
catch (const BadAlloc&) catch (const BadAlloc&)
{ {
@ -646,6 +651,7 @@ void Sort::allocateBuffer(MemoryPool& pool)
{ {
m_size_memory /= 2; m_size_memory /= 2;
m_memory = FB_NEW_POOL(pool) UCHAR[m_size_memory]; m_memory = FB_NEW_POOL(pool) UCHAR[m_size_memory];
m_flags &= ~scb_reuse_buffer;
break; break;
} }
catch (const BadAlloc&) catch (const BadAlloc&)
@ -666,13 +672,17 @@ void Sort::releaseBuffer()
SyncLockGuard guard(&m_dbb->dbb_sortbuf_sync, SYNC_EXCLUSIVE, "Sort::releaseBuffer"); SyncLockGuard guard(&m_dbb->dbb_sortbuf_sync, SYNC_EXCLUSIVE, "Sort::releaseBuffer");
if (m_size_memory == MAX_SORT_BUFFER_SIZE && if ((m_flags & scb_reuse_buffer) &&
m_dbb->dbb_sort_buffers.getCount() < MAX_CACHED_SORT_BUFFERS) m_dbb->dbb_sort_buffers.getCount() < MAX_CACHED_SORT_BUFFERS)
{ {
fb_assert(m_size_memory == MAX_SORT_BUFFER_SIZE);
m_dbb->dbb_sort_buffers.push(m_memory); m_dbb->dbb_sort_buffers.push(m_memory);
} }
else else
delete[] m_memory; delete[] m_memory;
m_flags &= ~scb_reuse_buffer;
} }

View File

@ -270,7 +270,8 @@ typedef bool (*FPTR_REJECT_DUP_CALLBACK)(const UCHAR*, const UCHAR*, void*);
// flags as set in m_flags // flags as set in m_flags
const int scb_sorted = 1; // stream has been sorted const int scb_sorted = 1; // stream has been sorted
const int scb_reuse_buffer = 2; // reuse buffer if possible
class Sort class Sort
{ {

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)
{ {

View File

@ -4061,13 +4061,17 @@ void jrd_tra::checkBlob(thread_db* tdbb, const bid* blob_id, jrd_fld* fld, bool
vec<jrd_rel*>* vector = tra_attachment->att_relations; vec<jrd_rel*>* vector = tra_attachment->att_relations;
jrd_rel* blb_relation; jrd_rel* blb_relation;
if (rel_id < vector->count() && (blb_relation = (*vector)[rel_id])) if ((rel_id < vector->count() && (blb_relation = (*vector)[rel_id])) ||
(blb_relation = MET_relation(tdbb, rel_id)))
{ {
const MetaName security_name = fld ? MetaName security_name = (fld && fld->fld_security_name.hasData()) ?
fld->fld_security_name : blb_relation->rel_security_name; fld->fld_security_name : blb_relation->rel_security_name;
if (security_name.isEmpty()) if (security_name.isEmpty())
{
MET_scan_relation(tdbb, blb_relation); MET_scan_relation(tdbb, blb_relation);
security_name = blb_relation->rel_security_name;
}
SecurityClass* s_class = SCL_get_class(tdbb, security_name.c_str()); SecurityClass* s_class = SCL_get_class(tdbb, security_name.c_str());

View File

@ -88,6 +88,7 @@ TraceManager::TraceManager(Attachment* in_att) :
attachment(in_att), attachment(in_att),
service(NULL), service(NULL),
filename(NULL), filename(NULL),
callback(NULL),
trace_sessions(*in_att->att_pool), trace_sessions(*in_att->att_pool),
active(false) active(false)
{ {
@ -98,16 +99,18 @@ TraceManager::TraceManager(Service* in_svc) :
attachment(NULL), attachment(NULL),
service(in_svc), service(in_svc),
filename(NULL), filename(NULL),
callback(NULL),
trace_sessions(in_svc->getPool()), trace_sessions(in_svc->getPool()),
active(true) active(true)
{ {
init(); init();
} }
TraceManager::TraceManager(const char* in_filename) : TraceManager::TraceManager(const char* in_filename, ICryptKeyCallback* cb) :
attachment(NULL), attachment(NULL),
service(NULL), service(NULL),
filename(in_filename), filename(in_filename),
callback(cb),
trace_sessions(*getDefaultMemoryPool()), trace_sessions(*getDefaultMemoryPool()),
active(true) active(true)
{ {
@ -251,7 +254,8 @@ void TraceManager::update_session(const TraceSession& session)
} }
// if this session is not from administrator, it may trace connections // if this session is not from administrator, it may trace connections
// only created by the same user // only created by the same user, or when it has TRACE_ANY_ATTACHMENT
// privilege in current context
if (!(session.ses_flags & (trs_admin | trs_system))) if (!(session.ses_flags & (trs_admin | trs_system)))
{ {
const char* curr_user = nullptr; const char* curr_user = nullptr;
@ -265,10 +269,11 @@ void TraceManager::update_session(const TraceSession& session)
if (attachment) if (attachment)
{ {
if ((!attachment->att_user) || (attachment->att_flags & ATT_mapping)) if (attachment->att_flags & ATT_mapping)
return; return;
curr_user = attachment->getUserName().c_str(); if (attachment->att_user)
curr_user = attachment->att_user->getUserName().c_str();
if (session.ses_auth.hasData()) if (session.ses_auth.hasData())
{ {
@ -290,7 +295,7 @@ void TraceManager::update_session(const TraceSession& session)
} }
else if (service) else if (service)
{ {
curr_user = service->getUserName().c_str(); curr_user = service->getUserName().nullStr();
if (session.ses_auth.hasData()) if (session.ses_auth.hasData())
{ {
@ -308,6 +313,26 @@ void TraceManager::update_session(const TraceSession& session)
mapResult = mapping.mapUser(s_user, t_role); mapResult = mapping.mapUser(s_user, t_role);
} }
} }
else if (filename)
{
if (session.ses_auth.hasData())
{
Mapping mapping(Mapping::MAP_NO_FLAGS, callback);
mapping.needSystemPrivileges(priv);
mapping.setAuthBlock(session.ses_auth);
mapping.setSqlRole(session.ses_role);
RefPtr<const Config> config;
PathName org_filename(filename), expanded_name;
if (! expandDatabaseName(org_filename, expanded_name, &config))
expanded_name = filename;
mapping.setSecurityDbAlias(config->getSecurityDatabase(), expanded_name.c_str());
mapping.setDb(filename, expanded_name.c_str(), nullptr);
mapResult = mapping.mapUser(s_user, t_role);
}
}
else else
{ {
// failed attachment attempts traced by admin trace only // failed attachment attempts traced by admin trace only
@ -327,7 +352,7 @@ void TraceManager::update_session(const TraceSession& session)
t_role.upper(); t_role.upper();
if (s_user != DBA_USER_NAME && t_role != ADMIN_ROLE && if (s_user != DBA_USER_NAME && t_role != ADMIN_ROLE &&
s_user != curr_user && (!priv.test(TRACE_ANY_ATTACHMENT))) ((!curr_user) || (s_user != curr_user)) && (!priv.test(TRACE_ANY_ATTACHMENT)))
{ {
return; return;
} }

View File

@ -39,6 +39,12 @@
#include "../../jrd/trace/TraceConfigStorage.h" #include "../../jrd/trace/TraceConfigStorage.h"
#include "../../jrd/trace/TraceSession.h" #include "../../jrd/trace/TraceSession.h"
namespace Firebird {
class ICryptKeyCallback;
}
namespace Jrd { namespace Jrd {
class Database; class Database;
@ -52,7 +58,7 @@ public:
/* Initializes plugins. */ /* Initializes plugins. */
explicit TraceManager(Attachment* in_att); explicit TraceManager(Attachment* in_att);
explicit TraceManager(Service* in_svc); explicit TraceManager(Service* in_svc);
explicit TraceManager(const char* in_filename); TraceManager(const char* in_filename, Firebird::ICryptKeyCallback* callback);
/* Finalize plugins. Called when database is closed by the engine */ /* Finalize plugins. Called when database is closed by the engine */
~TraceManager(); ~TraceManager();
@ -142,6 +148,12 @@ public:
return active; return active;
} }
// external access to stored attachment
Attachment* getAttachment()
{
return attachment;
}
/* DSQL-friendly routines to call Trace API hooks. /* DSQL-friendly routines to call Trace API hooks.
Needed because DSQL cannot include JRD for the current engine */ Needed because DSQL cannot include JRD for the current engine */
static bool need_dsql_prepare(Attachment* att); static bool need_dsql_prepare(Attachment* att);
@ -163,6 +175,7 @@ private:
Attachment* attachment; Attachment* attachment;
Service* service; Service* service;
const char* filename; const char* filename;
Firebird::ICryptKeyCallback* callback;
NotificationNeeds trace_needs, new_needs; NotificationNeeds trace_needs, new_needs;
// This structure should be POD-like to be stored in Array // This structure should be POD-like to be stored in Array

View File

@ -9,7 +9,7 @@ BuildType=T
MajorVer=5 MajorVer=5
MinorVer=0 MinorVer=0
RevNo=0 RevNo=0
BuildNum=501 BuildNum=516
NowAt=`pwd` NowAt=`pwd`
cd `dirname $0` cd `dirname $0`

View File

@ -66,6 +66,10 @@ public:
void deprecatedDisconnect(CheckStatusWrapper* status); void deprecatedDisconnect(CheckStatusWrapper* status);
private: private:
void internalCommit(CheckStatusWrapper* status);
void internalRollback(CheckStatusWrapper* status);
void internalDisconnect(CheckStatusWrapper* status);
typedef HalfStaticArray<ITransaction*, 8> SubArray; typedef HalfStaticArray<ITransaction*, 8> SubArray;
typedef HalfStaticArray<UCHAR, 1024> TdrBuffer; typedef HalfStaticArray<UCHAR, 1024> TdrBuffer;
SubArray sub; SubArray sub;
@ -248,7 +252,7 @@ void DTransaction::prepare(CheckStatusWrapper* status,
} }
} }
void DTransaction::commit(CheckStatusWrapper* status) void DTransaction::internalCommit(CheckStatusWrapper* status)
{ {
try try
{ {
@ -309,7 +313,7 @@ void DTransaction::commitRetaining(CheckStatusWrapper* status)
} }
} }
void DTransaction::rollback(CheckStatusWrapper* status) void DTransaction::internalRollback(CheckStatusWrapper* status)
{ {
try try
{ {
@ -361,7 +365,7 @@ void DTransaction::rollbackRetaining(CheckStatusWrapper* status)
} }
} }
void DTransaction::disconnect(CheckStatusWrapper* status) void DTransaction::internalDisconnect(CheckStatusWrapper* status)
{ {
try try
{ {
@ -392,17 +396,38 @@ void DTransaction::disconnect(CheckStatusWrapper* status)
void DTransaction::deprecatedCommit(CheckStatusWrapper* status) void DTransaction::deprecatedCommit(CheckStatusWrapper* status)
{ {
commit(status); internalCommit(status);
} }
void DTransaction::deprecatedRollback(CheckStatusWrapper* status) void DTransaction::deprecatedRollback(CheckStatusWrapper* status)
{ {
rollback(status); internalRollback(status);
} }
void DTransaction::deprecatedDisconnect(CheckStatusWrapper* status) void DTransaction::deprecatedDisconnect(CheckStatusWrapper* status)
{ {
disconnect(status); internalDisconnect(status);
}
void DTransaction::disconnect(CheckStatusWrapper* status)
{
internalDisconnect(status);
if (status->isEmpty())
release();
}
void DTransaction::rollback(CheckStatusWrapper* status)
{
internalRollback(status);
if (status->isEmpty())
release();
}
void DTransaction::commit(CheckStatusWrapper* status)
{
internalCommit(status);
if (status->isEmpty())
release();
} }

View File

@ -5432,8 +5432,6 @@ YTransaction* YTransaction::enterDtc(CheckStatusWrapper* status)
YEntry<YTransaction> entry(status, this); YEntry<YTransaction> entry(status, this);
YTransaction* copy = FB_NEW YTransaction(this); YTransaction* copy = FB_NEW YTransaction(this);
// copy is created with zero handle
copy->addRef();
copy->addRef(); copy->addRef();
next->addRef(); // We use NoIncr in YTransaction ctor next->addRef(); // We use NoIncr in YTransaction ctor