diff --git a/src/common/classes/FpeControl.h b/src/common/classes/FpeControl.h new file mode 100644 index 0000000000..a6ff5b44f9 --- /dev/null +++ b/src/common/classes/FpeControl.h @@ -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 , + * Bill Oliver + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef CLASSES_FPE_CONTROL_H +#define CLASSES_FPE_CONTROL_H + +#include +#if defined(WIN_NT) +#include +#else +#include +#include +#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 +inline bool isinf(F x) +{ + return isnan(x - x); +} +#endif // isinf +#endif // WIN_NT + +#endif //CLASSES_FPE_CONTROL_H diff --git a/src/common/cvt.cpp b/src/common/cvt.cpp index 340eb8f103..9f3a98c6aa 100644 --- a/src/common/cvt.cpp +++ b/src/common/cvt.cpp @@ -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; diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index 9499dea4b0..f673ec2929 100644 --- a/src/jrd/evl.cpp +++ b/src/jrd/evl.cpp @@ -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; diff --git a/src/jrd/os/isc_i_proto.h b/src/jrd/os/isc_i_proto.h index b0c332acb0..e631c2a1c9 100644 --- a/src/jrd/os/isc_i_proto.h +++ b/src/jrd/os/isc_i_proto.h @@ -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 *); diff --git a/src/jrd/os/win32/isc_ipc.cpp b/src/jrd/os/win32/isc_ipc.cpp index 6b97517360..35ce28c3b9 100644 --- a/src/jrd/os/win32/isc_ipc.cpp +++ b/src/jrd/os/win32/isc_ipc.cpp @@ -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(overflow_handler)); - if (temp != reinterpret_cast(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(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(system_overflow_handler)(signal, code); - } - } -} diff --git a/src/jrd/why.cpp b/src/jrd/why.cpp index 3f6838bcbd..c7045775be 100644 --- a/src/jrd/why.cpp +++ b/src/jrd/why.cpp @@ -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 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; #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) { diff --git a/src/remote/os/win32/srvr_w32.cpp b/src/remote/os/win32/srvr_w32.cpp index 72f2c073b9..e5c5244063 100644 --- a/src/remote/os/win32/srvr_w32.cpp +++ b/src/remote/os/win32/srvr_w32.cpp @@ -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; diff --git a/src/remote/server.cpp b/src/remote/server.cpp index f04bdc3a83..ceb2d7ce81 100644 --- a/src/remote/server.cpp +++ b/src/remote/server.cpp @@ -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;