4 * Copyright 1995 Alexandre Julliard
26 #include "selectors.h"
27 #include "stackframe.h"
35 /* Min. number of thunks allocated when creating a new segment */
38 extern INT32
WINSOCK_DeleteTaskWSI( TDB
* pTask
, struct _WSINFO
* );
39 extern BOOL32
MODULE_FreeModule( HMODULE16 hModule
, TDB
* ptaskContext
);
41 /* Pointer to function to switch to a larger stack */
42 int (*IF1632_CallLargeStack
)( int (*func
)(), void *arg
) = NULL
;
44 static HTASK16 hFirstTask
= 0;
45 static HTASK16 hCurrentTask
= 0;
46 static HTASK16 hTaskToKill
= 0;
47 static HTASK16 hLockedTask
= 0;
48 static UINT16 nTaskCount
= 0;
49 static HGLOBAL16 hDOSEnvironment
= 0;
51 static HGLOBAL16
TASK_CreateDOSEnvironment(void);
52 static void TASK_YieldToSystem(TDB
*);
55 /***********************************************************************
58 BOOL32
TASK_Init(void)
60 if (!(hDOSEnvironment
= TASK_CreateDOSEnvironment()))
61 fprintf( stderr
, "Not enough memory for DOS Environment\n" );
62 return (hDOSEnvironment
!= 0);
66 /***********************************************************************
69 HTASK16
TASK_GetNextTask( HTASK16 hTask
)
71 TDB
* pTask
= (TDB
*)GlobalLock16(hTask
);
73 if (pTask
->hNext
) return pTask
->hNext
;
74 return (hFirstTask
!= hTask
) ? hFirstTask
: 0;
78 /***********************************************************************
79 * TASK_CreateDOSEnvironment
81 * Create the original DOS environment.
83 static HGLOBAL16
TASK_CreateDOSEnvironment(void)
85 static const char program_name
[] = "KRNL386.EXE";
87 int initial_size
, size
, i
, winpathlen
, sysdirlen
;
90 extern char **environ
;
92 /* DOS environment format:
100 * ASCIIZ program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
103 /* First compute the size of the fixed part of the environment */
105 for (i
= winpathlen
= 0; ; i
++)
107 int len
= DIR_GetDosPath( i
, NULL
, 0 );
109 winpathlen
+= len
+ 1;
111 if (!winpathlen
) winpathlen
= 1;
112 sysdirlen
= GetSystemDirectory32A( NULL
, 0 ) + 1;
113 initial_size
= 5 + winpathlen
+ /* PATH=xxxx */
114 1 + /* BYTE 0 at end */
115 sizeof(WORD
) + /* WORD 1 */
116 sysdirlen
+ /* program directory */
117 strlen(program_name
) + 1; /* program name */
119 /* Compute the total size of the Unix environment (except path) */
121 for (e
= environ
, size
= initial_size
; *e
; e
++)
123 if (lstrncmpi32A(*e
, "path=", 5))
125 int len
= strlen(*e
) + 1;
126 if (size
+ len
>= 32767)
128 fprintf( stderr
, "Warning: environment larger than 32k.\n" );
136 /* Now allocate the environment */
138 if (!(handle
= GlobalAlloc16( GMEM_FIXED
, size
))) return 0;
139 p
= (char *)GlobalLock16( handle
);
141 /* And fill it with the Unix environment */
143 for (e
= environ
, size
= initial_size
; *e
; e
++)
145 if (lstrncmpi32A(*e
, "path=", 5))
147 int len
= strlen(*e
) + 1;
148 if (size
+ len
>= 32767) break;
155 /* Now add the path */
157 strcpy( p
, "PATH=" );
158 for (i
= 0, p
+= 5; ; i
++)
160 if (!DIR_GetDosPath( i
, p
, winpathlen
)) break;
164 if (p
[-1] == ';') p
[-1] = '\0';
167 /* Now add the program name */
172 GetSystemDirectory32A( p
, sysdirlen
);
174 strcat( p
, program_name
);
178 p
= (char *) GlobalLock16( handle
);
179 TRACE(task
, "Master DOS environment at %p\n", p
);
180 for (; *p
; p
+= strlen(p
) + 1) TRACE(task
, " %s\n", p
);
181 TRACE(task
, "Progname: %s\n", p
+3 );
187 /***********************************************************************
190 static void TASK_LinkTask( HTASK16 hTask
)
195 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return;
196 prevTask
= &hFirstTask
;
199 TDB
*prevTaskPtr
= (TDB
*)GlobalLock16( *prevTask
);
200 if (prevTaskPtr
->priority
>= pTask
->priority
) break;
201 prevTask
= &prevTaskPtr
->hNext
;
203 pTask
->hNext
= *prevTask
;
209 /***********************************************************************
212 static void TASK_UnlinkTask( HTASK16 hTask
)
217 prevTask
= &hFirstTask
;
218 while (*prevTask
&& (*prevTask
!= hTask
))
220 pTask
= (TDB
*)GlobalLock16( *prevTask
);
221 prevTask
= &pTask
->hNext
;
225 pTask
= (TDB
*)GlobalLock16( *prevTask
);
226 *prevTask
= pTask
->hNext
;
233 /***********************************************************************
236 * Create a thunk free-list in segment 'handle', starting from offset 'offset'
237 * and containing 'count' entries.
239 static void TASK_CreateThunks( HGLOBAL16 handle
, WORD offset
, WORD count
)
245 pThunk
= (THUNKS
*)((BYTE
*)GlobalLock16( handle
) + offset
);
247 pThunk
->magic
= THUNK_MAGIC
;
248 pThunk
->free
= (int)&pThunk
->thunks
- (int)pThunk
;
250 for (i
= 0; i
< count
-1; i
++)
252 free
+= 8; /* Offset of next thunk */
253 pThunk
->thunks
[4*i
] = free
;
255 pThunk
->thunks
[4*i
] = 0; /* Last thunk */
259 /***********************************************************************
262 * Allocate a thunk for MakeProcInstance().
264 static SEGPTR
TASK_AllocThunk( HTASK16 hTask
)
270 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return 0;
271 sel
= pTask
->hCSAlias
;
272 pThunk
= &pTask
->thunks
;
273 base
= (int)pThunk
- (int)pTask
;
274 while (!pThunk
->free
)
277 if (!sel
) /* Allocate a new segment */
279 sel
= GLOBAL_Alloc( GMEM_FIXED
, sizeof(THUNKS
) + (MIN_THUNKS
-1)*8,
280 pTask
->hPDB
, TRUE
, FALSE
, FALSE
);
281 if (!sel
) return (SEGPTR
)0;
282 TASK_CreateThunks( sel
, 0, MIN_THUNKS
);
285 pThunk
= (THUNKS
*)GlobalLock16( sel
);
288 base
+= pThunk
->free
;
289 pThunk
->free
= *(WORD
*)((BYTE
*)pThunk
+ pThunk
->free
);
290 return PTR_SEG_OFF_TO_SEGPTR( sel
, base
);
294 /***********************************************************************
297 * Free a MakeProcInstance() thunk.
299 static BOOL32
TASK_FreeThunk( HTASK16 hTask
, SEGPTR thunk
)
305 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return 0;
306 sel
= pTask
->hCSAlias
;
307 pThunk
= &pTask
->thunks
;
308 base
= (int)pThunk
- (int)pTask
;
309 while (sel
&& (sel
!= HIWORD(thunk
)))
312 pThunk
= (THUNKS
*)GlobalLock16( sel
);
315 if (!sel
) return FALSE
;
316 *(WORD
*)((BYTE
*)pThunk
+ LOWORD(thunk
) - base
) = pThunk
->free
;
317 pThunk
->free
= LOWORD(thunk
) - base
;
322 /***********************************************************************
325 * 32-bit entry point for a new task. This function is responsible for
326 * setting up the registers and jumping to the 16-bit entry point.
328 static void TASK_CallToStart(void)
331 TDB
*pTask
= (TDB
*)GlobalLock16( hCurrentTask
);
332 NE_MODULE
*pModule
= MODULE_GetPtr( pTask
->hModule
);
333 SEGTABLEENTRY
*pSegTable
= NE_SEG_TABLE( pModule
);
335 SET_CUR_THREAD( pTask
->thdb
);
336 /* Terminate the stack frame */
337 THREAD_STACK16(pTask
->thdb
)->frame32
= NULL
;
338 if (pModule
->flags
& NE_FFLAGS_WIN32
)
340 /* FIXME: all this is an ugly hack */
342 FARPROC32 entry
= (FARPROC32
)RVA_PTR( PROCESS_Current()->exe_modref
->module
, OptionalHeader
.AddressOfEntryPoint
);
344 pTask
->userhandler
= (USERSIGNALPROC
)&USER_SignalProc
;
345 if (pModule
->heap_size
)
346 LocalInit( pTask
->hInstance
, 0, pModule
->heap_size
);
348 InitApp( pTask
->hModule
);
349 PE_InitializeDLLs( PROCESS_Current(), DLL_PROCESS_ATTACH
, (LPVOID
)-1 );
350 TRACE(relay
, "(entryproc=%p)\n", entry
);
352 TASK_KillCurrentTask( exit_code
);
356 /* Registers at initialization must be:
358 * bx stack size in bytes
359 * cx heap size in bytes
360 * si previous app instance
361 * di current app instance
363 * es selector to the PSP
364 * ds dgroup of the application
366 * sp top of the stack
370 memset( &context
, 0, sizeof(context
) );
371 CS_reg(&context
) = pSegTable
[pModule
->cs
- 1].selector
;
372 DS_reg(&context
) = pSegTable
[pModule
->dgroup
- 1].selector
;
373 ES_reg(&context
) = pTask
->hPDB
;
374 EIP_reg(&context
) = pModule
->ip
;
375 EBX_reg(&context
) = pModule
->stack_size
;
376 ECX_reg(&context
) = pModule
->heap_size
;
377 EDI_reg(&context
) = context
.SegDs
;
379 TRACE(task
, "Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
380 CS_reg(&context
), IP_reg(&context
), DS_reg(&context
),
381 SELECTOROF(pTask
->thdb
->cur_stack
),
382 OFFSETOF(pTask
->thdb
->cur_stack
) );
384 Callbacks
->CallRegisterShortProc( &context
, 0 );
385 /* This should never return */
386 fprintf( stderr
, "TASK_CallToStart: Main program returned!\n" );
387 TASK_KillCurrentTask( 1 );
392 /***********************************************************************
395 HTASK16
TASK_CreateTask( HMODULE16 hModule
, HINSTANCE16 hInstance
,
396 HINSTANCE16 hPrevInstance
, HANDLE16 hEnvironment
,
397 LPCSTR cmdLine
, UINT16 cmdShow
)
402 HGLOBAL16 hParentEnv
;
404 SEGTABLEENTRY
*pSegTable
;
408 STACK16FRAME
*frame16
;
409 STACK32FRAME
*frame32
;
411 if (!(pModule
= MODULE_GetPtr( hModule
))) return 0;
412 pSegTable
= NE_SEG_TABLE( pModule
);
414 /* Allocate the task structure */
416 hTask
= GLOBAL_Alloc( GMEM_FIXED
| GMEM_ZEROINIT
, sizeof(TDB
),
417 hModule
, FALSE
, FALSE
, FALSE
);
418 if (!hTask
) return 0;
419 pTask
= (TDB
*)GlobalLock16( hTask
);
421 /* Allocate the new environment block */
423 if (!(hParentEnv
= hEnvironment
))
425 TDB
*pParent
= (TDB
*)GlobalLock16( hCurrentTask
);
426 hParentEnv
= pParent
? pParent
->pdb
.environment
: hDOSEnvironment
;
428 /* FIXME: do we really need to make a copy also when */
429 /* we don't use the parent environment? */
430 if (!(hEnvironment
= GlobalAlloc16( GMEM_FIXED
, GlobalSize16(hParentEnv
))))
432 GlobalFree16( hTask
);
435 memcpy( GlobalLock16( hEnvironment
), GlobalLock16( hParentEnv
),
436 GlobalSize16( hParentEnv
) );
438 /* Fill the task structure */
440 pTask
->nEvents
= 1; /* So the task can be started */
441 pTask
->hSelf
= hTask
;
444 if (pModule
->flags
& NE_FFLAGS_WIN32
)
445 pTask
->flags
|= TDBF_WIN32
;
447 pTask
->version
= pModule
->expected_version
;
448 pTask
->hInstance
= hInstance
;
449 pTask
->hPrevInstance
= hPrevInstance
;
450 pTask
->hModule
= hModule
;
451 pTask
->hParent
= hCurrentTask
;
452 pTask
->magic
= TDB_MAGIC
;
453 pTask
->nCmdShow
= cmdShow
;
454 pTask
->curdrive
= DRIVE_GetCurrentDrive() | 0x80;
455 strcpy( pTask
->curdir
, "\\" );
456 lstrcpyn32A( pTask
->curdir
+ 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
457 sizeof(pTask
->curdir
) - 1 );
459 /* Create the thunks block */
461 TASK_CreateThunks( hTask
, (int)&pTask
->thunks
- (int)pTask
, 7 );
463 /* Copy the module name */
465 name
= MODULE_GetModuleName( hModule
);
466 strncpy( pTask
->module_name
, name
, sizeof(pTask
->module_name
) );
468 /* Allocate a selector for the PDB */
470 pTask
->hPDB
= GLOBAL_CreateBlock( GMEM_FIXED
, &pTask
->pdb
, sizeof(PDB
),
471 hModule
, FALSE
, FALSE
, FALSE
, NULL
);
475 pTask
->pdb
.int20
= 0x20cd;
476 pTask
->pdb
.dispatcher
[0] = 0x9a; /* ljmp */
477 PUT_DWORD(&pTask
->pdb
.dispatcher
[1], (DWORD
)MODULE_GetEntryPoint(
478 GetModuleHandle16("KERNEL"), 102 )); /* KERNEL.102 is DOS3Call() */
479 pTask
->pdb
.savedint22
= INT_GetHandler( 0x22 );
480 pTask
->pdb
.savedint23
= INT_GetHandler( 0x23 );
481 pTask
->pdb
.savedint24
= INT_GetHandler( 0x24 );
482 pTask
->pdb
.fileHandlesPtr
=
483 PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel(pTask
->hPDB
),
484 (int)&((PDB
*)0)->fileHandles
);
485 pTask
->pdb
.hFileHandles
= 0;
486 memset( pTask
->pdb
.fileHandles
, 0xff, sizeof(pTask
->pdb
.fileHandles
) );
487 pTask
->pdb
.environment
= hEnvironment
;
488 pTask
->pdb
.nbFiles
= 20;
489 lstrcpyn32A( pTask
->pdb
.cmdLine
, cmdLine
, sizeof(pTask
->pdb
.cmdLine
) );
491 /* Get the compatibility flags */
493 pTask
->compat_flags
= GetProfileInt32A( "Compatibility", name
, 0 );
495 /* Allocate a code segment alias for the TDB */
497 pTask
->hCSAlias
= GLOBAL_CreateBlock( GMEM_FIXED
, (void *)pTask
,
498 sizeof(TDB
), pTask
->hPDB
, TRUE
,
499 FALSE
, FALSE
, NULL
);
501 /* Set the owner of the environment block */
503 FarSetOwner( pTask
->pdb
.environment
, pTask
->hPDB
);
505 /* Default DTA overwrites command-line */
507 pTask
->dta
= PTR_SEG_OFF_TO_SEGPTR( pTask
->hPDB
,
508 (int)&pTask
->pdb
.cmdLine
- (int)&pTask
->pdb
);
510 /* Create the Win32 part of the task */
512 pdb32
= PROCESS_Create( pTask
, cmdLine
);
513 /* FIXME: check for pdb32 == NULL. */
515 if (pModule
->flags
& NE_FFLAGS_WIN32
)
518 LPTHREAD_START_ROUTINE start =
519 (LPTHREAD_START_ROUTINE)(
520 PROCESS_Current()->exe_modref->load_addr +
521 PROCESS_Current()->exe_modref->pe_module->pe_header->OptionalHeader.AddressOfEntryPoint);
523 pTask
->thdb
= THREAD_Create( pdb32
,
524 PE_HEADER(pModule
->module32
)->OptionalHeader
.SizeOfStackReserve
,
528 pTask
->thdb
= THREAD_Create( pdb32
, 0, NULL
, NULL
);
529 /* FIXME: check for pTask->thdb == NULL. */
531 /* Create the 16-bit stack frame */
533 if (!(sp
= pModule
->sp
))
534 sp
= pSegTable
[pModule
->ss
-1].minsize
+ pModule
->stack_size
;
536 pTask
->thdb
->cur_stack
= PTR_SEG_OFF_TO_SEGPTR( hInstance
, sp
);
537 pTask
->thdb
->cur_stack
-= sizeof(STACK16FRAME
) + sizeof(STACK32FRAME
*);
538 frame16
= (STACK16FRAME
*)PTR_SEG_TO_LIN( pTask
->thdb
->cur_stack
);
539 frame16
->ebp
= sp
+ (int)&((STACK16FRAME
*)0)->bp
;
540 frame16
->bp
= LOWORD(frame16
->ebp
);
541 frame16
->ds
= frame16
->es
= pTask
->hInstance
;
542 frame16
->entry_point
= 0;
543 frame16
->entry_cs
= 0;
544 /* The remaining fields will be initialized in TASK_Reschedule */
546 /* Create the 32-bit stack frame */
548 stack32Top
= (char*)pTask
->thdb
->teb
.stack_top
;
549 frame16
->frame32
= frame32
= (STACK32FRAME
*)stack32Top
- 1;
550 frame32
->frame16
= pTask
->thdb
->cur_stack
+ sizeof(STACK16FRAME
);
556 frame32
->retaddr
= (DWORD
)TASK_CallToStart
;
557 /* The remaining fields will be initialized in TASK_Reschedule */
559 if (!THREAD_Current()->cur_stack
)
560 THREAD_Current()->cur_stack
= pTask
->thdb
->cur_stack
;
562 /* Add the task to the linked list */
564 TASK_LinkTask( hTask
);
566 TRACE(task
, "module='%s' cmdline='%s' task=%04x\n",
567 name
, cmdLine
, hTask
);
573 /***********************************************************************
576 static void TASK_DeleteTask( HTASK16 hTask
)
581 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return;
584 pTask
->magic
= 0xdead; /* invalidate signature */
586 /* Delete the Win32 part of the task */
588 K32OBJ_DecCount( &pTask
->thdb
->process
->header
);
589 K32OBJ_DecCount( &pTask
->thdb
->header
);
591 /* Free the task module */
593 MODULE_FreeModule( pTask
->hModule
, pTask
);
595 /* Free the selector aliases */
597 GLOBAL_FreeBlock( pTask
->hCSAlias
);
598 GLOBAL_FreeBlock( pTask
->hPDB
);
600 /* Free the task structure itself */
602 GlobalFree16( hTask
);
604 /* Free all memory used by this task (including the 32-bit stack, */
605 /* the environment block and the thunk segments). */
607 GlobalFreeAll( hPDB
);
611 /***********************************************************************
612 * TASK_KillCurrentTask
614 * Kill the currently running task. As it's not possible to kill the
615 * current task like this, it is simply marked for destruction, and will
616 * be killed when either TASK_Reschedule or this function is called again
617 * in the context of another task.
619 void TASK_KillCurrentTask( INT16 exitCode
)
621 TDB
* pTask
= (TDB
*) GlobalLock16( hCurrentTask
);
622 if (!pTask
) USER_ExitWindows(); /* No current task yet */
624 TRACE(task
, "Killing task %04x\n", hCurrentTask
);
626 /* Delete active sockets */
629 WINSOCK_DeleteTaskWSI( pTask
, pTask
->pwsi
);
631 /* Perform USER cleanup */
633 if (pTask
->userhandler
)
634 pTask
->userhandler( hCurrentTask
, USIG_TERMINATION
, 0,
635 pTask
->hInstance
, pTask
->hQueue
);
637 if (hTaskToKill
&& (hTaskToKill
!= hCurrentTask
))
639 /* If another task is already marked for destruction, */
640 /* we can kill it now, as we are in another context. */
641 TASK_DeleteTask( hTaskToKill
);
646 TRACE(task
, "this is the last task, exiting\n" );
650 /* Remove the task from the list to be sure we never switch back to it */
651 TASK_UnlinkTask( hCurrentTask
);
654 TDB
* p
= (TDB
*)GlobalLock16( hFirstTask
);
657 if( p
->hYieldTo
== hCurrentTask
) p
->hYieldTo
= 0;
658 p
= (TDB
*)GlobalLock16( p
->hNext
);
662 hTaskToKill
= hCurrentTask
;
666 TASK_YieldToSystem(pTask
);
668 /* We should never return from this Yield() */
670 fprintf(stderr
,"Return of the living dead %04x!!!\n", hCurrentTask
);
674 /***********************************************************************
677 * This is where all the magic of task-switching happens!
679 * Note: This function should only be called via the TASK_YieldToSystem()
680 * wrapper, to make sure that all the context is saved correctly.
682 * It must not call functions that may yield control.
684 void TASK_Reschedule(void)
686 TDB
*pOldTask
= NULL
, *pNewTask
;
688 STACK16FRAME
*newframe16
;
693 /* First check if there's a task to kill */
695 if (hTaskToKill
&& (hTaskToKill
!= hCurrentTask
))
697 TASK_DeleteTask( hTaskToKill
);
701 /* Find a task to yield to */
703 pOldTask
= (TDB
*)GlobalLock16( hCurrentTask
);
704 if (pOldTask
&& pOldTask
->hYieldTo
)
706 /* check for DirectedYield() */
708 hTask
= pOldTask
->hYieldTo
;
709 pNewTask
= (TDB
*)GlobalLock16( hTask
);
710 if( !pNewTask
|| !pNewTask
->nEvents
) hTask
= 0;
711 pOldTask
->hYieldTo
= 0;
714 /* extract hardware events only! */
716 if (!hTask
) EVENT_WaitNetEvent( FALSE
, TRUE
);
720 /* Find a task that has an event pending */
725 pNewTask
= (TDB
*)GlobalLock16( hTask
);
727 TRACE(task
, "\ttask = %04x, events = %i\n", hTask
, pNewTask
->nEvents
);
729 if (pNewTask
->nEvents
) break;
730 hTask
= pNewTask
->hNext
;
732 if (hLockedTask
&& (hTask
!= hLockedTask
)) hTask
= 0;
735 /* No task found, wait for some events to come in */
737 EVENT_WaitNetEvent( TRUE
, TRUE
);
740 if (hTask
== hCurrentTask
)
742 TRACE(task
, "returning to the current task(%04x)\n", hTask
);
743 return; /* Nothing to do */
745 pNewTask
= (TDB
*)GlobalLock16( hTask
);
746 TRACE(task
, "Switching to task %04x (%.8s)\n",
747 hTask
, pNewTask
->module_name
);
749 /* Make the task the last in the linked list (round-robin scheduling) */
751 pNewTask
->priority
++;
752 TASK_UnlinkTask( hTask
);
753 TASK_LinkTask( hTask
);
754 pNewTask
->priority
--;
756 /* Finish initializing the new task stack if necessary */
758 newframe16
= THREAD_STACK16( pNewTask
->thdb
);
759 if (!newframe16
->entry_cs
)
761 STACK16FRAME
*oldframe16
= CURRENT_STACK16
;
762 STACK32FRAME
*oldframe32
= oldframe16
->frame32
;
763 STACK32FRAME
*newframe32
= newframe16
->frame32
;
764 newframe16
->entry_ip
= oldframe16
->entry_ip
;
765 newframe16
->entry_cs
= oldframe16
->entry_cs
;
766 newframe16
->ip
= oldframe16
->ip
;
767 newframe16
->cs
= oldframe16
->cs
;
768 newframe32
->ebp
= oldframe32
->ebp
;
769 newframe32
->restore_addr
= oldframe32
->restore_addr
;
770 newframe32
->codeselector
= oldframe32
->codeselector
;
773 /* Switch to the new stack */
775 hCurrentTask
= hTask
;
776 SET_CUR_THREAD( pNewTask
->thdb
);
777 pNewTask
->ss_sp
= pNewTask
->thdb
->cur_stack
;
781 /***********************************************************************
784 * Scheduler interface, this way we ensure that all "unsafe" events are
785 * processed outside the scheduler.
787 void TASK_YieldToSystem(TDB
* pTask
)
791 Callbacks
->CallTaskRescheduleProc();
795 pQ
= (MESSAGEQUEUE
*)GlobalLock16(pTask
->hQueue
);
796 if( pQ
&& pQ
->flags
& QUEUE_FLAG_XEVENT
&&
797 !(pQ
->wakeBits
& (QS_SENDMESSAGE
| QS_SMRESULT
)) )
799 pQ
->flags
&= ~QUEUE_FLAG_XEVENT
;
800 EVENT_WaitNetEvent( FALSE
, FALSE
);
806 /***********************************************************************
807 * InitTask (KERNEL.91)
809 * Called by the application startup code.
811 void WINAPI
InitTask( CONTEXT
*context
)
815 SEGTABLEENTRY
*pSegTable
;
816 INSTANCEDATA
*pinstance
;
817 LONG stacklow
, stackhi
;
819 if (context
) EAX_reg(context
) = 0;
820 if (!(pTask
= (TDB
*)GlobalLock16( hCurrentTask
))) return;
821 if (!(pModule
= MODULE_GetPtr( pTask
->hModule
))) return;
823 /* This is a hack to install task USER signal handler before
824 * implicitly loaded DLLs are initialized (see windows/user.c) */
826 pTask
->userhandler
= (USERSIGNALPROC
)&USER_SignalProc
;
828 /* Initialize implicitly loaded DLLs */
829 NE_InitializeDLLs( pTask
->hModule
);
833 /* Registers on return are:
834 * ax 1 if OK, 0 on error
835 * cx stack limit in bytes
836 * dx cmdShow parameter
837 * si instance handle of the previous instance
838 * di instance handle of the new task
839 * es:bx pointer to command-line inside PSP
841 EAX_reg(context
) = 1;
842 EBX_reg(context
) = pTask
->pdb
.cmdLine
[0] ? 0x81 : 0x80;
843 ECX_reg(context
) = pModule
->stack_size
;
844 EDX_reg(context
) = pTask
->nCmdShow
;
845 ESI_reg(context
) = (DWORD
)pTask
->hPrevInstance
;
846 EDI_reg(context
) = (DWORD
)pTask
->hInstance
;
847 ES_reg (context
) = (WORD
)pTask
->hPDB
;
850 /* Initialize the local heap */
851 if ( pModule
->heap_size
)
853 LocalInit( pTask
->hInstance
, 0, pModule
->heap_size
);
856 /* Initialize the INSTANCEDATA structure */
857 pSegTable
= NE_SEG_TABLE( pModule
);
858 stacklow
= pSegTable
[pModule
->ss
- 1].minsize
;
859 stackhi
= stacklow
+ pModule
->stack_size
;
860 if (stackhi
> 0xffff) stackhi
= 0xffff;
861 pinstance
= (INSTANCEDATA
*)PTR_SEG_OFF_TO_LIN(CURRENT_DS
, 0);
862 pinstance
->stackbottom
= stackhi
; /* yup, that's right. Confused me too. */
863 pinstance
->stacktop
= stacklow
;
864 pinstance
->stackmin
= OFFSETOF( pTask
->thdb
->cur_stack
);
868 /***********************************************************************
869 * WaitEvent (KERNEL.30)
871 BOOL16 WINAPI
WaitEvent( HTASK16 hTask
)
875 if (!hTask
) hTask
= hCurrentTask
;
876 pTask
= (TDB
*)GlobalLock16( hTask
);
877 if (pTask
->nEvents
> 0)
882 TASK_YieldToSystem(pTask
);
884 /* When we get back here, we have an event */
886 if (pTask
->nEvents
> 0) pTask
->nEvents
--;
891 /***********************************************************************
892 * PostEvent (KERNEL.31)
894 void WINAPI
PostEvent( HTASK16 hTask
)
898 if (!hTask
) hTask
= hCurrentTask
;
899 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return;
904 /***********************************************************************
905 * SetPriority (KERNEL.32)
907 void WINAPI
SetPriority( HTASK16 hTask
, INT16 delta
)
912 if (!hTask
) hTask
= hCurrentTask
;
913 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return;
914 newpriority
= pTask
->priority
+ delta
;
915 if (newpriority
< -32) newpriority
= -32;
916 else if (newpriority
> 15) newpriority
= 15;
918 pTask
->priority
= newpriority
+ 1;
919 TASK_UnlinkTask( hTask
);
920 TASK_LinkTask( hTask
);
925 /***********************************************************************
926 * LockCurrentTask (KERNEL.33)
928 HTASK16 WINAPI
LockCurrentTask( BOOL16 bLock
)
930 if (bLock
) hLockedTask
= hCurrentTask
;
931 else hLockedTask
= 0;
936 /***********************************************************************
937 * IsTaskLocked (KERNEL.122)
939 HTASK16 WINAPI
IsTaskLocked(void)
945 /***********************************************************************
946 * OldYield (KERNEL.117)
948 void WINAPI
OldYield(void)
952 pCurTask
= (TDB
*)GlobalLock16( hCurrentTask
);
953 if (pCurTask
) pCurTask
->nEvents
++; /* Make sure we get back here */
954 TASK_YieldToSystem(pCurTask
);
955 if (pCurTask
) pCurTask
->nEvents
--;
959 /***********************************************************************
960 * DirectedYield (KERNEL.150)
962 void WINAPI
DirectedYield( HTASK16 hTask
)
964 TDB
*pCurTask
= (TDB
*)GlobalLock16( hCurrentTask
);
965 pCurTask
->hYieldTo
= hTask
;
970 /***********************************************************************
971 * UserYield (USER.332)
973 void WINAPI
UserYield(void)
975 TDB
*pCurTask
= (TDB
*)GlobalLock16( hCurrentTask
);
976 MESSAGEQUEUE
*queue
= (MESSAGEQUEUE
*)GlobalLock16( pCurTask
->hQueue
);
977 /* Handle sent messages */
978 while (queue
&& (queue
->wakeBits
& QS_SENDMESSAGE
))
979 QUEUE_ReceiveMessage( queue
);
983 queue
= (MESSAGEQUEUE
*)GlobalLock16( pCurTask
->hQueue
);
984 while (queue
&& (queue
->wakeBits
& QS_SENDMESSAGE
))
985 QUEUE_ReceiveMessage( queue
);
989 /***********************************************************************
990 * Yield16 (KERNEL.29)
992 void WINAPI
Yield16(void)
994 TDB
*pCurTask
= (TDB
*)GlobalLock16( hCurrentTask
);
995 if (pCurTask
) pCurTask
->hYieldTo
= 0;
996 if (pCurTask
&& pCurTask
->hQueue
) UserYield();
1001 /***********************************************************************
1002 * MakeProcInstance16 (KERNEL.51)
1004 FARPROC16 WINAPI
MakeProcInstance16( FARPROC16 func
, HANDLE16 hInstance
)
1009 if (!hInstance
) return 0;
1010 if (__winelib
) return func
; /* func can be called directly in Winelib */
1011 thunkaddr
= TASK_AllocThunk( hCurrentTask
);
1012 if (!thunkaddr
) return (FARPROC16
)0;
1013 thunk
= PTR_SEG_TO_LIN( thunkaddr
);
1014 lfunc
= PTR_SEG_TO_LIN( func
);
1016 TRACE(task
, "(%08lx,%04x): got thunk %08lx\n",
1017 (DWORD
)func
, hInstance
, (DWORD
)thunkaddr
);
1018 if (((lfunc
[0]==0x8c) && (lfunc
[1]==0xd8)) ||
1019 ((lfunc
[0]==0x1e) && (lfunc
[1]==0x58))
1021 fprintf(stderr
,"FIXME: MakeProcInstance16 thunk would be useless for %p, overwriting with nop;nop;\n", func
);
1022 lfunc
[0]=0x90; /* nop */
1023 lfunc
[1]=0x90; /* nop */
1026 *thunk
++ = 0xb8; /* movw instance, %ax */
1027 *thunk
++ = (BYTE
)(hInstance
& 0xff);
1028 *thunk
++ = (BYTE
)(hInstance
>> 8);
1029 *thunk
++ = 0xea; /* ljmp func */
1030 *(DWORD
*)thunk
= (DWORD
)func
;
1031 return (FARPROC16
)thunkaddr
;
1035 /***********************************************************************
1036 * FreeProcInstance16 (KERNEL.52)
1038 void WINAPI
FreeProcInstance16( FARPROC16 func
)
1040 TRACE(task
, "(%08lx)\n", (DWORD
)func
);
1041 if (!__winelib
) TASK_FreeThunk( hCurrentTask
, (SEGPTR
)func
);
1045 /**********************************************************************
1046 * GetCodeHandle (KERNEL.93)
1048 HANDLE16 WINAPI
GetCodeHandle( FARPROC16 proc
)
1051 BYTE
*thunk
= (BYTE
*)PTR_SEG_TO_LIN( proc
);
1053 if (__winelib
) return 0;
1055 /* Return the code segment containing 'proc'. */
1056 /* Not sure if this is really correct (shouldn't matter that much). */
1058 /* Check if it is really a thunk */
1059 if ((thunk
[0] == 0xb8) && (thunk
[3] == 0xea))
1060 handle
= GlobalHandle16( thunk
[6] + (thunk
[7] << 8) );
1062 handle
= GlobalHandle16( HIWORD(proc
) );
1068 /**********************************************************************
1069 * DefineHandleTable16 (KERNEL.94)
1071 BOOL16 WINAPI
DefineHandleTable16( WORD wOffset
)
1073 return TRUE
; /* FIXME */
1077 /***********************************************************************
1078 * SetTaskQueue (KERNEL.34)
1080 HQUEUE16 WINAPI
SetTaskQueue( HTASK16 hTask
, HQUEUE16 hQueue
)
1085 if (!hTask
) hTask
= hCurrentTask
;
1086 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return 0;
1088 hPrev
= pTask
->hQueue
;
1089 pTask
->hQueue
= hQueue
;
1091 TIMER_SwitchQueue( hPrev
, hQueue
);
1097 /***********************************************************************
1098 * GetTaskQueue (KERNEL.35)
1100 HQUEUE16 WINAPI
GetTaskQueue( HTASK16 hTask
)
1104 if (!hTask
) hTask
= hCurrentTask
;
1105 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return 0;
1106 return pTask
->hQueue
;
1110 /***********************************************************************
1111 * SwitchStackTo (KERNEL.108)
1113 void WINAPI
SwitchStackTo( WORD seg
, WORD ptr
, WORD top
)
1116 STACK16FRAME
*oldFrame
, *newFrame
;
1117 INSTANCEDATA
*pData
;
1120 if (!(pTask
= (TDB
*)GlobalLock16( hCurrentTask
))) return;
1121 if (!(pData
= (INSTANCEDATA
*)GlobalLock16( seg
))) return;
1122 TRACE(task
, "old=%04x:%04x new=%04x:%04x\n",
1123 SELECTOROF( pTask
->thdb
->cur_stack
),
1124 OFFSETOF( pTask
->thdb
->cur_stack
), seg
, ptr
);
1126 /* Save the old stack */
1128 pData
->old_ss_sp
= pTask
->thdb
->cur_stack
;
1129 pData
->stacktop
= top
;
1130 pData
->stackmin
= ptr
;
1131 pData
->stackbottom
= ptr
;
1133 /* Switch to the new stack */
1135 /* Note: we need to take the 3 arguments into account; otherwise,
1136 * the stack will underflow upon return from this function.
1138 oldFrame
= (STACK16FRAME
*)PTR_SEG_TO_LIN( pTask
->thdb
->cur_stack
);
1139 pTask
->thdb
->cur_stack
= PTR_SEG_OFF_TO_SEGPTR( seg
,
1140 ptr
- sizeof(STACK16FRAME
) - 3 * sizeof(WORD
) );
1141 newFrame
= (STACK16FRAME
*)PTR_SEG_TO_LIN( pTask
->thdb
->cur_stack
);
1143 /* Copy the stack frame and the local variables to the new stack */
1145 copySize
= oldFrame
->bp
- OFFSETOF(pData
->old_ss_sp
);
1146 memmove( newFrame
, oldFrame
, MAX( copySize
, sizeof(STACK16FRAME
) ));
1150 /***********************************************************************
1151 * SwitchStackBack (KERNEL.109)
1153 * Note: the function is declared as 'register' in the spec file in order
1154 * to make sure all registers are preserved, but we don't use them in any
1155 * way, so we don't need a CONTEXT* argument.
1157 void WINAPI
SwitchStackBack(void)
1160 STACK16FRAME
*oldFrame
, *newFrame
;
1161 INSTANCEDATA
*pData
;
1163 if (!(pTask
= (TDB
*)GlobalLock16( hCurrentTask
))) return;
1164 if (!(pData
= (INSTANCEDATA
*)GlobalLock16(SELECTOROF(pTask
->thdb
->cur_stack
))))
1166 if (!pData
->old_ss_sp
)
1168 fprintf( stderr
, "SwitchStackBack: no previous SwitchStackTo\n" );
1171 TRACE(task
, "restoring stack %04x:%04x\n",
1172 SELECTOROF(pData
->old_ss_sp
), OFFSETOF(pData
->old_ss_sp
) );
1174 oldFrame
= (STACK16FRAME
*)PTR_SEG_TO_LIN( pTask
->thdb
->cur_stack
);
1176 /* Switch back to the old stack */
1178 pTask
->thdb
->cur_stack
= pData
->old_ss_sp
;
1179 pData
->old_ss_sp
= 0;
1181 /* Build a stack frame for the return */
1183 newFrame
= (STACK16FRAME
*)PTR_SEG_TO_LIN( pTask
->thdb
->cur_stack
);
1184 newFrame
->frame32
= oldFrame
->frame32
;
1185 if (TRACE_ON(relay
))
1187 newFrame
->entry_ip
= oldFrame
->entry_ip
;
1188 newFrame
->entry_cs
= oldFrame
->entry_cs
;
1193 /***********************************************************************
1194 * GetTaskQueueDS (KERNEL.118)
1196 void WINAPI
GetTaskQueueDS( CONTEXT
*context
)
1198 DS_reg(context
) = GlobalHandleToSel( GetTaskQueue(0) );
1202 /***********************************************************************
1203 * GetTaskQueueES (KERNEL.119)
1205 void WINAPI
GetTaskQueueES( CONTEXT
*context
)
1207 ES_reg(context
) = GlobalHandleToSel( GetTaskQueue(0) );
1211 /***********************************************************************
1212 * GetCurrentTask (KERNEL.36)
1214 HTASK16 WINAPI
GetCurrentTask(void)
1216 return hCurrentTask
;
1219 DWORD WINAPI
WIN16_GetCurrentTask(void)
1221 /* This is the version used by relay code; the first task is */
1222 /* returned in the high word of the result */
1223 return MAKELONG( hCurrentTask
, hFirstTask
);
1227 /***********************************************************************
1228 * GetCurrentPDB (KERNEL.37)
1230 HANDLE16 WINAPI
GetCurrentPDB(void)
1234 if (!(pTask
= (TDB
*)GlobalLock16( hCurrentTask
))) return 0;
1239 /***********************************************************************
1240 * GetInstanceData (KERNEL.54)
1242 INT16 WINAPI
GetInstanceData( HINSTANCE16 instance
, WORD buffer
, INT16 len
)
1244 char *ptr
= (char *)GlobalLock16( instance
);
1245 if (!ptr
|| !len
) return 0;
1246 if ((int)buffer
+ len
>= 0x10000) len
= 0x10000 - buffer
;
1247 memcpy( (char *)GlobalLock16(CURRENT_DS
) + buffer
, ptr
+ buffer
, len
);
1252 /***********************************************************************
1253 * GetExeVersion (KERNEL.105)
1255 WORD WINAPI
GetExeVersion(void)
1259 if (!(pTask
= (TDB
*)GlobalLock16( hCurrentTask
))) return 0;
1260 return pTask
->version
;
1264 /***********************************************************************
1265 * SetErrorMode16 (KERNEL.107)
1267 UINT16 WINAPI
SetErrorMode16( UINT16 mode
)
1272 if (!(pTask
= (TDB
*)GlobalLock16( hCurrentTask
))) return 0;
1273 oldMode
= pTask
->error_mode
;
1274 pTask
->error_mode
= mode
;
1275 pTask
->thdb
->process
->error_mode
= mode
;
1280 /***********************************************************************
1281 * SetErrorMode32 (KERNEL32.486)
1283 UINT32 WINAPI
SetErrorMode32( UINT32 mode
)
1285 return SetErrorMode16( (UINT16
)mode
);
1289 /***********************************************************************
1290 * GetDOSEnvironment (KERNEL.131)
1292 SEGPTR WINAPI
GetDOSEnvironment(void)
1296 if (!(pTask
= (TDB
*)GlobalLock16( hCurrentTask
))) return 0;
1297 return (SEGPTR
)WIN16_GlobalLock16( pTask
->pdb
.environment
);
1301 /***********************************************************************
1302 * GetNumTasks (KERNEL.152)
1304 UINT16 WINAPI
GetNumTasks(void)
1310 /***********************************************************************
1311 * GetTaskDS (KERNEL.155)
1313 * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1314 * I don't think we need to bother with this.
1316 HINSTANCE16 WINAPI
GetTaskDS(void)
1320 if (!(pTask
= (TDB
*)GlobalLock16( hCurrentTask
))) return 0;
1321 return pTask
->hInstance
;
1325 /***********************************************************************
1326 * IsTask (KERNEL.320)
1328 BOOL16 WINAPI
IsTask( HTASK16 hTask
)
1332 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return FALSE
;
1333 if (GlobalSize16( hTask
) < sizeof(TDB
)) return FALSE
;
1334 return (pTask
->magic
== TDB_MAGIC
);
1338 /***********************************************************************
1339 * SetTaskSignalProc (KERNEL.38)
1341 * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1343 FARPROC16 WINAPI
SetTaskSignalProc( HTASK16 hTask
, FARPROC16 proc
)
1348 if (!hTask
) hTask
= hCurrentTask
;
1349 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return NULL
;
1350 oldProc
= (FARPROC16
)pTask
->userhandler
;
1351 pTask
->userhandler
= (USERSIGNALPROC
)proc
;
1356 /***********************************************************************
1357 * SetSigHandler (KERNEL.140)
1359 WORD WINAPI
SetSigHandler( FARPROC16 newhandler
, FARPROC16
* oldhandler
,
1360 UINT16
*oldmode
, UINT16 newmode
, UINT16 flag
)
1362 FIXME(task
,"(%p,%p,%p,%d,%d), unimplemented.\n",
1363 newhandler
,oldhandler
,oldmode
,newmode
,flag
);
1365 if (flag
!= 1) return 0;
1366 if (!newmode
) newhandler
= NULL
; /* Default handler */
1371 if (!(pTask
= (TDB
*)GlobalLock16( hCurrentTask
))) return 0;
1372 if (oldmode
) *oldmode
= pTask
->signal_flags
;
1373 pTask
->signal_flags
= newmode
;
1374 if (oldhandler
) *oldhandler
= pTask
->sighandler
;
1375 pTask
->sighandler
= newhandler
;
1381 /***********************************************************************
1382 * GlobalNotify (KERNEL.154)
1384 VOID WINAPI
GlobalNotify( FARPROC16 proc
)
1388 if (!(pTask
= (TDB
*)GlobalLock16( hCurrentTask
))) return;
1389 pTask
->discardhandler
= proc
;
1393 /***********************************************************************
1394 * GetExePtr (KERNEL.133)
1396 HMODULE16 WINAPI
GetExePtr( HANDLE16 handle
)
1402 /* Check for module handle */
1404 if (!(ptr
= GlobalLock16( handle
))) return 0;
1405 if (((NE_MODULE
*)ptr
)->magic
== IMAGE_OS2_SIGNATURE
) return handle
;
1407 /* Check the owner for module handle */
1409 owner
= FarGetOwner( handle
);
1410 if (!(ptr
= GlobalLock16( owner
))) return 0;
1411 if (((NE_MODULE
*)ptr
)->magic
== IMAGE_OS2_SIGNATURE
) return owner
;
1413 /* Search for this handle and its owner inside all tasks */
1418 TDB
*pTask
= (TDB
*)GlobalLock16( hTask
);
1419 if ((hTask
== handle
) ||
1420 (pTask
->hInstance
== handle
) ||
1421 (pTask
->hQueue
== handle
) ||
1422 (pTask
->hPDB
== handle
)) return pTask
->hModule
;
1423 if ((hTask
== owner
) ||
1424 (pTask
->hInstance
== owner
) ||
1425 (pTask
->hQueue
== owner
) ||
1426 (pTask
->hPDB
== owner
)) return pTask
->hModule
;
1427 hTask
= pTask
->hNext
;
1432 /***********************************************************************
1433 * TaskFirst (TOOLHELP.63)
1435 BOOL16 WINAPI
TaskFirst( TASKENTRY
*lpte
)
1437 lpte
->hNext
= hFirstTask
;
1438 return TaskNext( lpte
);
1442 /***********************************************************************
1443 * TaskNext (TOOLHELP.64)
1445 BOOL16 WINAPI
TaskNext( TASKENTRY
*lpte
)
1448 INSTANCEDATA
*pInstData
;
1450 TRACE(toolhelp
, "(%p): task=%04x\n", lpte
, lpte
->hNext
);
1451 if (!lpte
->hNext
) return FALSE
;
1452 pTask
= (TDB
*)GlobalLock16( lpte
->hNext
);
1453 if (!pTask
|| pTask
->magic
!= TDB_MAGIC
) return FALSE
;
1454 pInstData
= (INSTANCEDATA
*)PTR_SEG_OFF_TO_LIN( pTask
->hInstance
, 0 );
1455 lpte
->hTask
= lpte
->hNext
;
1456 lpte
->hTaskParent
= pTask
->hParent
;
1457 lpte
->hInst
= pTask
->hInstance
;
1458 lpte
->hModule
= pTask
->hModule
;
1459 lpte
->wSS
= SELECTOROF( pTask
->thdb
->cur_stack
);
1460 lpte
->wSP
= OFFSETOF( pTask
->thdb
->cur_stack
);
1461 lpte
->wStackTop
= pInstData
->stacktop
;
1462 lpte
->wStackMinimum
= pInstData
->stackmin
;
1463 lpte
->wStackBottom
= pInstData
->stackbottom
;
1464 lpte
->wcEvents
= pTask
->nEvents
;
1465 lpte
->hQueue
= pTask
->hQueue
;
1466 strncpy( lpte
->szModule
, pTask
->module_name
, 8 );
1467 lpte
->szModule
[8] = '\0';
1468 lpte
->wPSPOffset
= 0x100; /*??*/
1469 lpte
->hNext
= pTask
->hNext
;
1474 /***********************************************************************
1475 * TaskFindHandle (TOOLHELP.65)
1477 BOOL16 WINAPI
TaskFindHandle( TASKENTRY
*lpte
, HTASK16 hTask
)
1479 lpte
->hNext
= hTask
;
1480 return TaskNext( lpte
);
1484 /***********************************************************************
1485 * GetAppCompatFlags16 (KERNEL.354)
1487 DWORD WINAPI
GetAppCompatFlags16( HTASK16 hTask
)
1489 return GetAppCompatFlags32( hTask
);
1493 /***********************************************************************
1494 * GetAppCompatFlags32 (USER32.205)
1496 DWORD WINAPI
GetAppCompatFlags32( HTASK32 hTask
)
1500 if (!hTask
) hTask
= GetCurrentTask();
1501 if (!(pTask
=(TDB
*)GlobalLock16( (HTASK16
)hTask
))) return 0;
1502 if (GlobalSize16(hTask
) < sizeof(TDB
)) return 0;
1503 return pTask
->compat_flags
;