In order to fix remaining synchronization bugs and in order to support IRQs in protected mode, DOSVM requires some restructuring. This patch removes some VM86 dependencies from dosvm.c and also provides a simple workaround for a nasty deadlock. Real workaround would require true nested interrupt handling in DOSVM_Wait which is something I would rather not mix with this patch. Changelog: Remove obsolete code from DOSVM_Wait. Prepare DOSVM_Wait for handling nested interrupts in both real and protected mode. Provide temporary workaround for keyboard related deadlock. Index: dlls/winedos/dosexe.h =================================================================== RCS file: /home/wine/wine/dlls/winedos/dosexe.h,v retrieving revision 1.24 diff -u -r1.24 dosexe.h --- dlls/winedos/dosexe.h 21 Apr 2003 23:22:53 -0000 1.24 +++ dlls/winedos/dosexe.h 2 May 2003 14:47:28 -0000 @@ -75,8 +75,8 @@ typedef void (WINAPI *RMCBPROC)(CONTEXT86*); typedef void (WINAPI *INTPROC)(CONTEXT86*); -#define DOS_PRIORITY_REALTIME 0 /* IRQ0 */ -#define DOS_PRIORITY_KEYBOARD 1 /* IRQ1 */ +#define DOS_PRIORITY_REALTIME 1 /* IRQ0 - FIXME: should be 0 */ +#define DOS_PRIORITY_KEYBOARD 0 /* IRQ1 - FIXME: should be 1 */ #define DOS_PRIORITY_VGA 2 /* IRQ9 */ #define DOS_PRIORITY_MOUSE 5 /* IRQ12 */ #define DOS_PRIORITY_SERIAL 10 /* IRQ4 */ @@ -106,7 +106,7 @@ /* dosvm.c */ extern INT WINAPI DOSVM_Enter( CONTEXT86 *context ); -extern void WINAPI DOSVM_Wait( INT read_pipe, HANDLE hObject ); +extern void WINAPI DOSVM_Wait( CONTEXT86 * ); extern DWORD WINAPI DOSVM_Loop( HANDLE hThread ); extern void WINAPI DOSVM_QueueEvent( INT irq, INT priority, DOSRELAY relay, LPVOID data ); extern void WINAPI DOSVM_PIC_ioport_out( WORD port, BYTE val ); @@ -180,7 +180,7 @@ /* int16.c */ extern void WINAPI DOSVM_Int16Handler(CONTEXT86*); -extern int WINAPI DOSVM_Int16ReadChar(BYTE*ascii,BYTE*scan,BOOL peek); +extern BOOL WINAPI DOSVM_Int16ReadChar( BYTE *, BYTE *, CONTEXT86 * ); extern int WINAPI DOSVM_Int16AddChar(BYTE ascii,BYTE scan); /* int17.c */ Index: dlls/winedos/dosvm.c =================================================================== RCS file: /home/wine/wine/dlls/winedos/dosvm.c,v retrieving revision 1.34 diff -u -r1.34 dosvm.c --- dlls/winedos/dosvm.c 4 Mar 2003 02:16:20 -0000 1.34 +++ dlls/winedos/dosvm.c 2 May 2003 14:47:33 -0000 @@ -90,7 +90,6 @@ static struct _DOSEVENT *pending_event, *current_event; static int sig_sent; static HANDLE event_notifier; -static CONTEXT86 *current_context; #define SHOULD_PEND(x) \ (x && ((!current_event) || (x->priority < current_event->priority))) @@ -140,7 +139,7 @@ { LPDOSEVENT event, cur, prev; - if (current_context) { + if (MZ_Current()) { event = malloc(sizeof(DOSEVENT)); if (!event) { ERR("out of memory allocating event entry\n"); @@ -259,79 +258,74 @@ } } + /*********************************************************************** - * Wait (WINEDOS.@) + * DOSVM_Wait + * + * Wait for asynchronous events. This routine temporarily enables + * interrupts and waits until some asynchronous event has been + * processed. */ -void WINAPI DOSVM_Wait( INT read_pipe, HANDLE hObject ) +void WINAPI DOSVM_Wait( CONTEXT86 *waitctx ) { - MSG msg; - DWORD waitret; - HANDLE objs[3]; - int objc; - BOOL got_msg = FALSE; - - objs[0]=GetStdHandle(STD_INPUT_HANDLE); - objs[1]=event_notifier; - objs[2]=hObject; - objc=hObject?3:2; - do { - /* check for messages (waste time before the response check below) */ - if (PeekMessageA) + if (SHOULD_PEND(pending_event)) { - while (PeekMessageA(&msg,0,0,0,PM_REMOVE|PM_NOYIELD)) { - /* got a message */ - DOSVM_ProcessMessage(&msg); - /* we don't need a TranslateMessage here */ - DispatchMessageA(&msg); - got_msg = TRUE; - } - } -chk_console_input: - if (!got_msg) { - /* check for console input */ - INPUT_RECORD msg; - DWORD num; - if (PeekConsoleInputA(objs[0],&msg,1,&num) && num) { - DOSVM_ProcessConsole(); - got_msg = TRUE; - } - } - if (read_pipe == -1) { - /* dispatch pending events */ - if (SHOULD_PEND(pending_event)) { - CONTEXT86 context = *current_context; + /* + * FIXME: This does not work in protected mode DOS programs. + * FIXME: If we have pending IRQ which has 16-bit handler, + * DOSVM_SendQueuedEvents may stuck in which case application + * deadlocks. This is why keyboard events must have top + * priority (default timer IRQ handler is 16-bit code). + * FIXME: Critical section locking is broken. + */ + CONTEXT86 context = *waitctx; IF_SET(&context); SET_PEND(&context); DOSVM_SendQueuedEvents(&context); - got_msg = TRUE; - } - if (got_msg) break; - } else { - fd_set readfds; - struct timeval timeout={0,0}; - /* quick check for response from dosmod - * (faster than doing the full blocking wait, if data already available) */ - FD_ZERO(&readfds); FD_SET(read_pipe,&readfds); - if (select(read_pipe+1,&readfds,NULL,NULL,&timeout)>0) - break; - } - /* nothing yet, block while waiting for something to do */ - if (MsgWaitForMultipleObjects) - waitret = MsgWaitForMultipleObjects(objc,objs,FALSE,INFINITE,QS_ALLINPUT); - else - waitret = WaitForMultipleObjects(objc,objs,FALSE,INFINITE); - - if (waitret==(DWORD)-1) { - ERR_(module)("dosvm wait error=%ld\n",GetLastError()); } - if ((read_pipe != -1) && hObject) { - if (waitret==(WAIT_OBJECT_0+2)) break; + else + { + HANDLE objs[2]; + int objc = DOSVM_IsWin16() ? 2 : 1; + DWORD waitret; + + objs[0] = event_notifier; + objs[1] = GetStdHandle(STD_INPUT_HANDLE); + + waitret = MsgWaitForMultipleObjects( objc, objs, FALSE, + INFINITE, QS_ALLINPUT ); + + if (waitret == WAIT_OBJECT_0) + { + /* + * New pending event has been queued, we ignore it + * here because it will be processed on next call to + * DOSVM_Wait. + */ + } + else if (objc == 2 && waitret == WAIT_OBJECT_0 + 1) + { + DOSVM_ProcessConsole(); + } + else if (waitret == WAIT_OBJECT_0 + objc) + { + MSG msg; + while (PeekMessageA(&msg,0,0,0,PM_REMOVE|PM_NOYIELD)) + { + /* got a message */ + DOSVM_ProcessMessage(&msg); + /* we don't need a TranslateMessage here */ + DispatchMessageA(&msg); + } + } + else + { + ERR_(module)( "dosvm wait error=%ld\n", GetLastError() ); + } } - if (waitret==WAIT_OBJECT_0) - goto chk_console_input; - } while (TRUE); } + DWORD WINAPI DOSVM_Loop( HANDLE hThread ) { HANDLE objs[2]; @@ -434,7 +428,7 @@ * QueryPerformanceCounter() or something like that */ InterlockedDecrement(&(NtCurrentTeb()->alarms)); } - TRACE_(int)("context=%p, current=%p\n", context, current_context); + TRACE_(int)("context=%p\n", context); TRACE_(int)("cs:ip=%04lx:%04lx, ss:sp=%04lx:%04lx\n", context->SegCs, context->Eip, context->SegSs, context->Esp); if (!ISV86(context)) { ERR_(int)("@#&*%%, winedos signal handling is *still* messed up\n"); @@ -450,9 +444,6 @@ int WINAPI DOSVM_Enter( CONTEXT86 *context ) { - CONTEXT86 *old_context = current_context; - - current_context = context; __TRY { __wine_enter_vm86( context ); @@ -463,7 +454,7 @@ TRACE_(module)( "leaving vm86 mode\n" ); } __ENDTRY - current_context = old_context; + return 0; } @@ -489,7 +480,7 @@ /* another event is pending, which we should probably * be able to process now */ TRACE("another event pending, setting flag\n"); - current_context->EFlags |= VIP_MASK; + NtCurrentTeb()->vm86_pending |= VIP_MASK; } } else { WARN("EOI without active IRQ\n"); Index: dlls/winedos/int16.c =================================================================== RCS file: /home/wine/wine/dlls/winedos/int16.c,v retrieving revision 1.10 diff -u -r1.10 int16.c --- dlls/winedos/int16.c 2 Jan 2003 17:59:47 -0000 1.10 +++ dlls/winedos/int16.c 2 May 2003 14:47:37 -0000 @@ -59,7 +59,7 @@ /* Returns: AH = Scan code AL = ASCII character */ TRACE("Get Keystroke\n"); - DOSVM_Int16ReadChar(&ascii, &scan, FALSE); + DOSVM_Int16ReadChar(&ascii, &scan, context); SET_AL( context, ascii ); SET_AH( context, scan ); break; @@ -69,7 +69,7 @@ /* AH = Scan code */ /* AL = ASCII character */ TRACE("Check for Keystroke\n"); - if (!DOSVM_Int16ReadChar(&ascii, &scan, TRUE)) + if (!DOSVM_Int16ReadChar(&ascii, &scan, NULL)) { SET_ZFLAG(context); } @@ -111,7 +111,7 @@ TRACE("Get Enhanced Keystroke - Partially supported\n"); /* Returns: AH = Scan code AL = ASCII character */ - DOSVM_Int16ReadChar(&ascii, &scan, FALSE); + DOSVM_Int16ReadChar(&ascii, &scan, context); SET_AL( context, ascii ); SET_AH( context, scan ); break; @@ -122,7 +122,7 @@ /* AH = Scan code */ /* AL = ASCII character */ TRACE("Check for Enhanced Keystroke - Partially supported\n"); - if (!DOSVM_Int16ReadChar(&ascii, &scan, TRUE)) + if (!DOSVM_Int16ReadChar(&ascii, &scan, NULL)) { SET_ZFLAG(context); } @@ -145,31 +145,51 @@ } } -int WINAPI DOSVM_Int16ReadChar(BYTE*ascii,BYTE*scan,BOOL peek) +/********************************************************************** + * DOSVM_Int16ReadChar + * + * Either peek into keyboard buffer or wait for next keystroke. + * + * If waitctx is NULL, return TRUE if buffer had keystrokes and + * FALSE if buffer is empty. Returned keystroke will be left into buffer. + * + * If waitctx is non-NULL, wait until keystrokes are available. + * Return value will always be TRUE and returned keystroke will be + * removed from buffer. + */ +int WINAPI DOSVM_Int16ReadChar(BYTE *ascii, BYTE *scan, CONTEXT86 *waitctx) { - BIOSDATA *data = BIOS_DATA; - WORD CurOfs = data->NextKbdCharPtr; + BIOSDATA *data = BIOS_DATA; + WORD CurOfs = data->NextKbdCharPtr; - /* check if there's data in buffer */ - if (peek) { - if (CurOfs == data->FirstKbdCharPtr) - return 0; - } else { - while (CurOfs == data->FirstKbdCharPtr) { - /* no input available yet, so wait... */ - DOSVM_Wait( -1, 0 ); + /* check if there's data in buffer */ + if (waitctx) + { + /* wait until input is available... */ + while (CurOfs == data->FirstKbdCharPtr) + DOSVM_Wait( waitctx ); } - } - /* read from keyboard queue */ - TRACE("(%p,%p,%d) -> %02x %02x\n",ascii,scan,peek,((BYTE*)data)[CurOfs],((BYTE*)data)[CurOfs+1]); - if (ascii) *ascii = ((BYTE*)data)[CurOfs]; - if (scan) *scan = ((BYTE*)data)[CurOfs+1]; - if (!peek) { - CurOfs += 2; - if (CurOfs >= data->KbdBufferEnd) CurOfs = data->KbdBufferStart; - data->NextKbdCharPtr = CurOfs; - } - return 1; + else + { + if (CurOfs == data->FirstKbdCharPtr) + return FALSE; + } + + /* read from keyboard queue */ + TRACE( "(%p,%p,%p) -> %02x %02x\n", ascii, scan, waitctx, + ((BYTE*)data)[CurOfs], ((BYTE*)data)[CurOfs+1] ); + + if (ascii) *ascii = ((BYTE*)data)[CurOfs]; + if (scan) *scan = ((BYTE*)data)[CurOfs+1]; + + if (waitctx) + { + CurOfs += 2; + if (CurOfs >= data->KbdBufferEnd) CurOfs = data->KbdBufferStart; + data->NextKbdCharPtr = CurOfs; + } + + return TRUE; } int WINAPI DOSVM_Int16AddChar(BYTE ascii,BYTE scan) Index: dlls/winedos/int21.c =================================================================== RCS file: /home/wine/wine/dlls/winedos/int21.c,v retrieving revision 1.29 diff -u -r1.29 int21.c --- dlls/winedos/int21.c 21 Apr 2003 23:22:53 -0000 1.29 +++ dlls/winedos/int21.c 2 May 2003 14:47:44 -0000 @@ -94,7 +94,7 @@ * Reads a character from the standard input. * Extended keycodes will be returned as two separate characters. */ -static BOOL INT21_ReadChar( BYTE *input, BOOL peek ) +static BOOL INT21_ReadChar( BYTE *input, CONTEXT86 *waitctx ) { static BYTE pending_scan = 0; @@ -102,7 +102,7 @@ { if (input) *input = pending_scan; - if (!peek) + if (waitctx) pending_scan = 0; return TRUE; } @@ -110,12 +110,12 @@ { BYTE ascii; BYTE scan; - if (!DOSVM_Int16ReadChar( &ascii, &scan, peek )) + if (!DOSVM_Int16ReadChar( &ascii, &scan, waitctx )) return FALSE; if (input) *input = ascii; - if (!peek && !ascii) + if (waitctx && !ascii) pending_scan = scan; return TRUE; } @@ -328,7 +328,7 @@ BYTE ascii; BYTE scan; - DOSVM_Int16ReadChar( &ascii, &scan, FALSE ); + DOSVM_Int16ReadChar( &ascii, &scan, context ); if (ascii == '\r' || ascii == '\n') { @@ -1312,7 +1312,7 @@ { BYTE ascii; TRACE("DIRECT CHARACTER INPUT WITH ECHO\n"); - INT21_ReadChar( &ascii, FALSE ); + INT21_ReadChar( &ascii, context ); SET_AL( context, ascii ); /* * FIXME: What to echo when extended keycodes are read? @@ -1337,10 +1337,10 @@ { TRACE("Direct Console Input\n"); - if (INT21_ReadChar( NULL, TRUE )) + if (INT21_ReadChar( NULL, NULL )) { BYTE ascii; - INT21_ReadChar( &ascii, FALSE ); + INT21_ReadChar( &ascii, context ); SET_AL( context, ascii ); RESET_ZFLAG( context ); } @@ -1367,7 +1367,7 @@ { BYTE ascii; TRACE("DIRECT CHARACTER INPUT WITHOUT ECHO\n"); - INT21_ReadChar( &ascii, FALSE ); + INT21_ReadChar( &ascii, context ); SET_AL( context, ascii ); } break; @@ -1376,7 +1376,7 @@ { BYTE ascii; TRACE("CHARACTER INPUT WITHOUT ECHO\n"); - INT21_ReadChar( &ascii, FALSE ); + INT21_ReadChar( &ascii, context ); SET_AL( context, ascii ); } break; @@ -1414,7 +1414,7 @@ case 0x0b: /* GET STDIN STATUS */ TRACE( "GET STDIN STATUS\n" ); { - if (INT21_ReadChar( NULL, TRUE )) + if (INT21_ReadChar( NULL, NULL )) SET_AL( context, 0xff ); /* character available */ else SET_AL( context, 0 ); /* no character available */ Index: dlls/winedos/devices.c =================================================================== RCS file: /home/wine/wine/dlls/winedos/devices.c,v retrieving revision 1.7 diff -u -r1.7 devices.c --- dlls/winedos/devices.c 7 Dec 2002 23:46:41 -0000 1.7 +++ dlls/winedos/devices.c 2 May 2003 14:47:49 -0000 @@ -282,7 +282,7 @@ /* check for new keyboard input */ while (CurOfs == bios->FirstKbdCharPtr) { /* no input available yet, so wait... */ - DOSVM_Wait( -1, 0 ); + DOSVM_Wait( ctx ); } /* read from keyboard queue (call int16?) */ data = ((WORD*)bios)[CurOfs]; @@ -301,7 +301,7 @@ /* check for new keyboard input */ while (CurOfs == bios->FirstKbdCharPtr) { /* no input available yet, so wait... */ - DOSVM_Wait( -1, 0 ); + DOSVM_Wait( ctx ); } /* read from keyboard queue (call int16?) */ data = ((WORD*)bios)[CurOfs]; -- Jukka Heinonen <http://www.iki.fi/jhei/>