winedos+ntdll / Fix real mode event handling

Jukka Heinonen jhei at iki.fi
Mon Nov 10 14:54:17 CST 2003


This patch should fix all bugs and races I know in real mode
asynchronous event handling. Some obsolete
flag checks/management statements have been removed and 
NTDLL/winedos roles in flag handling have been made simpler.
And I think this version of code is simpler to understand.

DPMI event handling is still broken but I guess
raise_vm86_sti_exception is not buggy any more.




Changelog:
  Fix race in real mode event handling.
  Merge real mode pending event checking routines.
  Remove some obsolete code.




Index: dlls/winedos/dosvm.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/dosvm.c,v
retrieving revision 1.54
diff -u -r1.54 dosvm.c
--- dlls/winedos/dosvm.c	14 Oct 2003 05:20:34 -0000	1.54
+++ dlls/winedos/dosvm.c	10 Nov 2003 20:02:36 -0000
@@ -557,9 +557,6 @@
   /* case EXCEPTION_VM86_PICRETURN: */
     if (!ISV86(context))
       ERR( "Protected mode STI caught by real mode handler!\n" );
-
-    context->EFlags |= VIF_MASK;
-    context->EFlags &= ~VIP_MASK;
     DOSVM_SendQueuedEvents(context);
     return EXCEPTION_CONTINUE_EXECUTION;
   }




Index: dlls/ntdll/signal_i386.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/signal_i386.c,v
retrieving revision 1.79
diff -u -r1.79 signal_i386.c
--- dlls/ntdll/signal_i386.c	6 Nov 2003 00:08:05 -0000	1.79
+++ dlls/ntdll/signal_i386.c	10 Nov 2003 20:02:45 -0000
@@ -532,6 +532,71 @@
     vm86->regs.ss     = context->SegSs;
     vm86->regs.eflags = context->EFlags;
 }
+
+
+/**********************************************************************
+ *		merge_vm86_pending_flags
+ *
+ * Merges TEB.vm86_ptr and TEB.vm86_pending VIP flags and
+ * raises exception if there are pending events and VIF flag
+ * has been turned on.
+ *
+ * Called from __wine_enter_vm86 because vm86_enter
+ * doesn't check for pending events. 
+ *
+ * Called from raise_vm86_sti_exception to check for
+ * pending events in a signal safe way.
+ */
+static void merge_vm86_pending_flags( EXCEPTION_RECORD *rec )
+{
+    BOOL check_pending = TRUE;
+    struct vm86plus_struct *vm86 =
+        (struct vm86plus_struct*)(NtCurrentTeb()->vm86_ptr);
+
+    /*
+     * In order to prevent a race when SIGUSR2 occurs while
+     * we are returning from exception handler, pending events
+     * will be rechecked after each raised exception.
+     */
+    while (check_pending && NtCurrentTeb()->vm86_pending)
+    {
+        check_pending = FALSE;
+        NtCurrentTeb()->vm86_ptr = NULL;
+            
+        /*
+         * If VIF is set, throw exception.
+         * Note that SIGUSR2 may turn VIF flag off so
+         * VIF check must occur only when TEB.vm86_ptr is NULL.
+         */
+        if (vm86->regs.eflags & VIF_MASK)
+        {
+            CONTEXT vcontext;
+            save_vm86_context( &vcontext, vm86 );
+            
+            rec->ExceptionCode    = EXCEPTION_VM86_STI;
+            rec->ExceptionFlags   = EXCEPTION_CONTINUABLE;
+            rec->ExceptionRecord  = NULL;
+            rec->NumberParameters = 0;
+            rec->ExceptionAddress = (LPVOID)vcontext.Eip;
+
+            vcontext.EFlags &= ~VIP_MASK;
+            NtCurrentTeb()->vm86_pending = 0;
+            EXC_RtlRaiseException( rec, &vcontext );
+
+            restore_vm86_context( &vcontext, vm86 );
+            check_pending = TRUE;
+        }
+
+        NtCurrentTeb()->vm86_ptr = vm86;
+    }
+
+    /*
+     * Merge VIP flags in a signal safe way. This requires
+     * that the following operation compiles into atomic
+     * instruction.
+     */
+    vm86->regs.eflags |= NtCurrentTeb()->vm86_pending;
+}
 #endif /* __HAVE_VM86 */
 
 
