DosCreateThread (OS/2 1.x)

From EDM2
Jump to: navigation, search

This call creates an asynchronous thread of execution under the current process.

Syntax

DosCreateThread (PgmAddress, ThreadIDWord, NewThreadStack)

Parameters

PgmAddress (PFNTHREAD) - input 
Address within program module where new thread begins execution. This address must not be in an IOPL segment.
ThreadIDWord (PTID) - output 
Address of thread ID of the new thread.
NewThreadStack (PBYTE) - input 
Address of the new thread's stack.

Return Code

rc (USHORT) - return
Return code descriptions are:
  • 0 NO_ERROR
  • 8 ERROR_NOT_ENOUGH_MEMORY
  • 89 ERROR_NO_PROC_SLOTS
  • 212 ERROR_LOCKED

Remarks

OS/2 creates the first thread of a process when it starts the executable file. This thread is dispatched with a regular class priority. To start another thread of execution under the current process, the current thread allocates stack memory and issues DosCreateThread. Upon generation of the far call, the thread's initial dispatch point is the address specified for PgmAddress. The started thread has a unique stack and register context and the same priority as the requesting thread.

Note: The minimum available space on the stack for a thread calling an operating system function must be 4K bytes.

A thread's stack, register context, and priority is the only information maintained by OS/2 that is specific to the thread. The thread shares resources with other threads of the process. Any thread in the process can open a file or device, and any other thread can issue a read or write to that handle. This is also true for pipes, queues, and system semaphores.

The address passed as the NewThreadStack value must be the address of the highest byte in the stack. This value is loaded into the SS:PP registers before starting the new thread.

A thread started with DosCreateThread terminates upon return of this call or when a DosExit is issued. Any thread can temporarily stop the execution of other threads in its process with DosSuspendThread, DosResumeThread, DosEnterCritSec, and DosExitCritSec calls. Any thread can also examine and change the priority at which it and other threads execute with DosGetPrty and DosSetPrty.

Note: DosCreateThread cannot be issued from within a segment that has I/O privilege (IOPL). If the new thread entry point is in an IOPL code segment, a general protection fault is generated, and the process is terminated.

All code segments execute at a privilege level. Segments for OS/2 applications usually execute at privilege level 3. However, if an application has an IOPL code segment that is executing at privilege level 2 and has to start another thread of execution, DosCallback can be issued from the IOPL segment to invoke a privilege level 3 segment. But before the DosCreateThread request is made, the IOPL segment's stack must be resized in the privilege level 3 segment by a call to DosR2StackRealloc. For more information on IOPL code segments, see IBM Operating System/2 Version 1.2 I/O Subsystems And Device Support Volume 1.

Bindings

C

#define INCL_DOSPROCESS

USHORT  rc = DosCreateThread(PgmAddress, ThreadIDWord, NewThreadStack);

PFNTHREAD  PgmAddress;     /* Program address */
PTID       ThreadIDWord;   /* New thread ID (returned) */
PBYTE      NewThreadStack; /* End of stack for new thread */

USHORT     rc;             /* return code */

MASM

EXTRN  DosCreateThread:FAR
INCL_DOSPROCESS     EQU 1

PUSH   DWORD   PgmAddress     ;Program address
PUSH@  WORD    ThreadIDWord   ;New thread ID (returned)
PUSH@  OTHER   NewThreadStack ;End of stack for new thread
CALL   DosCreateThread

Returns WORD

Example

In this example, a second thread is started at TestRoutine with a stack size of 4096 bytes. Remember to compile with Stack checking disabled (-Gs). Also, threads started with DosCreateThread should not use some C library functions. See chapter 6 of the IBM C/2 Language Reference (version 1.1) for a discussion of threads and the C functions _beginthread and _endthread. This example can be compiled as follows:

cl -YMS -Gs example.c
#define INCL_DOSPROCESS
#define INCL_VIO
#define SLEEP_THREAD1 5000L
#define SLEEP_THREAD2 1000L
#define VIO_HANDLE 0
#define RETURN_CODE 0

