mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 22:43:04 +01:00
MT safe floating point exceptions control. Raise error in case of FP overflow according to SQL standard requirements.
This commit is contained in:
parent
81e87bdead
commit
46527f2084
165
src/common/classes/FpeControl.h
Normal file
165
src/common/classes/FpeControl.h
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* PROGRAM: FPE handling
|
||||
* MODULE: FpeControl.h
|
||||
* DESCRIPTION: handle state of math coprocessor when thread
|
||||
* enters / leaves engine
|
||||
*
|
||||
* 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
||||
*
|
||||
* 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 Peshkoff
|
||||
* for the Firebird Open Source RDBMS project.
|
||||
*
|
||||
* Copyright (c) 2008 Alexander Peshkoff <peshkoff@mail.ru>,
|
||||
* Bill Oliver <Bill.Oliver@sas.com>
|
||||
* and all contributors signed below.
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Contributor(s): ______________________________________.
|
||||
*/
|
||||
|
||||
#ifndef CLASSES_FPE_CONTROL_H
|
||||
#define CLASSES_FPE_CONTROL_H
|
||||
|
||||
#include <math.h>
|
||||
#if defined(WIN_NT)
|
||||
#include <float.h>
|
||||
#else
|
||||
#include <fenv.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
namespace Firebird
|
||||
{
|
||||
|
||||
// class to hold the state of the Floating Point Exception mask
|
||||
|
||||
// the firebird server *must* run with FP exceptions masked, as we may
|
||||
// intentionally overflow and then check for infinity to raise an error.
|
||||
// most hosts run with FP exceptions masked by default, but we need
|
||||
// to save the mask for cases where Firebird is used as an embedded
|
||||
// database.
|
||||
class FpeControl
|
||||
{
|
||||
public:
|
||||
// the constructor (1) saves the current floating point mask, and
|
||||
// (2) masks all floating point exceptions. Use is similar to
|
||||
// the ContextPoolHolder for memory allocation.
|
||||
|
||||
// on modern systems, the default is to mask exceptions
|
||||
FpeControl() throw()
|
||||
{
|
||||
getCurrentMask(savedMask);
|
||||
if (!areExceptionsMasked(savedMask))
|
||||
{
|
||||
maskAll();
|
||||
}
|
||||
}
|
||||
|
||||
~FpeControl() throw()
|
||||
{
|
||||
// change it back if necessary
|
||||
if (!areExceptionsMasked(savedMask))
|
||||
{
|
||||
restoreMask();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WIN_NT)
|
||||
static void maskAll() throw()
|
||||
{
|
||||
_clearfp(); // always call _clearfp() before setting control word
|
||||
_controlfp (_CW_DEFAULT, _MCW_EM);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef unsigned int Mask;
|
||||
Mask savedMask;
|
||||
|
||||
static bool areExceptionsMasked(const Mask& m) throw()
|
||||
{
|
||||
return m == _CW_DEFAULT;
|
||||
}
|
||||
|
||||
static void getCurrentMask(Mask& m) throw()
|
||||
{
|
||||
m = _controlfp(0, 0);
|
||||
}
|
||||
|
||||
void restoreMask() throw()
|
||||
{
|
||||
_clearfp(); // always call _clearfp() before setting control word
|
||||
_controlfp(savedMask, _MCW_EM); // restore saved
|
||||
}
|
||||
|
||||
#else
|
||||
static void maskAll() throw()
|
||||
{
|
||||
fesetenv(FE_DFL_ENV);
|
||||
}
|
||||
|
||||
private:
|
||||
// Can't dereference FE_DFL_ENV, therefore need to have something to compare with
|
||||
class DefaultEnvironment
|
||||
{
|
||||
public:
|
||||
DefaultEnvironment()
|
||||
{
|
||||
fenv_t saved;
|
||||
fegetenv(&saved);
|
||||
fesetenv(FE_DFL_ENV);
|
||||
fegetenv(&clean);
|
||||
fesetenv(&saved);
|
||||
}
|
||||
|
||||
fenv_t clean;
|
||||
};
|
||||
|
||||
fenv_t savedMask;
|
||||
|
||||
static bool areExceptionsMasked(const fenv_t& m) throw()
|
||||
{
|
||||
const static DefaultEnvironment defaultEnvironment;
|
||||
return memcmp(&defaultEnvironment.clean, &m, sizeof(fenv_t)) == 0;
|
||||
}
|
||||
|
||||
static void getCurrentMask(fenv_t& m) throw()
|
||||
{
|
||||
fegetenv(&m);
|
||||
}
|
||||
|
||||
void restoreMask() throw()
|
||||
{
|
||||
fesetenv(&savedMask);
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
} //namespace Firebird
|
||||
|
||||
|
||||
// getting a portable isinf() is harder than you would expect
|
||||
#ifdef WIN_NT
|
||||
inline bool isinf(double x) {
|
||||
return (!_finite (x) && !_isnan(x));
|
||||
}
|
||||
#else
|
||||
#ifndef isinf
|
||||
template <typename F>
|
||||
inline bool isinf(F x)
|
||||
{
|
||||
return isnan(x - x);
|
||||
}
|
||||
#endif // isinf
|
||||
#endif // WIN_NT
|
||||
|
||||
#endif //CLASSES_FPE_CONTROL_H
|
@ -49,6 +49,7 @@
|
||||
#include "../jrd/quad.h"
|
||||
#include "../jrd/val.h"
|
||||
#include "../common/classes/VaryStr.h"
|
||||
#include "../common/classes/FpeControl.h"
|
||||
#include "../jrd/dsc_proto.h"
|
||||
|
||||
|
||||
@ -1185,6 +1186,10 @@ double CVT_get_double(const dsc* desc, ErrorFunction err)
|
||||
value /= CVT_power_of_ten(scale);
|
||||
else if (scale < 0)
|
||||
value *= CVT_power_of_ten(-scale);
|
||||
|
||||
if (isinf (value))
|
||||
err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range));
|
||||
|
||||
}
|
||||
return value;
|
||||
|
||||
|
@ -112,6 +112,7 @@
|
||||
#include "../jrd/misc_func_ids.h"
|
||||
#include "../common/config/config.h"
|
||||
#include "../jrd/SysFunction.h"
|
||||
#include "../common/classes/FpeControl.h"
|
||||
|
||||
const int TEMP_LENGTH = 128;
|
||||
|
||||
@ -4025,6 +4026,11 @@ static dsc* multiply(const dsc* desc, impure_value* value, const jrd_nod* node)
|
||||
const double d1 = MOV_get_double(desc);
|
||||
const double d2 = MOV_get_double(&value->vlu_desc);
|
||||
value->vlu_misc.vlu_double = DOUBLE_MULTIPLY(d1, d2);
|
||||
if (isinf(value->vlu_misc.vlu_double))
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_arith_except) <<
|
||||
Arg::Gds(isc_exception_float_overflow));
|
||||
}
|
||||
value->vlu_desc.dsc_dtype = DEFAULT_DOUBLE;
|
||||
value->vlu_desc.dsc_length = sizeof(double);
|
||||
value->vlu_desc.dsc_scale = 0;
|
||||
@ -4117,6 +4123,11 @@ static dsc* multiply2(const dsc* desc, impure_value* value, const jrd_nod* node)
|
||||
const double d1 = MOV_get_double(desc);
|
||||
const double d2 = MOV_get_double(&value->vlu_desc);
|
||||
value->vlu_misc.vlu_double = DOUBLE_MULTIPLY(d1, d2);
|
||||
if (isinf(value->vlu_misc.vlu_double))
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_arith_except) <<
|
||||
Arg::Gds(isc_exception_float_overflow));
|
||||
}
|
||||
value->vlu_desc.dsc_dtype = DEFAULT_DOUBLE;
|
||||
value->vlu_desc.dsc_length = sizeof(double);
|
||||
value->vlu_desc.dsc_scale = 0;
|
||||
@ -4216,6 +4227,11 @@ static dsc* divide2(const dsc* desc, impure_value* value, const jrd_nod* node)
|
||||
}
|
||||
const double d1 = MOV_get_double(&value->vlu_desc);
|
||||
value->vlu_misc.vlu_double = DOUBLE_DIVIDE(d1, d2);
|
||||
if (isinf(value->vlu_misc.vlu_double))
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_arith_except) <<
|
||||
Arg::Gds(isc_exception_float_overflow));
|
||||
}
|
||||
value->vlu_desc.dsc_dtype = DEFAULT_DOUBLE;
|
||||
value->vlu_desc.dsc_length = sizeof(double);
|
||||
value->vlu_desc.dsc_scale = 0;
|
||||
|
@ -24,10 +24,6 @@
|
||||
#ifndef JRD_ISC_I_PROTO_H
|
||||
#define JRD_ISC_I_PROTO_H
|
||||
|
||||
// This will install FP overflow signal handler
|
||||
void ISC_enter();
|
||||
void ISC_exit();
|
||||
|
||||
#ifdef WIN_NT
|
||||
// This will poke event
|
||||
int ISC_kill(SLONG, SLONG, void *);
|
||||
|
@ -64,7 +64,6 @@
|
||||
#endif
|
||||
|
||||
static bool initialized_signals = false;
|
||||
static SLONG volatile overflow_count = 0;
|
||||
|
||||
static int process_id = 0;
|
||||
|
||||
@ -83,56 +82,7 @@ static struct opn_event_t opn_events[MAX_OPN_EVENTS];
|
||||
static USHORT opn_event_count;
|
||||
static ULONG opn_event_clock;
|
||||
|
||||
static void (*system_overflow_handler)(int);
|
||||
static void cleanup(void*);
|
||||
static void overflow_handler(int, int);
|
||||
|
||||
// Not thread-safe
|
||||
|
||||
ULONG isc_enter_count = 0;
|
||||
|
||||
void ISC_enter()
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* I S C _ e n t e r
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Enter ISC world from caller.
|
||||
*
|
||||
**************************************/
|
||||
/* Setup overflow handler - with chaining to any user handler */
|
||||
void (*temp)(int) = signal(SIGFPE,
|
||||
reinterpret_cast<void(*)(int)>(overflow_handler));
|
||||
if (temp != reinterpret_cast<void(*)(int)>(overflow_handler))
|
||||
system_overflow_handler = temp;
|
||||
|
||||
#ifdef DEBUG_FPE_HANDLING
|
||||
/* Debug code to simulate an FPE occuring during DB Operation */
|
||||
if (overflow_count < 100)
|
||||
kill(getpid(), SIGFPE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ISC_exit()
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* I S C _ e x i t
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Exit ISC world, return to caller.
|
||||
*
|
||||
**************************************/
|
||||
/* No longer attempt to handle overflow internally */
|
||||
signal(SIGFPE, system_overflow_handler);
|
||||
}
|
||||
|
||||
|
||||
int ISC_kill(SLONG pid, SLONG signal_number, void *object_hndl)
|
||||
{
|
||||
@ -245,15 +195,8 @@ void ISC_signal_init()
|
||||
return;
|
||||
|
||||
initialized_signals = true;
|
||||
|
||||
overflow_count = 0;
|
||||
gds__register_cleanup(cleanup, 0);
|
||||
|
||||
process_id = getpid();
|
||||
|
||||
system_overflow_handler =
|
||||
signal(SIGFPE, reinterpret_cast<void(*)(int)>(overflow_handler));
|
||||
|
||||
ISC_get_security_desc();
|
||||
}
|
||||
|
||||
@ -279,46 +222,3 @@ static void cleanup(void*)
|
||||
|
||||
initialized_signals = false;
|
||||
}
|
||||
|
||||
static void overflow_handler(int signal, int code)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* o v e r f l o w _ h a n d l e r
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Somebody overflowed. Ho hum.
|
||||
*
|
||||
**************************************/
|
||||
|
||||
#ifdef DEBUG_FPE_HANDLING
|
||||
fprintf(stderr, "overflow_handler (%x)\n", arg);
|
||||
#endif
|
||||
|
||||
/* If we're within ISC world (inside why-value) when the FPE occurs
|
||||
* we handle it (basically by ignoring it). If it occurs outside of
|
||||
* ISC world, return back a code that tells signal_handler to call any
|
||||
* customer provided handler.
|
||||
*/
|
||||
if (isc_enter_count) {
|
||||
++overflow_count;
|
||||
#ifdef DEBUG_FPE_HANDLING
|
||||
fprintf(stderr, "SIGFPE in isc code ignored %d\n",
|
||||
overflow_count);
|
||||
#endif
|
||||
/* We've "handled" the FPE */
|
||||
}
|
||||
else {
|
||||
/* We've NOT "handled" the FPE - let's chain
|
||||
the signal to other handlers */
|
||||
if (system_overflow_handler != SIG_DFL &&
|
||||
system_overflow_handler != SIG_IGN &&
|
||||
system_overflow_handler != SIG_SGE &&
|
||||
system_overflow_handler != SIG_ACK)
|
||||
{
|
||||
reinterpret_cast<void (*)(int, int)>(system_overflow_handler)(signal, code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,7 @@
|
||||
#include "../common/classes/init.h"
|
||||
#include "../common/classes/semaphore.h"
|
||||
#include "../common/classes/fb_atomic.h"
|
||||
#include "../common/classes/FpeControl.h"
|
||||
#include "../jrd/constants.h"
|
||||
#include "../jrd/ThreadStart.h"
|
||||
#ifdef SCROLLABLE_CURSORS
|
||||
@ -736,8 +737,6 @@ static ISC_STATUS prepare(ISC_STATUS *, Transaction*);
|
||||
static void release_dsql_support(sqlda_sup&);
|
||||
static void save_error_string(ISC_STATUS *);
|
||||
static bool set_path(const PathName&, PathName&);
|
||||
static void subsystem_enter() throw();
|
||||
static void subsystem_exit() throw();
|
||||
|
||||
GlobalPtr<Semaphore> why_sem;
|
||||
static bool why_initialized = false;
|
||||
@ -766,8 +765,6 @@ static const USHORT FPE_RESET_INIT_ONLY = 0x0; /* Don't reset FPE after init *
|
||||
static const USHORT FPE_RESET_NEXT_API_CALL = 0x1; /* Reset FPE on next gds call */
|
||||
static const USHORT FPE_RESET_ALL_API_CALL = 0x2; /* Reset FPE on all gds call */
|
||||
|
||||
static AtomicCounter isc_enter_count;
|
||||
|
||||
#if !(defined SUPERCLIENT || defined SUPERSERVER)
|
||||
static ULONG subsystem_usage = 0;
|
||||
static USHORT subsystem_FPE_reset = FPE_RESET_INIT_ONLY;
|
||||
@ -921,15 +918,14 @@ namespace
|
||||
};
|
||||
#endif //UNIX
|
||||
|
||||
// YEntry: Tracks subsystem_enter/exit() calls.
|
||||
// YEntry: Tracks FP exceptions state (via FpeControl).
|
||||
// Accounts activity per different attachments.
|
||||
class YEntry : public Status
|
||||
class YEntry : public Status, public FpeControl
|
||||
{
|
||||
public:
|
||||
explicit YEntry(ISC_STATUS* v) throw()
|
||||
: Status(v), att(0)
|
||||
{
|
||||
subsystem_enter();
|
||||
#ifdef UNIX
|
||||
static GlobalPtr<CtrlCHandler> ctrlCHandler;
|
||||
#endif //UNIX
|
||||
@ -952,7 +948,6 @@ namespace
|
||||
MutexLockGuard guard(att->enterMutex);
|
||||
att->enterCount--;
|
||||
}
|
||||
subsystem_exit();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -5766,84 +5761,6 @@ static bool set_path(const PathName& file_name, PathName& expanded_name)
|
||||
}
|
||||
|
||||
|
||||
static void subsystem_enter() throw()
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* s u b s y s t e m _ e n t e r
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Enter subsystem.
|
||||
*
|
||||
**************************************/
|
||||
|
||||
try
|
||||
{
|
||||
++isc_enter_count;
|
||||
#if !(defined SUPERCLIENT || defined SUPERSERVER)
|
||||
if (subsystem_usage == 0 ||
|
||||
(subsystem_FPE_reset & (FPE_RESET_NEXT_API_CALL | FPE_RESET_ALL_API_CALL)))
|
||||
{
|
||||
ISC_enter();
|
||||
subsystem_FPE_reset &= ~FPE_RESET_NEXT_API_CALL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_FPE_HANDLING
|
||||
{
|
||||
/* It's difficult to make a FPE to occur inside the engine - for debugging
|
||||
* just force one to occur every-so-often. */
|
||||
static ULONG counter = 0;
|
||||
if (((counter++) % 10) == 0)
|
||||
{
|
||||
fprintf(stderr, "Forcing FPE to occur within engine\n");
|
||||
kill(getpid(), SIGFPE);
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_FPE_HANDLING */
|
||||
}
|
||||
catch (const Exception&)
|
||||
{
|
||||
// ToDo: show full exception message here
|
||||
gds__log("Unexpected exception in subsystem_enter()");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void subsystem_exit() throw()
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* s u b s y s t e m _ e x i t
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* Exit subsystem.
|
||||
*
|
||||
**************************************/
|
||||
|
||||
try
|
||||
{
|
||||
#if !(defined SUPERCLIENT || defined SUPERSERVER)
|
||||
if (subsystem_usage == 0 ||
|
||||
(subsystem_FPE_reset & (FPE_RESET_NEXT_API_CALL | FPE_RESET_ALL_API_CALL)))
|
||||
{
|
||||
ISC_exit();
|
||||
}
|
||||
#endif
|
||||
--isc_enter_count;
|
||||
}
|
||||
catch (const Exception&)
|
||||
{
|
||||
// ToDo: show full exception message here
|
||||
gds__log("Unexpected exception in subsystem_exit()");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if !defined (SUPERCLIENT)
|
||||
bool WHY_set_shutdown(bool flag)
|
||||
{
|
||||
|
@ -106,6 +106,7 @@
|
||||
#include "../common/config/config.h"
|
||||
#include "../common/utils_proto.h"
|
||||
#include "../../../common/classes/semaphore.h"
|
||||
#include "../../../common/classes/FpeControl.h"
|
||||
|
||||
|
||||
static THREAD_ENTRY_DECLARE inet_connect_wait_thread(THREAD_ENTRY_PARAM);
|
||||
@ -243,7 +244,7 @@ int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE /*hPrevInst*/, LPSTR lpszArgs,
|
||||
// Initialize the service
|
||||
|
||||
ISC_signal_init();
|
||||
ISC_enter();
|
||||
Firebird::FpeControl::maskAll();
|
||||
|
||||
int nReturnValue = 0;
|
||||
ISC_STATUS_ARRAY status_vector;
|
||||
|
@ -60,7 +60,7 @@
|
||||
#ifdef DEBUG
|
||||
#include "gen/iberror.h"
|
||||
#endif
|
||||
#include "../jrd/os/isc_i_proto.h"
|
||||
#include "../common/classes/FpeControl.h"
|
||||
#include "../remote/proto_proto.h" // xdr_protocol_overhead()
|
||||
#include "../jrd/scroll_cursors.h"
|
||||
|
||||
@ -268,7 +268,7 @@ void SRVR_main(rem_port* main_port, USHORT flags)
|
||||
*
|
||||
**************************************/
|
||||
|
||||
ISC_enter(); // Setup floating point exception handler once and for all.
|
||||
Firebird::FpeControl::maskAll();
|
||||
|
||||
// Setup context pool for main thread
|
||||
Firebird::ContextPoolHolder mainThreadContext(getDefaultMemoryPool());
|
||||
@ -4939,7 +4939,7 @@ static THREAD_ENTRY_DECLARE loopThread(THREAD_ENTRY_PARAM)
|
||||
*
|
||||
**************************************/
|
||||
|
||||
ISC_enter(); // Setup floating point exception handler once and for all.
|
||||
Firebird::FpeControl::maskAll();
|
||||
|
||||
Worker worker;
|
||||
rem_port* port = NULL;
|
||||
|
Loading…
Reference in New Issue
Block a user