@@ -845,38 +910,21 @@
 #ifdef __HAVE_VM86
 /**********************************************************************
  *		raise_vm86_sti_exception
- *
- * FIXME: this is most likely broken.
  */
 static void WINAPI raise_vm86_sti_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
 {
-    struct vm86plus_struct *vm86;
-
-    /* __wine_enter_vm86() merges the vm86_pending flag in safely */
+    /* merge_vm86_pending_flags merges the vm86_pending flag in safely */
     NtCurrentTeb()->vm86_pending |= VIP_MASK;
 
-    vm86 = (struct vm86plus_struct*)(NtCurrentTeb()->vm86_ptr);
-    if (vm86)
+    if (NtCurrentTeb()->vm86_ptr)
     {
-        if (vm86->regs.eflags & VIP_MASK) return;
-        vm86->regs.eflags |= VIP_MASK;
         if (((char*)context->Eip >= (char*)vm86_return) &&
             ((char*)context->Eip <= (char*)vm86_return_end) &&
             (VM86_TYPE(context->Eax) != VM86_SIGNAL)) {
             /* exiting from VM86, can't throw */
             return;
         }
-        if (vm86->regs.eflags & VIF_MASK) {
-            /* VIF is set, throw exception */
-            CONTEXT vcontext;
-            NtCurrentTeb()->vm86_pending = 0;
-            NtCurrentTeb()->vm86_ptr = NULL;
-            save_vm86_context( &vcontext, vm86 );
-            rec->ExceptionAddress = (LPVOID)vcontext.Eip;
-            EXC_RtlRaiseException( rec, &vcontext );
-            restore_vm86_context( &vcontext, vm86 );
-            NtCurrentTeb()->vm86_ptr = vm86;
-        }
+        merge_vm86_pending_flags( rec );
     }
     else if (NtCurrentTeb()->dpmi_vif &&
              !IS_SELECTOR_SYSTEM(context->SegCs) &&
@@ -1195,40 +1243,18 @@
     for (;;)
     {
         restore_vm86_context( context, &vm86 );
-        /* Linux doesn't preserve pending flag (VIP_MASK) on return,
-         * so save it on entry, just in case */
-        teb->vm86_pending |= (context->EFlags & VIP_MASK);
-        /* Work around race conditions with signal handler
-         * (avoiding sigprocmask for performance reasons) */
-        teb->vm86_ptr = &vm86;
-        vm86.regs.eflags |= teb->vm86_pending;
 
-        /* Check for VIF|VIP here, since vm86_enter doesn't */
-        if ((vm86.regs.eflags & (VIF_MASK|VIP_MASK)) == (VIF_MASK|VIP_MASK)) {
-            teb->vm86_ptr = NULL;
-            teb->vm86_pending = 0;
-            context->EFlags |= VIP_MASK;
-            rec.ExceptionCode    = EXCEPTION_VM86_STI;
-            rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
-            rec.ExceptionRecord  = NULL;
-            rec.ExceptionAddress = (LPVOID)context->Eip;
-            rec.NumberParameters = 0;
-            EXC_RtlRaiseException( &rec, context );
-            continue;
-        }
+        teb->vm86_ptr = &vm86;
+        merge_vm86_pending_flags( &rec );
 
-        do
+        res = vm86_enter( &teb->vm86_ptr ); /* uses and clears teb->vm86_ptr */
+        if (res < 0)
         {
-            res = vm86_enter( &teb->vm86_ptr ); /* uses and clears teb->vm86_ptr */
-            if (res < 0)
-            {
-                errno = -res;
-                return;
-            }
-        } while (VM86_TYPE(res) == VM86_SIGNAL);
+            errno = -res;
+            return;
+        }
 
         save_vm86_context( context, &vm86 );
-        context->EFlags |= teb->vm86_pending;
 
         rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
         rec.ExceptionRecord  = NULL;
@@ -1263,12 +1289,15 @@
             rec.ExceptionInformation[0] = VM86_ARG(res);
             break;
         case VM86_STI: /* sti/popf/iret instruction enabled virtual interrupts */
+            context->EFlags |= VIF_MASK;
+            context->EFlags &= ~VIP_MASK;
             teb->vm86_pending = 0;
             rec.ExceptionCode = EXCEPTION_VM86_STI;
             break;
         case VM86_PICRETURN: /* return due to pending PIC request */
             rec.ExceptionCode = EXCEPTION_VM86_PICRETURN;
             break;
+        case VM86_SIGNAL: /* cannot happen because vm86_enter handles this case */
         default:
             ERR( "unhandled result from vm86 mode %x\n", res );
             continue;




-- 
Jukka Heinonen <http://www.iki.fi/jhei/>



More information about the wine-patches mailing list