BUG: PowerPC FPSCR Not Saved on Thread Context Switch

Last reviewed: November 18, 1996
Article ID: Q158947
The information in this article applies to:
  • Microsoft Win32 Software Development Kit (SDK) for Windows NT, versions 3.51, 4.0

SYMPTOMS

Multithreaded applications compiled for Windows NT on PowerPC processors may show incorrect or unexpected floating point exception handling behavior. When two or more threads in a process set their floating point masks to different values, all of the threads get the same mask value as the thread that set its mask last.

When this problem is encountered, the exact behavior of the application will depend on which floating point exceptions are masked and unmasked and how the application handles exceptions that do occur. Following is a possible scenario:

  • One thread unmasks a floating point exception and, some time after that, another unmasks a different exception. Then, during the course of computation the first thread generates an exception that it has unmasked but its exception handler does not get called. Or, it generates the exception that the second thread unmasked and its handler does get called but with an exception that it wasn't expecting.

This problem occurs only on PowerPC processors; Windows NT on other processors does not exhibit this problem.

CAUSE

The PowerPC's floating point status and control register (FPSCR) is not being saved during thread context switches; it is being treated as a per- process resource rather than a per-thread resource.

STATUS

Microsoft has confirmed this to be a problem in Microsoft Windows NT versions 3.51 and 4.0. Microsoft is researching this problem and will post new information here as it becomes available.

MORE INFORMATION

The only time this behavior becomes a problem is when a multithreaded application uses different floating point exception masks for each thread. If the application is single-threaded or if all threads use the same floating point exception mask, the problem does not occur.

The code below demonstrates the problem. The first thread unmasks floating point divide by zero exceptions and then spawns another thread and waits for it to exit. The spawned thread unmasks floating point underflow exceptions and then exits. When the first thread returns from waiting for the second, the FPSCR is set to floating point underflow exceptions being unmasked but floating point divide by zero exceptions being masked. Thus, the FPSCR value of the first thread's is now set to that of the second thread. When the first thread performs the computation, the divide by zero error is not sent to the exception handler.

Sample Code

To see the problem in a debugger as it happens, build this sample with Microsoft Visual C++, and then set breakpoints on the lines with the "BP x here" comments. As you step through the code, examine the value of the FPSCR in the registers window of the debugger:

   #include <windows.h>
   #include <stdio.h>
   #include <float.h>

   DWORD WINAPI Thread (LPVOID lpvParam);

   void main (int argc, char ** argv)
   {
      volatile float x1 = 5.0F,
                     x2 = 0.0F;
      DWORD          tid;
      HANDLE         hThread;

      // Unmask floating point divide by zero exceptions for
      // this thread.
      _controlfp ((unsigned int)~_EM_ZERODIVIDE, _MCW_EM);   // BP 1 here

      // Create another thread
      hThread = CreateThread(NULL, 0, Thread, NULL, 0, &tid);

      // This will force a context switch.
      WaitForSingleObject (hThread, INFINITE);       // BP 2 here
      CloseHandle (hThread);

      // WHEN GET TO HERE, this thread's FPSCR should still
      // have the FP divide by zero exception unmasked.
      __try
      {

         x1 /= x2;   // cause FP divide by zero         // BP 3 here

      }
      __except (GetExceptionCode() == EXCEPTION_FLT_DIVIDE_BY_ZERO ?
                EXCEPTION_EXECUTE_HANDLER :
                EXCEPTION_CONTINUE_SEARCH)
      {
         // BP 4 on next line
         printf ("Trapped floating point divide by zero exception\n");
         _clearfp();
      }
      // BP 5 on next line
      printf ("past floating point divide by zero exception\n");
   }

   // This is a dummy thread that gets the default FPSCR settings
   // when created.
   DWORD WINAPI Thread (LPVOID lpvParam)
   {
      // Unmask floating point underflow exceptions for
      // this thread.
      _controlfp ((unsigned int)~_EM_UNDERFLOW, _MCW_EM);    // BP 6 here
      return 0;
   }

When this application works correctly, it should produce this output:

   Trapped floating point divide by zero exception
   past floating point divide by zero exception

The incorrect output is:

   past floating point divide by zero exception


KBCategory: kbprg kbbuglist
KBSubcategory: BseFltpt
Additional reference words: 3.51 4.00 kbdss fp floating point status
control signal interrupt handler



THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: November 18, 1996
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.