A mysterious exception, which after I scratched my head for quite a while, turned out to be a simple overlook.
In a recent testing of a new internal beta release of some software , we came across a fatal exception thrown from the main program by the end of the simulation run. It happened when the program was trying to export the data to a network SQL server.
What was perplexing is that the exception only happened on testing machines; on the developer’s machine, everything seemed normal.
After pulling some of my hairs, it turned out –
On the development machine, we have the capacity-limited SQL Server Express 2008 R2 installed for debugging and testing purpose. When developing, this server is restricted as a local server and set as the default database connection. The system has a separate ini file that overwrites the default setting and specifies the real database server to connect.
The SQL server connection instance, when created and initialized with its ctor, uses the default connection set up, which will try to create a connection in the first place to the local SQL server on the development machine. Something like this:
lvSQLConn := TMSSQLConnection.Create;
lvSQLConn.ServerAddress := (server address specified from the ini file)
Therefore the default connection happens during the construction. Of course, when running on the development machine, there will be absolutely no problem, because that local SQL server will always be available to access as Named Pipe. However, on the deployment machine, that local SQL server becomes inaccessible, hence an exception (something like “Server doesn’t support requested protocol“) is thrown right in the middle of construction, which as you can see from the code, is not caught by the try-except-finally, but caught by its host, which has no idea about the type of exception.
The fix is very simple and straightforward:
- Move the class construction line into the try-except-finally;
- Turn off the default connection (i.e., set the Active := False for the data access component during design time).
The morale of this story?
- Be wary of (or try not to do) resource allocation during an object construction;
- Be careful with class constructor so exceptions would not be thrown, or if thrown, get properly handled.
I have been working on some work implementing Dynamic Traffic Assignment (DTA) and encountered some issues related to floating point computations – and here is a summary of the lessons or experience –
Some of the code is in FORTRAN 2003, some of the code is in C++. Both are latest Intel compilers.
- Intel compilers tend to generate binary code with larger size (with the same optimization switch) than Microsoft C++ compiler;
- FORTRAN 2003 is a whole new language and shouldn’t be treated the same as FORTRAN77;
- In order for DEBUG build and RELEASE build to have identical floating point outputs, we have to make sure the following setting, otherwise the two built versions may generate slightly different floating point numbers and if accumulated iteratively may cause non-trivial difference. These settings are: a).FORTRAN compiler->Floating Point Model must be “Strict”; and b). Floating Point Speculation must be “Off”;
- FORTRAN->Run-time->”Check Array and String Bounds” and “Check Uninitialized Variables” are very useful flags especially the code has to deal with legacy FORTRAN 77 code.
- FORTRAN->Floating Point->Floating Point Exception Handling, enable “Underflow gives 0.0; Abort on other IEEE exceptions”, so that floating point exceptions can be thrown timely. But I don’t find a comparable switch from Intel C++ compiler.
Another lesson that calls for attention is floating point overflow – of course there are a whole bunch IEEE definitions on these, but practically speaking, this type of exception is pretty tricky to catch as I found they don’t get thrown immediately when it happens (with the Intel compiler), if you have a multi-threaded program it is even harder to trace when and where a floating point overflow is triggered.
In the Dynamic Traffic Assignment work, the testing network is a tremendously huge network and the path cost itself is a big number, this big number when got exponential-ed, easily blows off the upper limits of a double precision.
- Be careful in using the exp( ) function and protection code should be in position to check floating point overflow;
- Using number scaling or other fast numerical techniques to approximate exp, if needed.
- Some C++ library can check overflow, including std::, boost::, leda:: but the problem is, the overflow can be detected only BEFORE the value is assigned to a non-temporary variable. So the check point has to be executed with each iteration and that really drags down the performance.
Assuming we have the following function type defined in C, i.e.,
typedef int (_stdcall * SetRelTurnFlowCbFunc)(DTAEnvironment *dtaEnv,
TASession *taSesn, int iterID,
int arrSize, EdgeDynaAttrRec **edgeDynaAttrArr);
This type is defined to facilitate call-back using function pointers. The actual implementation of this function is defined using FORTRAN as
FUNCTION SetRelTurnFlowCbFunc(dtaEnv, taSesn, iterID, arrSize, edgeDynaAttr_C_Arr)
!DEC$ ATTRIBUTES STDCALL :: SetRelTurnFlowCbFunc
TYPE(C_PTR), VALUE, INTENT(IN) :: dtaEnv
TYPE(C_PTR), VALUE, INTENT(IN) :: taSesn
INTEGER(C_INT), VALUE, INTENT(IN) :: iterID
INTEGER(C_INT), VALUE, INTENT(IN) :: arrSize
TYPE(C_PTR), VALUE, INTENT(IN) :: edgeDynaAttr_C_Arr
CALL C_F_POINTER(edgeDynaAttr_C_Arr, edgeDynaAttr_F_Arr, [arrSize])
DO n = 1, arrSize
CALL C_F_POINTER(edgeDynaAttr_F_Arr(n), aEdgeDynaAttrRec)
END FUNCTION SetRelTurnFlowCbFunc
There are several subtleties here –
- Use compiler directive to tell the FORTRAN compiler the calling convention. You can leave this to the default setting of the IDE you use but I always do this explicitly.
- When converting from a C pointer to Fortran pointer, using C_F_POINTER, a explicit array size must be specified, so that the block of memory can be correctly addressed and interpreted by the FORTRAN compiler.
- For C pointer of pointer, an inner loop is needed. In the above example, aEdgeDynaArrRec is declared as a Fortran pointer to some data structure. In the inner loop, each C pointer is individually converted to the F-pointer.