TID    ThreadID;
BYTE   ThreadStackArea[4096];
USHORT rc;

VOID APIENTRY TestRoutine()
   {
   USHORT r;

   r = DosSleep(SLEEP_THREAD2);        /* Interval size */
   r = VioWrtTTY("...Thread2...",      /* String to be written */
                 14,                   /* Length of string */
                 VIO_HANDLE);          /* Video handle */
   DosExit(EXIT_THREAD,                /* Indicates end thread of process */
           RETURN_CODE);               /* Result code */
   }

main()
   {
   rc = DosCreateThread((PFNTHREAD) TestRoutine, /* Program address */
                        &ThreadID,               /* New thread ID */
                        &ThreadStackArea[4095]); /* End of stack for new thread */
   rc = DosSleep(SLEEP_THREAD2);       /* Interval size */
   printf("...Thread1...\n");
   }

The following example shows how to suspend and resume execution of a thread within a process. The main thread creates Thread2 and allows it to begin executing. Thread2 iterates through a loop that prints a line and then sleeps, relinquishing its time slice to the main thread. After one iteration by Thread2, the main thread suspends Thread2 and then resumes it. Subsequently, Thread2 completes the remaining three iterations.

#define INCL_DOSPROCESS

#include <os2.h>

#define   SEGSIZE       4000   /* Number of bytes requested in segment */
#define   ALLOCFLAGS    0      /* Segment allocation flags - no sharing */
#define   SLEEPSHORT    5L     /* Sleep interval - 5 milliseconds */
#define   SLEEPLONG     75L    /* Sleep interval - 75 milliseconds */
#define   RETURN_CODE   0      /* Return code for DosExit() */

VOID APIENTRY Thread2()
{
  USHORT     i;

  /* Loop with four iterations */
  for(i=1; i<5; i++)
  {
    printf("In Thread2, i is now %d\n", i);
    /* Sleep to relinquish time slice to main thread */
    DosSleep(SLEEPSHORT);          /* Sleep interval */
  }
  DosExit(EXIT_THREAD,             /* Action code - end a thread */
          RETURN_CODE);            /* Return code */
}

main()
{
  TID        ThreadID;             /* Thread identification */
  SEL        ThreadStackSel;       /* Segment selector for thread stack */
  PBYTE      StackEnd;             /* Ptr. to end of thread stack */
  USHORT     rc;

  /** Allocate segment for thread stack; make pointer to **/
  /**  end of stack. **/
  /** We must allocate a segment in order to preserve **/
  /**  segment protection for the thread. **/

  rc = DosAllocSeg(SEGSIZE,             /* Number of bytes requested */
                   &ThreadStackSel,     /* Segment selector (returned) */
                   ALLOCFLAGS);         /* Allocation flags - no sharing */
  StackEnd = MAKEP(ThreadStackSel, SEGSIZE-1);

  /** Start Thread2 **/
  if(!(rc=DosCreateThread((PFNTHREAD) Thread2,    /* Thread address */
                       &ThreadID,                 /* Thread ID (returned) */
                       StackEnd)))   /* End of thread stack */
    printf("Thread2 created.\n");

  /* Sleep to relinquish time slice to Thread2 */
  if(!(DosSleep(SLEEPSHORT)))                  /* Sleep interval */
    printf("Slept a little to let Thread2 execute.\n");

  /***** Suspend Thread2, do some work, then resume Thread2 *****/
  if(!(rc=DosSuspendThread(ThreadID)))         /* Thread ID */
    printf("Thread2 SUSPENDED.\n");
  printf("Perform work that will not be interrupted by Thread2.\n");
  if(!(rc=DosResumeThread(ThreadID)))          /* Thread ID */
    printf("Thread2 RESUMED.\n");
  printf("Now we may be interrupted by Thread2.\n");

  /* Sleep to allow Thread2 to complete */
  DosSleep(SLEEPLONG);                         /* Sleep interval */
}