From ed4900318899935ad2d162e34abd8c806f688d57 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Sun, 6 Jun 1999 14:47:50 +0000 Subject: [PATCH] 16-bit scheduler reorganized: run all tasks in their own thread. Process creation sequence adapted to new scheduler. --- include/process.h | 2 + include/task.h | 9 +- loader/task.c | 525 ++++++++++++++++---------------------------- scheduler/process.c | 280 ++++++++++------------- 4 files changed, 321 insertions(+), 495 deletions(-) diff --git a/include/process.h b/include/process.h index f48ee2bc1e3..29c65449885 100644 --- a/include/process.h +++ b/include/process.h @@ -93,6 +93,8 @@ typedef struct _PDB void *server_pid; /* Server id for this process */ HANDLE *dos_handles; /* Handles mapping DOS -> Win32 */ struct _PDB *next; /* List reference - list of PDB's */ + WORD hInstance; /* hInstance on startup */ + WORD hPrevInstance; /* hPrevInstance on startup */ } PDB; /* Process flags */ diff --git a/include/task.h b/include/task.h index a0b6000c054..cf25aa26836 100644 --- a/include/task.h +++ b/include/task.h @@ -107,8 +107,8 @@ typedef struct _TDB WORD more_thunks[6*4]; /* c2 Space for 6 more thunks */ BYTE module_name[8]; /* f2 Module name for task */ WORD magic; /* fa TDB signature */ - DWORD unused7; /* fc */ - PDB16 pdb; /* 100 PDB for this task */ + HANDLE hEvent; /* fc scheduler event handle */ + PDB16 pdb; /* 100 PDB for this task */ } TDB; #define TDB_MAGIC ('T' | ('D' << 8)) @@ -149,11 +149,10 @@ extern void (*TASK_AddTaskEntryBreakpoint)( HTASK16 hTask ); extern BOOL TASK_Create( struct _THDB *thdb, struct _NE_MODULE *pModule, HINSTANCE16 hInstance, HINSTANCE16 hPrevInstance, UINT16 cmdShow ); -extern void TASK_StartTask( HTASK16 hTask ); extern void TASK_KillTask( HTASK16 hTask ); -extern void TASK_KillCurrentTask( INT16 exitCode ); extern HTASK16 TASK_GetNextTask( HTASK16 hTask ); -extern BOOL TASK_Reschedule(void); +extern void TASK_Reschedule(void); +extern void TASK_CallToStart(void); extern void TASK_InstallTHHook( THHOOK *pNewThook ); extern HQUEUE16 WINAPI SetThreadQueue16( DWORD thread, HQUEUE16 hQueue ); diff --git a/loader/task.c b/loader/task.c index 8dca248c649..fca3ffbdfdf 100644 --- a/loader/task.c +++ b/loader/task.c @@ -59,13 +59,8 @@ THHOOK *pThhook = &DefaultThhook; #define hFirstTask (pThhook->HeadTDB) #define hLockedTask (pThhook->LockTDB) -static HTASK16 hTaskToKill = 0; static UINT16 nTaskCount = 0; -static HANDLE TASK_ScheduleEvent = INVALID_HANDLE_VALUE; - -static void TASK_YieldToSystem( void ); - /*********************************************************************** * TASK_InstallTHHook @@ -231,81 +226,54 @@ static BOOL TASK_FreeThunk( HTASK16 hTask, SEGPTR thunk ) * 32-bit entry point for a new task. This function is responsible for * setting up the registers and jumping to the 16-bit entry point. */ -static void TASK_CallToStart(void) +void TASK_CallToStart(void) { TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() ); NE_MODULE *pModule = NE_GetPtr( pTask->hModule ); SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule ); + CONTEXT context; - SYSDEPS_SetCurThread( pTask->thdb ); - CLIENT_InitThread(); + /* Add task to 16-bit scheduler pool */ + TASK_Reschedule(); - /* Terminate the stack frame chain */ - memset(THREAD_STACK16( pTask->thdb ), '\0', sizeof(STACK16FRAME)); + /* Registers at initialization must be: + * ax zero + * bx stack size in bytes + * cx heap size in bytes + * si previous app instance + * di current app instance + * bp zero + * es selector to the PSP + * ds dgroup of the application + * ss stack selector + * sp top of the stack + */ - /* Initialize process critical section */ - InitializeCriticalSection( &PROCESS_Current()->crit_section ); + memset( &context, 0, sizeof(context) ); + CS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->cs - 1].hSeg); + DS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->dgroup - 1].hSeg); + ES_reg(&context) = pTask->hPDB; + EIP_reg(&context) = pModule->ip; + EBX_reg(&context) = pModule->stack_size; + ECX_reg(&context) = pModule->heap_size; + EDI_reg(&context) = context.SegDs; - /* Call USER signal proc */ - PROCESS_CallUserSignalProc( USIG_THREAD_INIT, 0 ); /* for initial thread */ - PROCESS_CallUserSignalProc( USIG_PROCESS_INIT, 0 ); - PROCESS_CallUserSignalProc( USIG_PROCESS_LOADED, 0 ); - PROCESS_CallUserSignalProc( USIG_PROCESS_RUNNING, 0 ); + TRACE_(task)("Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n", + CS_reg(&context), IP_reg(&context), DS_reg(&context), + SELECTOROF(pTask->thdb->cur_stack), + OFFSETOF(pTask->thdb->cur_stack) ); - if (pModule->flags & NE_FFLAGS_WIN32) - { - ERR_(task)("Called for Win32 task!\n" ); - ExitProcess( 1 ); - } - else if (pModule->dos_image) - { - DOSVM_Enter( NULL ); - ExitProcess( 0 ); - } - else - { - /* Registers at initialization must be: - * ax zero - * bx stack size in bytes - * cx heap size in bytes - * si previous app instance - * di current app instance - * bp zero - * es selector to the PSP - * ds dgroup of the application - * ss stack selector - * sp top of the stack - */ - CONTEXT context; - - memset( &context, 0, sizeof(context) ); - CS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->cs - 1].hSeg); - DS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->dgroup - 1].hSeg); - ES_reg(&context) = pTask->hPDB; - EIP_reg(&context) = pModule->ip; - EBX_reg(&context) = pModule->stack_size; - ECX_reg(&context) = pModule->heap_size; - EDI_reg(&context) = context.SegDs; - - TRACE_(task)("Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n", - CS_reg(&context), IP_reg(&context), DS_reg(&context), - SELECTOROF(pTask->thdb->cur_stack), - OFFSETOF(pTask->thdb->cur_stack) ); - - Callbacks->CallRegisterShortProc( &context, 0 ); - /* This should never return */ - ERR_(task)("Main program returned! (should never happen)\n" ); - ExitProcess( 1 ); - } + Callbacks->CallRegisterShortProc( &context, 0 ); } /*********************************************************************** * TASK_Create * - * NOTE: This routine might be called by a Win32 thread. We don't have - * any real problems with that, since we operated merely on a private - * TDB structure that is not yet linked into the task list. + * NOTE: This routine might be called by a Win32 thread. Thus, we need + * to be careful to protect global data structures. We do this + * by entering the Win16Lock while linking the task into the + * global task list. */ BOOL TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance, HINSTANCE16 hPrevInstance, UINT16 cmdShow) @@ -314,10 +282,7 @@ BOOL TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance, TDB *pTask; LPSTR cmd_line; WORD sp; - char *stack32Top; char name[10]; - STACK16FRAME *frame16; - STACK32FRAME *frame32; PDB *pdb32 = thdb->process; SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule ); @@ -410,12 +375,18 @@ BOOL TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance, pTask->dta = PTR_SEG_OFF_TO_SEGPTR( pTask->hPDB, (int)&pTask->pdb.cmdLine - (int)&pTask->pdb ); + /* Create scheduler event for 16-bit tasks */ + + if ( !(pTask->flags & TDBF_WIN32) ) + { + pTask->hEvent = CreateEventA( NULL, TRUE, FALSE, NULL ); + pTask->hEvent = ConvertToGlobalHandle( pTask->hEvent ); + } + /* Enter task handle into thread and process */ pTask->thdb->teb.htask16 = pTask->thdb->process->task = hTask; - TRACE_(task)("module='%s' cmdline='%s' task=%04x\n", name, cmd_line, hTask ); - - if (pTask->flags & TDBF_WIN32) return TRUE; + TRACE_(task)("module='%s' cmdline='%s' task=%04x\n", name, cmd_line, hTask ); /* If we have a DGROUP/hInstance, use it for 16-bit stack */ @@ -427,47 +398,10 @@ BOOL TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance, pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( hInstance, sp ); } - /* Create the 16-bit stack frame */ + /* If requested, add entry point breakpoint */ - pTask->thdb->cur_stack -= sizeof(STACK16FRAME); - frame16 = (STACK16FRAME *)PTR_SEG_TO_LIN( pTask->thdb->cur_stack ); - frame16->ebp = OFFSETOF( pTask->thdb->cur_stack ) + (int)&((STACK16FRAME *)0)->bp; - frame16->bp = LOWORD(frame16->ebp); - frame16->ds = frame16->es = hInstance; - frame16->fs = 0; - frame16->entry_point = 0; - frame16->entry_cs = 0; - frame16->mutex_count = 1; /* TASK_Reschedule is called from 16-bit code */ - /* The remaining fields will be initialized in TASK_Reschedule */ - - /* Create the 32-bit stack frame */ - - stack32Top = (char*)pTask->thdb->teb.stack_top; - frame16->frame32 = frame32 = (STACK32FRAME *)stack32Top - 1; - frame32->frame16 = pTask->thdb->cur_stack + sizeof(STACK16FRAME); - frame32->edi = 0; - frame32->esi = 0; - frame32->edx = 0; - frame32->ecx = 0; - frame32->ebx = 0; - frame32->retaddr = (DWORD)TASK_CallToStart; - /* The remaining fields will be initialized in TASK_Reschedule */ - - return TRUE; -} - -/*********************************************************************** - * TASK_StartTask - * - * NOTE: This routine might be called by a Win32 thread. Thus, we need - * to be careful to protect global data structures. We do this - * by entering the Win16Lock while linking the task into the - * global task list. - */ -void TASK_StartTask( HTASK16 hTask ) -{ - TDB *pTask = (TDB *)GlobalLock16( hTask ); - if ( !pTask ) return; + if ( TASK_AddTaskEntryBreakpoint ) + TASK_AddTaskEntryBreakpoint( hTask ); /* Add the task to the linked list */ @@ -475,27 +409,9 @@ void TASK_StartTask( HTASK16 hTask ) TASK_LinkTask( hTask ); SYSLEVEL_LeaveWin16Lock(); - TRACE_(task)("linked task %04x\n", hTask ); - - /* If requested, add entry point breakpoint */ - - if ( TASK_AddTaskEntryBreakpoint ) - TASK_AddTaskEntryBreakpoint( hTask ); - - /* Get the task up and running. */ - - if ( THREAD_IsWin16( pTask->thdb ) ) - { - /* Post event to start the task */ - PostEvent16( hTask ); - - /* If we ourselves are a 16-bit task, we Yield() directly. */ - if ( THREAD_IsWin16( THREAD_Current() ) ) - OldYield16(); - } + return TRUE; } - /*********************************************************************** * TASK_DeleteTask */ @@ -608,60 +524,23 @@ void TASK_KillTask( HTASK16 hTask ) if ( hLockedTask == hTask ) hLockedTask = 0; - if ( hTaskToKill && ( hTaskToKill != hCurrentTask ) ) - { - /* If another task is already marked for destruction, */ - /* we can kill it now, as we are in another context. */ - TASK_DeleteTask( hTaskToKill ); - hTaskToKill = 0; - } + TASK_DeleteTask( hTask ); - /* - * If hTask is not the task currently scheduled by the Win16 - * scheduler, we simply delete it; otherwise we mark it for - * destruction. Note that if the current task is a 32-bit - * one, hCurrentTask is *different* from GetCurrentTask()! - */ + /* When deleting the current task ... */ if ( hTask == hCurrentTask ) { - assert( hTaskToKill == 0 || hTaskToKill == hCurrentTask ); - hTaskToKill = hCurrentTask; - } - else - TASK_DeleteTask( hTask ); + DWORD lockCount; - SYSLEVEL_LeaveWin16Lock(); -} + /* ... schedule another one ... */ + TASK_Reschedule(); + + /* ... and completely release the Win16Lock, just in case. */ + ReleaseThunkLock( &lockCount ); - -/*********************************************************************** - * TASK_KillCurrentTask - * - * Kill the currently running task. As it's not possible to kill the - * current task like this, it is simply marked for destruction, and will - * be killed when either TASK_Reschedule or this function is called again - * in the context of another task. - */ -void TASK_KillCurrentTask( INT16 exitCode ) -{ - if ( !THREAD_IsWin16( THREAD_Current() ) ) - { - FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel); return; } - assert(hCurrentTask == GetCurrentTask()); - - TRACE_(task)("Killing current task %04x\n", hCurrentTask ); - - TASK_KillTask( 0 ); - - TASK_YieldToSystem(); - - /* We should never return from this Yield() */ - - ERR_(task)("Return of the living dead %04x!!!\n", hCurrentTask); - exit(1); + SYSLEVEL_LeaveWin16Lock(); } /*********************************************************************** @@ -669,189 +548,170 @@ void TASK_KillCurrentTask( INT16 exitCode ) * * This is where all the magic of task-switching happens! * - * Note: This function should only be called via the TASK_YieldToSystem() - * wrapper, to make sure that all the context is saved correctly. - * - * It must not call functions that may yield control. + * 16-bit Windows performs non-preemptive (cooperative) multitasking. + * This means that each 16-bit task runs until it voluntarily yields + * control, at which point the scheduler gets active and selects the + * next task to run. + * + * In Wine, all processes, even 16-bit ones, are scheduled preemptively + * by the standard scheduler of the underlying OS. As many 16-bit apps + * *rely* on the behaviour of the Windows scheduler, however, we have + * to simulate that behaviour. + * + * This is achieved as follows: every 16-bit task is at time (except + * during task creation and deletion) in one of two states: either it + * is the one currently running, then the global variable hCurrentTask + * contains its task handle, or it is not currently running, then it + * is blocked on a special scheduler event, a global handle to which + * is stored in the task struct. + * + * When the current task yields control, this routine gets called. Its + * purpose is to determine the next task to be active, signal the + * scheduler event of that task, and then put the current task to sleep + * waiting for *its* scheduler event to get signalled again. + * + * This routine can get called in a few other special situations as well: + * + * - On creation of a 16-bit task, the Unix process executing the task + * calls TASK_Reschedule once it has completed its initialization. + * At this point, the task needs to be blocked until its scheduler + * event is signalled the first time (this will be done by the parent + * process to get the task up and running). + * + * - When the task currently running terminates itself, this routine gets + * called and has to schedule another task, *without* blocking the + * terminating task. + * + * - When a 32-bit thread posts an event for a 16-bit task, it might be + * the case that *no* 16-bit task is currently running. In this case + * the task that has now an event pending is to be scheduled. + * */ -BOOL TASK_Reschedule(void) +void TASK_Reschedule(void) { - TDB *pOldTask = NULL, *pNewTask; - HTASK16 hTask = 0; - STACK16FRAME *newframe16; + TDB *pOldTask = NULL, *pNewTask = NULL; + HTASK16 hOldTask = 0, hNewTask = 0; + enum { MODE_YIELD, MODE_SLEEP, MODE_WAKEUP } mode; + DWORD lockCount; - /* Create scheduler event */ - if ( TASK_ScheduleEvent == INVALID_HANDLE_VALUE ) + SYSLEVEL_EnterWin16Lock(); + + /* Check what we need to do */ + hOldTask = GetCurrentTask(); + pOldTask = (TDB *)GlobalLock16( hOldTask ); + TRACE_(task)( "entered with hCurrentTask %04x by hTask %04x (pid %d)\n", + hCurrentTask, hOldTask, getpid() ); + + if ( pOldTask && THREAD_IsWin16( THREAD_Current() ) ) { - TASK_ScheduleEvent = CreateEventA( NULL, TRUE, FALSE, NULL ); - TASK_ScheduleEvent = ConvertToGlobalHandle( TASK_ScheduleEvent ); + /* We are called by an active (non-deleted) 16-bit task */ + + /* If we don't even have a current task, or else the current + task has yielded, we'll need to schedule a new task and + (possibly) put the calling task to sleep. Otherwise, we + only block the caller. */ + + if ( !hCurrentTask || hCurrentTask == hOldTask ) + mode = MODE_YIELD; + else + mode = MODE_SLEEP; } - - /* Get the initial task up and running */ - if (!hCurrentTask && GetCurrentTask()) + else { - /* We need to remove one pair of stackframes (exept for Winelib) */ - STACK16FRAME *oldframe16 = CURRENT_STACK16; - STACK32FRAME *oldframe32 = oldframe16? oldframe16->frame32 : NULL; - STACK16FRAME *newframe16 = oldframe32? PTR_SEG_TO_LIN( oldframe32->frame16 ) : NULL; - STACK32FRAME *newframe32 = newframe16? newframe16->frame32 : NULL; - if (newframe32) + /* We are called by a deleted 16-bit task or a 32-bit thread */ + + /* The only situation where we need to do something is if we + now do not have a current task. Then, we'll need to wake up + some task that has events pending. */ + + if ( !hCurrentTask || hCurrentTask == hOldTask ) + mode = MODE_WAKEUP; + else { - newframe16->entry_ip = oldframe16->entry_ip; - newframe16->entry_cs = oldframe16->entry_cs; - newframe16->ip = oldframe16->ip; - newframe16->cs = oldframe16->cs; - newframe32->ebp = oldframe32->ebp; - newframe32->restore_addr = oldframe32->restore_addr; - newframe32->codeselector = oldframe32->codeselector; - - THREAD_Current()->cur_stack = oldframe32->frame16; + /* nothing to do */ + SYSLEVEL_LeaveWin16Lock(); + return; } - - hCurrentTask = GetCurrentTask(); - pNewTask = (TDB *)GlobalLock16( hCurrentTask ); - pNewTask->ss_sp = pNewTask->thdb->cur_stack; - return FALSE; } - /* NOTE: As we are entered from 16-bit code, we hold the Win16Lock. - We hang onto it thoughout most of this routine, so that accesses - to global variables (most notably the task list) are protected. */ - assert(hCurrentTask == GetCurrentTask()); - - TRACE_(task)("entered with hTask %04x (pid %d)\n", hCurrentTask, getpid()); - -#ifdef CONFIG_IPC - /* FIXME: What about the Win16Lock ??? */ - dde_reschedule(); -#endif - /* First check if there's a task to kill */ - - if (hTaskToKill && (hTaskToKill != hCurrentTask)) + /* Find a task to yield to: check for DirectedYield() */ + if ( mode == MODE_YIELD && pOldTask && pOldTask->hYieldTo ) { - TASK_DeleteTask( hTaskToKill ); - hTaskToKill = 0; - } - - /* Find a task to yield to */ - - pOldTask = (TDB *)GlobalLock16( hCurrentTask ); - if (pOldTask && pOldTask->hYieldTo) - { - /* check for DirectedYield() */ - - hTask = pOldTask->hYieldTo; - pNewTask = (TDB *)GlobalLock16( hTask ); - if( !pNewTask || !pNewTask->nEvents) hTask = 0; + hNewTask = pOldTask->hYieldTo; + pNewTask = (TDB *)GlobalLock16( hNewTask ); + if( !pNewTask || !pNewTask->nEvents) hNewTask = 0; pOldTask->hYieldTo = 0; } - while (!hTask) + /* Find a task to yield to: check for pending events */ + if ( (mode == MODE_YIELD || mode == MODE_WAKEUP) && !hNewTask ) { - /* Find a task that has an event pending */ - - hTask = hFirstTask; - while (hTask) + hNewTask = hFirstTask; + while (hNewTask) { - pNewTask = (TDB *)GlobalLock16( hTask ); + pNewTask = (TDB *)GlobalLock16( hNewTask ); - TRACE_(task)("\ttask = %04x, events = %i\n", hTask, pNewTask->nEvents); + TRACE_(task)( "\ttask = %04x, events = %i\n", + hNewTask, pNewTask->nEvents ); if (pNewTask->nEvents) break; - hTask = pNewTask->hNext; + hNewTask = pNewTask->hNext; } - if (hLockedTask && (hTask != hLockedTask)) hTask = 0; - if (hTask) break; - - /* No task found, wait for some events to come in */ - - /* NOTE: We release the Win16Lock while waiting for events. This is to enable - Win32 threads to thunk down to 16-bit temporarily. Since Win16 - tasks won't execute and Win32 threads are not allowed to enter - TASK_Reschedule anyway, there should be no re-entrancy problem ... */ - - ResetEvent( TASK_ScheduleEvent ); - SYSLEVEL_ReleaseWin16Lock(); - WaitForSingleObject( TASK_ScheduleEvent, INFINITE ); - SYSLEVEL_RestoreWin16Lock(); + if (hLockedTask && (hNewTask != hLockedTask)) hNewTask = 0; } - if (hTask == hCurrentTask) + /* If we are still the task with highest priority, just return ... */ + if ( mode == MODE_YIELD && hNewTask == hCurrentTask ) { + TRACE_(task)("returning to the current task (%04x)\n", hCurrentTask ); + SYSLEVEL_LeaveWin16Lock(); + /* Allow Win32 threads to thunk down even while a Win16 task is in a tight PeekMessage() or Yield() loop ... */ - SYSLEVEL_ReleaseWin16Lock(); - SYSLEVEL_RestoreWin16Lock(); - - TRACE_(task)("returning to the current task(%04x)\n", hTask ); - return FALSE; /* Nothing to do */ - } - pNewTask = (TDB *)GlobalLock16( hTask ); - TRACE_(task)("Switching to task %04x (%.8s)\n", - hTask, pNewTask->module_name ); - - /* Make the task the last in the linked list (round-robin scheduling) */ - - pNewTask->priority++; - TASK_UnlinkTask( hTask ); - TASK_LinkTask( hTask ); - pNewTask->priority--; - - /* Finish initializing the new task stack if necessary */ - - newframe16 = THREAD_STACK16( pNewTask->thdb ); - if (!newframe16->entry_cs) - { - STACK16FRAME *oldframe16 = CURRENT_STACK16; - STACK32FRAME *oldframe32 = oldframe16->frame32; - STACK32FRAME *newframe32 = newframe16->frame32; - newframe16->entry_ip = oldframe16->entry_ip; - newframe16->entry_cs = oldframe16->entry_cs; - newframe16->ip = oldframe16->ip; - newframe16->cs = oldframe16->cs; - newframe32->ebp = oldframe32->ebp; - newframe32->restore_addr = oldframe32->restore_addr; - newframe32->codeselector = oldframe32->codeselector; - } - - /* Switch to the new stack */ - - /* NOTE: We need to release/restore the Win16Lock, as the task - switched to might be at another recursion level than - the old task ... */ - - SYSLEVEL_ReleaseWin16Lock(); - - hCurrentTask = hTask; - SYSDEPS_SetCurThread( pNewTask->thdb ); - pNewTask->ss_sp = pNewTask->thdb->cur_stack; - - SYSLEVEL_RestoreWin16Lock(); - - return FALSE; -} - - -/*********************************************************************** - * TASK_YieldToSystem - * - * Scheduler interface, this way we ensure that all "unsafe" events are - * processed outside the scheduler. - */ -static void TASK_YieldToSystem( void ) -{ - if ( !THREAD_IsWin16( THREAD_Current() ) ) - { - FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel); + ReleaseThunkLock( &lockCount ); + RestoreThunkLock( lockCount ); return; } - EVENT_Synchronize( FALSE ); + /* If no task to yield to found, suspend 16-bit scheduler ... */ + if ( mode == MODE_YIELD && !hNewTask ) + { + TRACE_(task)("No currently active task\n"); + hCurrentTask = 0; + } - Callbacks->CallTaskRescheduleProc(); + /* If we found a task to wake up, do it ... */ + if ( (mode == MODE_YIELD || mode == MODE_WAKEUP) && hNewTask ) + { + TRACE_(task)("Switching to task %04x (%.8s)\n", + hNewTask, pNewTask->module_name ); + + pNewTask->priority++; + TASK_UnlinkTask( hNewTask ); + TASK_LinkTask( hNewTask ); + pNewTask->priority--; + + hCurrentTask = hNewTask; + SetEvent( pNewTask->hEvent ); + + /* This is set just in case some app reads it ... */ + pNewTask->ss_sp = pNewTask->thdb->cur_stack; + } + + /* If we need to put the current task to sleep, do it ... */ + if ( (mode == MODE_YIELD || mode == MODE_SLEEP) && hOldTask != hCurrentTask ) + { + ResetEvent( pOldTask->hEvent ); + + ReleaseThunkLock( &lockCount ); + SYSLEVEL_CheckNotLevel( 1 ); + WaitForSingleObject( pOldTask->hEvent, INFINITE ); + RestoreThunkLock( lockCount ); + } + + SYSLEVEL_LeaveWin16Lock(); } - /*********************************************************************** * InitTask (KERNEL.91) * @@ -943,7 +803,7 @@ BOOL16 WINAPI WaitEvent16( HTASK16 hTask ) pTask->nEvents--; return FALSE; } - TASK_YieldToSystem(); + TASK_Reschedule(); /* When we get back here, we have an event */ @@ -969,7 +829,10 @@ void WINAPI PostEvent16( HTASK16 hTask ) } pTask->nEvents++; - SetEvent( TASK_ScheduleEvent ); + + /* If we are a 32-bit task, we might need to wake up the 16-bit scheduler */ + if ( !THREAD_IsWin16( THREAD_Current() ) ) + TASK_Reschedule(); } @@ -1028,7 +891,7 @@ void WINAPI OldYield16(void) } if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */ - TASK_YieldToSystem(); + TASK_Reschedule(); if (pCurTask) pCurTask->nEvents--; } diff --git a/scheduler/process.c b/scheduler/process.c index eca3e0d34f6..82f53cb17fa 100644 --- a/scheduler/process.c +++ b/scheduler/process.c @@ -8,6 +8,7 @@ #include #include #include +#include "wine/winbase16.h" #include "process.h" #include "module.h" #include "neexe.h" @@ -261,51 +262,6 @@ static BOOL PROCESS_BuildEnvDB( PDB *pdb ) return ENV_BuildEnvironment( pdb ); } - -/*********************************************************************** - * PROCESS_InheritEnvDB - */ -static BOOL PROCESS_InheritEnvDB( PDB *pdb, LPCSTR cmd_line, LPCSTR env, - BOOL inherit_handles, STARTUPINFOA *startup ) -{ - if (!(pdb->env_db = HeapAlloc(pdb->heap, HEAP_ZERO_MEMORY, sizeof(ENVDB)))) - return FALSE; - InitializeCriticalSection( &pdb->env_db->section ); - - /* Copy the parent environment */ - - if (!ENV_InheritEnvironment( pdb, env )) return FALSE; - - /* Copy the command line */ - - if (!(pdb->env_db->cmd_line = HEAP_strdupA( pdb->heap, 0, cmd_line ))) - return FALSE; - - /* Remember startup info */ - if (!(pdb->env_db->startup_info = - HeapAlloc( pdb->heap, HEAP_ZERO_MEMORY, sizeof(STARTUPINFOA) ))) - return FALSE; - *pdb->env_db->startup_info = *startup; - - /* Inherit the standard handles */ - if (pdb->env_db->startup_info->dwFlags & STARTF_USESTDHANDLES) - { - pdb->env_db->hStdin = pdb->env_db->startup_info->hStdInput; - pdb->env_db->hStdout = pdb->env_db->startup_info->hStdOutput; - pdb->env_db->hStderr = pdb->env_db->startup_info->hStdError; - } - else if (inherit_handles) - { - pdb->env_db->hStdin = pdb->parent->env_db->hStdin; - pdb->env_db->hStdout = pdb->parent->env_db->hStdout; - pdb->env_db->hStderr = pdb->parent->env_db->hStderr; - } - /* else will be done later on in PROCESS_Create */ - - return TRUE; -} - - /*********************************************************************** * PROCESS_CreateEnvDB * @@ -455,15 +411,22 @@ BOOL PROCESS_Init(void) void PROCESS_Start(void) { UINT cmdShow = 0; - LPTHREAD_START_ROUTINE entry; + LPTHREAD_START_ROUTINE entry = NULL; THDB *thdb = THREAD_Current(); PDB *pdb = thdb->process; NE_MODULE *pModule = NE_GetPtr( pdb->module ); OFSTRUCT *ofs = (OFSTRUCT *)((char*)(pModule) + (pModule)->fileinfo); - IMAGE_OPTIONAL_HEADER *header = &PE_HEADER(pModule->module32)->OptionalHeader; + IMAGE_OPTIONAL_HEADER *header = !pModule->module32? NULL : + &PE_HEADER(pModule->module32)->OptionalHeader; - /* Setup process flags */ - if (header->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI) pdb->flags |= PDB32_CONSOLE_PROC; + /* Get process type */ + enum { PROC_DOS, PROC_WIN16, PROC_WIN32 } type; + if ( pdb->flags & PDB32_DOS_PROC ) + type = PROC_DOS; + else if ( pdb->flags & PDB32_WIN16_PROC ) + type = PROC_WIN16; + else + type = PROC_WIN32; /* Map system DLLs into this process (from initial process) */ /* FIXME: this is a hack */ @@ -473,23 +436,32 @@ void PROCESS_Start(void) InitializeCriticalSection( &pdb->crit_section ); /* Create the heap */ - - if (!(pdb->heap = HeapCreate( HEAP_GROWABLE, header->SizeOfHeapReserve, - header->SizeOfHeapCommit ))) goto error; + if (!(pdb->heap = HeapCreate( HEAP_GROWABLE, + header? header->SizeOfHeapReserve : 0x10000, + header? header->SizeOfHeapCommit : 0 ))) + goto error; pdb->heap_list = pdb->heap; /* Create the environment db */ if (!PROCESS_CreateEnvDB()) goto error; - PROCESS_CallUserSignalProc( USIG_THREAD_INIT, 0 ); /* for initial thread */ - /* Create a task for this process */ if (pdb->env_db->startup_info->dwFlags & STARTF_USESHOWWINDOW) cmdShow = pdb->env_db->startup_info->wShowWindow; - if (!TASK_Create( thdb, pModule, 0, 0, cmdShow )) goto error; + if (!TASK_Create( thdb, pModule, pdb->hInstance, pdb->hPrevInstance, cmdShow )) + goto error; - /* Link the task in the task list */ - TASK_StartTask( pdb->task ); + /* Note: The USIG_PROCESS_CREATE signal is supposed to be sent in the + * context of the parent process. Actually, the USER signal proc + * doesn't really care about that, but it *does* require that the + * startup parameters are correctly set up, so that GetProcessDword + * works. Furthermore, before calling the USER signal proc the + * 16-bit stack must be set up, which it is only after TASK_Create + * in the case of a 16-bit process. Thus, we send the signal here. + */ + PROCESS_CallUserSignalProc( USIG_PROCESS_CREATE, 0 ); + + PROCESS_CallUserSignalProc( USIG_THREAD_INIT, 0 ); /* for initial thread */ PROCESS_CallUserSignalProc( USIG_PROCESS_INIT, 0 ); @@ -498,38 +470,59 @@ void PROCESS_Start(void) CloseHandle( pdb->load_done_evt ); pdb->load_done_evt = INVALID_HANDLE_VALUE; - /* Send the debug event to the debugger */ - entry = (LPTHREAD_START_ROUTINE)RVA_PTR(pModule->module32, - OptionalHeader.AddressOfEntryPoint); - if (pdb->flags & PDB32_DEBUGGED) - DEBUG_SendCreateProcessEvent( -1 /*FIXME*/, pModule->module32, entry ); + /* Perform Win32 specific process initialization */ + if ( type == PROC_WIN32 ) + { + /* Send the debug event to the debugger */ + entry = (LPTHREAD_START_ROUTINE)RVA_PTR(pModule->module32, + OptionalHeader.AddressOfEntryPoint); + if (pdb->flags & PDB32_DEBUGGED) + DEBUG_SendCreateProcessEvent( -1 /*FIXME*/, pModule->module32, entry ); - /* Create 32-bit MODREF */ - if (!PE_CreateModule( pModule->module32, ofs, 0, FALSE )) goto error; + /* Create 32-bit MODREF */ + if (!PE_CreateModule( pModule->module32, ofs, 0, FALSE )) goto error; - /* Increment EXE refcount */ - assert( pdb->exe_modref ); - pdb->exe_modref->refCount++; + /* Increment EXE refcount */ + assert( pdb->exe_modref ); + pdb->exe_modref->refCount++; + + /* Initialize thread-local storage */ + PE_InitTls(); + } PROCESS_CallUserSignalProc( USIG_PROCESS_LOADED, 0 ); /* FIXME: correct location? */ - /* Initialize thread-local storage */ - - PE_InitTls(); - - if ( pdb->flags & PDB32_CONSOLE_PROC ) + if ( (pdb->flags & PDB32_CONSOLE_PROC) || (pdb->flags & PDB32_DOS_PROC) ) AllocConsole(); + if ( type == PROC_WIN32 ) + { + EnterCriticalSection( &pdb->crit_section ); + MODULE_DllProcessAttach( pdb->exe_modref, (LPVOID)1 ); + LeaveCriticalSection( &pdb->crit_section ); + } + /* Now call the entry point */ - - EnterCriticalSection( &pdb->crit_section ); - MODULE_DllProcessAttach( pdb->exe_modref, (LPVOID)1 ); - LeaveCriticalSection( &pdb->crit_section ); - PROCESS_CallUserSignalProc( USIG_PROCESS_RUNNING, 0 ); - TRACE_(relay)("(entryproc=%p)\n", entry ); - ExitProcess( entry(NULL) ); + switch ( type ) + { + case PROC_DOS: + TRACE_(relay)( "Starting DOS process\n" ); + DOSVM_Enter( NULL ); + ERR_(relay)( "DOSVM_Enter returned; should not happen!\n" ); + ExitProcess( 0 ); + + case PROC_WIN16: + TRACE_(relay)( "Starting Win16 process\n" ); + TASK_CallToStart(); + ERR_(relay)( "TASK_CallToStart returned; should not happen!\n" ); + ExitProcess( 0 ); + + case PROC_WIN32: + TRACE_(relay)( "Starting Win32 process (entryproc=%p)\n", entry ); + ExitProcess( entry(NULL) ); + } error: ExitProcess( GetLastError() ); @@ -547,12 +540,11 @@ PDB *PROCESS_Create( NE_MODULE *pModule, LPCSTR cmd_line, LPCSTR env, BOOL inherit, DWORD flags, STARTUPINFOA *startup, PROCESS_INFORMATION *info ) { - HANDLE load_done_evt = INVALID_HANDLE_VALUE; - DWORD size, commit; + HANDLE handles[2], load_done_evt = INVALID_HANDLE_VALUE; + DWORD exitcode, size; int server_thandle; struct new_process_request req; struct new_process_reply reply; - UINT cmdShow = 0; THDB *thdb = NULL; PDB *parent = PROCESS_Current(); PDB *pdb = PROCESS_CreatePDB( parent, inherit ); @@ -591,15 +583,23 @@ PDB *PROCESS_Create( NE_MODULE *pModule, LPCSTR cmd_line, LPCSTR env, ((parent->flags & PDB32_DEBUGGED) && !(flags & DEBUG_ONLY_THIS_PROCESS))) pdb->flags |= PDB32_DEBUGGED; - if (pModule->module32) + if (pModule->module32) /* Win32 process */ { - size = PE_HEADER(pModule->module32)->OptionalHeader.SizeOfStackReserve; + IMAGE_OPTIONAL_HEADER *header = &PE_HEADER(pModule->module32)->OptionalHeader; + size = header->SizeOfStackReserve; + if (header->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI) + pdb->flags |= PDB32_CONSOLE_PROC; } - else /* 16-bit process */ + else if (!pModule->dos_image) /* Win16 process */ { size = 0; pdb->flags |= PDB32_WIN16_PROC; } + else /* DOS process */ + { + size = 0; + pdb->flags |= PDB32_DOS_PROC; + } /* Create the main thread */ @@ -609,84 +609,49 @@ PDB *PROCESS_Create( NE_MODULE *pModule, LPCSTR cmd_line, LPCSTR env, info->dwThreadId = (DWORD)thdb->teb.tid; thdb->startup = PROCESS_Start; - if (pModule->module32) + /* Create the load-done event */ + load_done_evt = CreateEventA( NULL, TRUE, FALSE, NULL ); + DuplicateHandle( GetCurrentProcess(), load_done_evt, + info->hProcess, &pdb->load_done_evt, 0, TRUE, DUPLICATE_SAME_ACCESS ); + + /* Pass module/instance to new process (FIXME: hack) */ + pdb->module = pModule->self; + pdb->hInstance = hInstance; + pdb->hPrevInstance = hPrevInstance; + SYSDEPS_SpawnThread( thdb ); + + /* Wait until process is initialized (or initialization failed) */ + handles[0] = info->hProcess; + handles[1] = load_done_evt; + + switch ( WaitForMultipleObjects( 2, handles, FALSE, INFINITE ) ) { - HANDLE handles[2]; - DWORD exitcode; + default: + ERR_(process)( "WaitForMultipleObjects failed\n" ); + break; - /* Create the load-done event */ - load_done_evt = CreateEventA( NULL, TRUE, FALSE, NULL ); - DuplicateHandle( GetCurrentProcess(), load_done_evt, - info->hProcess, &pdb->load_done_evt, 0, TRUE, DUPLICATE_SAME_ACCESS ); + case 0: + /* Child initialization code returns error condition as exitcode */ + if ( GetExitCodeProcess( info->hProcess, &exitcode ) ) + SetLastError( exitcode ); + goto error; - /* Call USER signal proc */ - PROCESS_CallUserSignalProcHelper( USIG_PROCESS_CREATE, info->dwProcessId, 0, - pdb->flags, startup->dwFlags ); - - /* Set the process module (FIXME: hack) */ - pdb->module = pModule->self; - SYSDEPS_SpawnThread( thdb ); - - /* Wait until process is initialized (or initialization failed) */ - handles[0] = info->hProcess; - handles[1] = load_done_evt; - - switch ( WaitForMultipleObjects( 2, handles, FALSE, INFINITE ) ) + case 1: + /* Get 16-bit task up and running */ + if ( pdb->flags & PDB32_WIN16_PROC ) { - default: - ERR_(process)( "WaitForMultipleObjects failed\n" ); - break; + /* Post event to start the task */ + PostEvent16( pdb->task ); - case 0: - /* Child initialization code returns error condition as exitcode */ - if ( GetExitCodeProcess( info->hProcess, &exitcode ) ) - SetLastError( exitcode ); - goto error; - - case 1: - break; - } - - CloseHandle( load_done_evt ); - load_done_evt = INVALID_HANDLE_VALUE; - } - else /* Create a 16-bit process */ - { - /* Create the heap */ - size = 0x10000; - commit = 0; - if (!(pdb->heap = HeapCreate( HEAP_GROWABLE, size, commit ))) goto error; - pdb->heap_list = pdb->heap; - - /* Inherit the env DB from the parent */ - if (!PROCESS_InheritEnvDB( pdb, cmd_line, env, inherit, startup )) goto error; - - /* Duplicate the standard handles */ - if ((!(pdb->env_db->startup_info->dwFlags & STARTF_USESTDHANDLES)) && !inherit) - { - DuplicateHandle( GetCurrentProcess(), pdb->parent->env_db->hStdin, - info->hProcess, &pdb->env_db->hStdin, 0, TRUE, DUPLICATE_SAME_ACCESS ); - DuplicateHandle( GetCurrentProcess(), pdb->parent->env_db->hStdout, - info->hProcess, &pdb->env_db->hStdout, 0, TRUE, DUPLICATE_SAME_ACCESS ); - DuplicateHandle( GetCurrentProcess(), pdb->parent->env_db->hStderr, - info->hProcess, &pdb->env_db->hStderr, 0, TRUE, DUPLICATE_SAME_ACCESS ); + /* If we ourselves are a 16-bit task, we Yield() directly. */ + if ( parent->flags & PDB32_WIN16_PROC ) + OldYield16(); } + break; + } - /* Call USER signal proc */ - PROCESS_CallUserSignalProcHelper( USIG_PROCESS_CREATE, info->dwProcessId, 0, - pdb->flags, startup->dwFlags ); - - /* Create a Win16 task for this process */ - if (startup->dwFlags & STARTF_USESHOWWINDOW) cmdShow = startup->wShowWindow; - if (!TASK_Create( thdb, pModule, hInstance, hPrevInstance, cmdShow )) goto error; - - /* Map system DLLs into this process (from initial process) */ - /* FIXME: this is a hack */ - pdb->modref_list = PROCESS_Initial()->modref_list; - - /* Start the task */ - TASK_StartTask( pdb->task ); - } + CloseHandle( load_done_evt ); + load_done_evt = INVALID_HANDLE_VALUE; return pdb; @@ -708,9 +673,6 @@ void WINAPI ExitProcess( DWORD status ) MODULE_DllProcessDetach( TRUE, (LPVOID)1 ); LeaveCriticalSection( &PROCESS_Current()->crit_section ); - if ( THREAD_IsWin16( THREAD_Current() ) ) - TASK_KillCurrentTask( status ); - TASK_KillTask( 0 ); TerminateProcess( GetCurrentProcess(), status ); }