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