16-bit scheduler reorganized: run all tasks in their own thread.

Process creation sequence adapted to new scheduler.
oldstable
Ulrich Weigand 1999-06-06 14:47:50 +00:00 committed by Alexandre Julliard
parent ad6657e334
commit ed49003188
4 changed files with 321 additions and 495 deletions

View File

@ -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 */

View File

@ -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 );

View File

@ -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--;
}

View File

@ -8,6 +8,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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 );
}