ID Number: Q71418
5.10 6.00 6.00a
OS/2
Summary:
In Microsoft C versions 5.1, 6.0, and 6.0a writing a floating-point
exception handler for a multithreaded OS/2 application is very similar
to writing one for a single-threaded application under MS-DOS or OS/2.
The main items to remember are that you must set the handler from
thread 1, and since you have multiple threads, you cannot use a single
set of global variables for each thread's instance data.
More Information:
After reading the documentation, it appears that the easiest thing to
do is to use the signal() run-time function with SIGFPE as the first
argument. However, if you then examine the MTDYNA.DOC file (from C
version 5.1) or the "Microsoft C Advanced Programming Techniques"
manual (from C version 6.0), you will note that both documents state
that using signal() does not work properly for processing signals in a
multithreaded program. In both cases, it is suggested that you instead
use DosSetSigHandler(). However, if you check the OS/2 documentation
for DosSetSigHandler(), you will notice that there is no support for
floating-point exceptions.
While there seems to be no resolution to this dilemma, the answer is
in the definition of a floating-point exception. A floating-point
error is not a true signal in the sense that the operating system
takes the exception and processes it through the usual signal handler
API. Instead, it is an exception that the run-time library traps.
Therefore, you can use signal() in a multithreaded program in the
specific case of SIGFPE processing.
The sample program below illustrates how to use signal() for SIGFPE
processing. This is essentially the same code as the SIGFP.C program,
which is included in the C versions 6.0 and 6.0a online help to
demonstrate the use of setjmp(), longjmp(), and signal(), but it has
been modified to support multiple threads.
The following are some notes about this procedure:
1. The signal() run-time function MUST be called from thread 1. If it
is not, the call will not fail but the handler will not be set and
the default error routine will be used.
2. setjmp() and longjmp() work on the thread they were called on.
Therefore, you must save the longjmp() address for each thread.
3. Along the same lines, the floating-point error number is thread
specific.
4. The run-time library provides a variable called _threadid, which
points to the current thread ID. This can be used to keep the thread
instance data separate.
In the sample program, an array indexed by the thread ID is used for
both the longjmp() address and the floating-point error number.
Depending on the requirements of the implementation, it may be more
appropriate to use a linked list or a larger array to handle this.
Sample Code
-----------
/* Compile options needed: /MT
*/
#define INCL_DOSSEMAPHORE
#include <os2.h>
#include <process.h>
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <string.h>
#define MAX_TID 10
extern int far *_threadid;
jmp_buf mark[MAX_TID]; // Long Jump address array
int fperr[MAX_TID]; // FP Error number array
void fphandler( int sig, int num );
void fpcheck( void );
void main(void);
void ThreadMain(void _far *);
char far *ThreadStackPtr;
ULONG hsemThreadSem = 0;
void main(void)
{
// Set up floating-point error handler.
if( signal( SIGFPE, fphandler ) == SIG_ERR )
{
fprintf( stderr, "\nCouldn't set SIGFPE\n" );
abort();
}
ThreadStackPtr = (void far *)malloc(2024);
DosSemSet(&hsemThreadSem);
_beginthread(ThreadMain, ThreadStackPtr, 2024, NULL);
DosSemWait(&hsemThreadSem, SEM_INDEFINITE_WAIT);
printf("\nFinished processing\n");
}
void ThreadMain(void _far * arg)
{
double n1, n2, r;
int jmpret;
// If an FP exception occurs, set the place to come back to.
jmpret = setjmp( mark[*_threadid] );
// If we had an error jmpret would be equal to -1. Otherwise
// it is equal to 0. Process as needed.
if( jmpret == 0 )
{
printf( "\nTest for invalid operation - enter two numbers: " );
scanf( "%lf %lf", &n1, &n2 );
r = n1 / n2;
printf( "\n\n%4.3g / %4.3g = %4.3g\n", n1, n2, r );
r = n1 * n2;
printf( "\n%4.3g * %4.3g = %4.3g\n", n1, n2, r );
}
else
fpcheck();
DosSemClear(&hsemThreadSem);
_endthread();
}
void fphandler( int sig, int num )
{
// Set the error number for the thread in question.
fperr[*_threadid] = num;
// Reset the floating-point library to indicate the error
// has been handled.
_fpreset();
// Pull off the stored address for this thread and
// resume processing.
longjmp( mark[*_threadid], -1 );
}
void fpcheck()
{
char fpstr[30];
// Find out what the error was for the thread that is executing
// this function.
switch( fperr[*_threadid] )
{
case FPE_INVALID:
strcpy( fpstr, "Invalid number" );
break;
case FPE_OVERFLOW:
strcpy( fpstr, "Overflow" );
break;
case FPE_UNDERFLOW:
strcpy( fpstr, "Underflow" );
break;
case FPE_ZERODIVIDE:
strcpy( fpstr, "Divide by zero" );
break;
default:
strcpy( fpstr, "Other floating point error" );
break;
}
printf("\nThread: %d\tError %d: %s\n", *_threadid,
fperr[*_threadid], fpstr);
}
Additional reference words: 5.10 6.00 6.00a