8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 18:03:04 +01:00
firebird-mirror/src/common/fb_exception.cpp

451 lines
9.8 KiB
C++
Raw Normal View History

2003-02-17 14:28:17 +01:00
#include "firebird.h"
//#include "fb_exception.h"
2002-11-11 19:06:01 +01:00
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include "gen/iberror.h"
#include "../common/classes/alloc.h"
#include "../common/classes/init.h"
#include "../common/classes/array.h"
#include "../common/thd.h"
#ifdef WIN_NT
#include <windows.h>
#endif
namespace {
class StringsBuffer
{
private:
class ThreadBuffer : public Firebird::GlobalStorage
{
private:
const static size_t BUFFER_SIZE = 4096;
char buffer[BUFFER_SIZE];
char* buffer_ptr;
FB_THREAD_ID thread;
public:
2009-12-01 06:28:32 +01:00
explicit ThreadBuffer(FB_THREAD_ID thr) : buffer_ptr(buffer), thread(thr) { }
const char* alloc(const char* string, size_t& length)
{
// if string is already in our buffer - return it
// it was already saved in our buffer once
if (string >= buffer && string < &buffer[BUFFER_SIZE])
return string;
// if string too long, truncate it
if (length > BUFFER_SIZE / 4)
length = BUFFER_SIZE / 4;
// If there isn't any more room in the buffer, start at the beginning again
if (buffer_ptr + length + 1 > buffer + BUFFER_SIZE)
buffer_ptr = buffer;
char* new_string = buffer_ptr;
memcpy(new_string, string, length);
new_string[length] = 0;
buffer_ptr += length + 1;
return new_string;
}
bool thisThread(FB_THREAD_ID currTID)
{
#ifdef WIN_NT
2009-09-01 11:44:19 +02:00
if (thread != currTID)
{
HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, false, thread);
2009-09-03 03:28:54 +02:00
// commented exit code check - looks like OS does not return handle
2009-09-02 11:04:10 +02:00
// for already exited thread
//DWORD exitCode = STILL_ACTIVE;
2009-09-01 11:44:19 +02:00
if (hThread)
{
2009-09-02 11:04:10 +02:00
//GetExitCodeThread(hThread, &exitCode);
2009-09-01 11:44:19 +02:00
CloseHandle(hThread);
}
2009-09-03 03:28:54 +02:00
2009-09-02 11:04:10 +02:00
//if ((!hThread) || (exitCode != STILL_ACTIVE))
2009-09-01 11:44:19 +02:00
if (!hThread)
{
// Thread does not exist any more
thread = currTID;
}
}
#endif
2009-09-03 03:28:54 +02:00
2009-09-01 11:44:19 +02:00
return thread == currTID;
}
};
typedef Firebird::Array<ThreadBuffer*> ProcessBuffer;
ProcessBuffer processBuffer;
Firebird::Mutex mutex;
2004-03-07 08:58:55 +01:00
public:
2009-09-02 11:04:10 +02:00
explicit StringsBuffer(Firebird::MemoryPool& p) : processBuffer(p) { }
~StringsBuffer()
2005-12-25 05:01:49 +01:00
{
ThreadCleanup::remove(cleanupAllStrings, this);
}
private:
size_t position(FB_THREAD_ID thr)
{
// mutex should be locked when this function is called
for (size_t i = 0; i < processBuffer.getCount(); ++i)
{
if (processBuffer[i]->thisThread(thr))
{
return i;
}
}
2002-11-11 19:06:01 +01:00
return processBuffer.getCount();
}
ThreadBuffer* getThreadBuffer(FB_THREAD_ID thr)
2006-04-06 10:18:53 +02:00
{
Firebird::MutexLockGuard guard(mutex);
2004-03-07 08:58:55 +01:00
size_t p = position(thr);
if (p < processBuffer.getCount())
{
return processBuffer[p];
}
ThreadBuffer* b = new ThreadBuffer(thr);
processBuffer.add(b);
return b;
}
void cleanup()
{
Firebird::MutexLockGuard guard(mutex);
size_t p = position(getThreadId());
if (p >= processBuffer.getCount())
2009-02-03 12:02:00 +01:00
{
return;
}
delete processBuffer[p];
processBuffer.remove(p);
2008-12-05 02:20:14 +01:00
}
2002-11-11 19:06:01 +01:00
static void cleanupAllStrings(void* toClean)
{
static_cast<StringsBuffer*>(toClean)->cleanup();
}
public:
const char* alloc(const char* s, size_t& len, FB_THREAD_ID thr = getThreadId())
{
ThreadCleanup::add(cleanupAllStrings, this);
return getThreadBuffer(thr)->alloc(s, len);
}
};
Firebird::GlobalPtr<StringsBuffer> allStrings;
} // namespace
namespace Firebird {
// Before using thr parameter, make sure that thread is not going to work with
// this functions itself.
2009-04-26 12:24:44 +02:00
// CVC: Do not let "perm" be incremented before "trans", because it may lead to serious memory errors,
// since several places in our code blindly pass the same vector twice.
void makePermanentVector(ISC_STATUS* perm, const ISC_STATUS* trans, FB_THREAD_ID thr) throw()
{
try
{
while (true)
2009-02-03 12:02:00 +01:00
{
const ISC_STATUS type = *perm++ = *trans++;
switch (type)
{
case isc_arg_end:
return;
case isc_arg_cstring:
{
size_t len = *perm++ = *trans++;
const char* temp = reinterpret_cast<char*>(*trans++);
*perm++ = (ISC_STATUS)(IPTR) (allStrings->alloc(temp, len, thr));
2010-02-07 11:29:08 +01:00
perm[-2] = len;
}
break;
case isc_arg_string:
case isc_arg_interpreted:
case isc_arg_sql_state:
{
const char* temp = reinterpret_cast<char*>(*trans++);
size_t len = strlen(temp);
*perm++ = (ISC_STATUS)(IPTR) (allStrings->alloc(temp, len, thr));
}
break;
default:
*perm++ = *trans++;
break;
}
}
}
catch (const system_call_failed& ex)
{
memcpy(perm, ex.value(), sizeof(ISC_STATUS_ARRAY));
}
catch (const BadAlloc& ex)
{
ex.stuff_exception(perm);
}
catch (...)
{
*perm++ = isc_arg_gds;
*perm++ = isc_random;
*perm++ = isc_arg_string;
*perm++ = (ISC_STATUS)(IPTR) "Unexpected exception in makePermanentVector()";
*perm++ = isc_arg_end;
}
}
void makePermanentVector(ISC_STATUS* v, FB_THREAD_ID thr) throw()
{
makePermanentVector(v, v, thr);
}
// ********************************* Exception *******************************
Exception::~Exception() throw() { }
2009-04-17 16:10:11 +02:00
// ********************************* status_exception *******************************
status_exception::status_exception() throw()
2004-03-07 08:58:55 +01:00
{
memset(m_status_vector, 0, sizeof(m_status_vector));
}
status_exception::status_exception(const ISC_STATUS *status_vector) throw()
2004-03-07 08:58:55 +01:00
{
memset(m_status_vector, 0, sizeof(m_status_vector));
if (status_vector)
{
set_status(status_vector);
}
}
2008-12-05 02:20:14 +01:00
void status_exception::set_status(const ISC_STATUS *new_vector) throw()
2005-12-25 05:01:49 +01:00
{
fb_assert(new_vector != 0);
2004-03-07 08:58:55 +01:00
makePermanentVector(m_status_vector, new_vector);
}
2008-12-05 02:20:14 +01:00
status_exception::~status_exception() throw()
{
}
const char* status_exception::what() const throw()
2005-12-25 05:01:49 +01:00
{
return "Firebird::status_exception";
}
2008-12-05 02:20:14 +01:00
void status_exception::raise(const ISC_STATUS *status_vector)
2004-03-07 08:58:55 +01:00
{
throw status_exception(status_vector);
}
void status_exception::raise(const Arg::StatusVector& statusVector)
2004-03-07 08:58:55 +01:00
{
throw status_exception(statusVector.value());
}
ISC_STATUS status_exception::stuff_exception(ISC_STATUS* const status_vector) const throw()
{
const ISC_STATUS *ptr = value();
ISC_STATUS *sv = status_vector;
// Copy status vector
while (true)
{
const ISC_STATUS type = *sv++ = *ptr++;
if (type == isc_arg_end)
break;
if (type == isc_arg_cstring)
*sv++ = *ptr++;
*sv++ = *ptr++;
}
return status_vector[1];
}
2009-04-17 16:10:11 +02:00
// ********************************* BadAlloc ****************************
void BadAlloc::raise()
{
throw BadAlloc();
}
ISC_STATUS BadAlloc::stuff_exception(ISC_STATUS* const status_vector) const throw()
{
ISC_STATUS *sv = status_vector;
*sv++ = isc_arg_gds;
*sv++ = isc_virmemexh;
*sv++ = isc_arg_end;
return status_vector[1];
}
const char* BadAlloc::what() const throw()
{
return "Firebird::BadAlloc";
}
2009-04-17 16:10:11 +02:00
// ********************************* LongJump ***************************
void LongJump::raise()
{
throw LongJump();
}
ISC_STATUS LongJump::stuff_exception(ISC_STATUS* const status_vector) const throw()
{
/*
2008-12-05 02:20:14 +01:00
* Do nothing for a while - not all utilities are ready,
* status_vector is passed in them by other means.
* Ideally status_exception should be always used for it,
* and we should activate the following code:
ISC_STATUS *sv = status_vector;
*sv++ = isc_arg_gds;
*sv++ = isc_random;
*sv++ = isc_arg_string;
*sv++ = (ISC_STATUS)(IPTR) "Unexpected Firebird::LongJump";
*sv++ = isc_arg_end;
*/
2008-12-05 02:20:14 +01:00
return status_vector[1];
}
2009-07-23 02:56:28 +02:00
const char* LongJump::what() const throw()
{
return "Firebird::LongJump";
}
2009-04-17 16:10:11 +02:00
// ********************************* system_error ***************************
2008-12-05 02:20:14 +01:00
system_error::system_error(const char* syscall, int error_code) :
status_exception(), errorCode(error_code)
{
Arg::Gds temp(isc_sys_request);
temp << Arg::Str(syscall);
temp << SYS_ERR(errorCode);
set_status(temp.value());
}
void system_error::raise(const char* syscall, int error_code)
{
throw system_error(syscall, error_code);
}
void system_error::raise(const char* syscall)
{
throw system_error(syscall, getSystemError());
}
int system_error::getSystemError()
{
#ifdef WIN_NT
return GetLastError();
#else
return errno;
#endif
}
2009-04-17 16:10:11 +02:00
// ********************************* system_call_failed ***************************
2008-12-05 02:20:14 +01:00
system_call_failed::system_call_failed(const char* syscall, int error_code) :
system_error(syscall, error_code)
{
#ifndef SUPERCLIENT
// NS: something unexpected has happened. Log the error to log file
// In the future we may consider terminating the process even in PROD_BUILD
2009-11-15 19:53:16 +01:00
gds__log("Operating system call %s failed. Error code %d", syscall, error_code);
#endif
2008-12-01 08:28:13 +01:00
#ifdef DEV_BUILD
2008-12-05 02:20:14 +01:00
// raised failed system call exception in DEV_BUILD in 99.99% means
// problems with the code - let's create memory dump now
abort();
#endif
}
void system_call_failed::raise(const char* syscall, int error_code)
2002-11-11 19:06:01 +01:00
{
throw system_call_failed(syscall, error_code);
2002-11-11 19:06:01 +01:00
}
void system_call_failed::raise(const char* syscall)
2002-11-11 19:06:01 +01:00
{
throw system_call_failed(syscall, getSystemError());
2002-11-11 19:06:01 +01:00
}
2009-04-17 16:10:11 +02:00
// ********************************* fatal_exception *******************************
fatal_exception::fatal_exception(const char* message) :
status_exception()
2002-11-11 19:06:01 +01:00
{
2009-02-03 12:02:00 +01:00
const ISC_STATUS temp[] =
{
2009-01-03 20:02:04 +01:00
isc_arg_gds,
isc_random,
2009-09-03 03:28:54 +02:00
isc_arg_string,
(ISC_STATUS)(IPTR) message,
2009-01-03 20:02:04 +01:00
isc_arg_end
};
set_status(temp);
2002-11-11 19:06:01 +01:00
}
// Keep in sync with the constructor above, please; "message" becomes 4th element
// after initialization of status vector in constructor.
const char* fatal_exception::what() const throw()
{
return reinterpret_cast<const char*>(value()[3]);
}
2003-12-31 06:36:12 +01:00
void fatal_exception::raise(const char* message)
2002-11-11 19:06:01 +01:00
{
throw fatal_exception(message);
}
void fatal_exception::raiseFmt(const char* format, ...)
{
va_list args;
va_start(args, format);
char buffer[1024];
VSNPRINTF(buffer, sizeof(buffer), format, args);
buffer[sizeof(buffer) - 1] = 0;
va_end(args);
throw fatal_exception(buffer);
}
2009-04-17 16:10:11 +02:00
// ************************** exception handling routines **************************
// Serialize exception into status_vector
ISC_STATUS stuff_exception(ISC_STATUS *status_vector, const Firebird::Exception& ex) throw()
{
return ex.stuff_exception(status_vector);
}
2002-11-11 19:06:01 +01:00
} // namespace Firebird