8
0
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:
alexpeshkoff 2009-05-27 07:57:06 +00:00
parent 81e87bdead
commit 46527f2084
8 changed files with 194 additions and 194 deletions

View 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

View File

@ -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;

View File

@ -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;

View File

@ -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 *);

View File

@ -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);
}
}
}

View File

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

View File

@ -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;

View File

@ -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;