[Previous]
[Contents]
[Next]

tfork()

create a new thread of execution

Synopsis:

#include <process.h>

pid_t tfork( char *stk_addr, 
             unsigned stk_size,
             int (*func)(void *),
             void *arg,
             int flags );

Description:

The tfork() (thread fork) function creates a new thread of execution within the same address space as the calling process.


Note: You should call _beginthread() instead of using tfork() directly:
  • _beginthread() initializes thread support in the Watcom C Library.
  • tfork() works only with QNX 4.23 or later, and won't work with the future Neutrino version of QNX.

tfork() is similar to the fork() function, in that a new process is created, and it follows the same rules as described by fork(). However, where the fork() function shares code and copies data and stack, the tfork() function shares both the code and data, and the child gets a new stack. A pointer to the bottom of the new stack and its size are passed as parameters to tfork().

The parent returns from the tfork() call, while the child begins execution at the function that was passed as the func argument to tfork(). A flags argument is provided to modify the behavior of tfork(). There are currently no flags defined, so this argument should always be zero.

Those familiar with threads in other systems will notice that the arguments to tfork() are very similar to a typical thread creation function. However, where these systems usually create an entity within an existing process (that is, a thread) with a family of functions to operate on the thread (set a signal, wait on death, destroy, and so on), the tfork() function creates a new process. As such, the normal process primitives can be used (for example, kill(), wait(), exit()) to achieve similar goals.

Since tfork() creates a process, and not a thread, the following characteristics should be noted:


Note: The QNX libraries aren't completely thread-safe. Before calling a function in a thread, check its Classification section to make sure it's safe to do so.

Be careful whenever a library function may be invoked simultaneously by processes that share data as a result of a tfork(). As a general rule, if a function isn't signal-safe or if it manipulates static data (globals) then it isn't reentrant, and entry must be serialized (that is, both processes must run sequentially).


To protect functions, you can use the semaphore functions (see sem_... functions) or run with SCHED_FIFO (see sched_... functions) with a common priority for functions that don't block.

Returns:

Upon successful completion, the tfork() function returns to the parent the process ID of the child. The child starts execution at func with a single argument arg. If an error occurs, -1 is returned and errno is set.

Errors:

EAGAIN
Insufficient resources were available to create the child process.
EINVAL
Invalid stack provided as a parameter.
ENOMEM
The process would require more space than the system is able to supply.

Examples:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#include <process.h>
#include <sys/kernel.h>

sem_t s;
char stack[3][2000];

void
thread(void *arg) {
    for(;;) {
        sem_wait(&s);
        printf("%d", *(int*)arg); fflush(stdout);
        yield_to_others();
        sem_post(&s);
    }
}

int main( void )
  {
    int rc, args[] = { 1, 2, 3 };  // else compiler warnings :-)

    rc = sem_init(&s, 1, 1);
    if(rc == -1) {
        fprintf(stderr, "sem_init: %s\n", strerror(errno));
        exit(1);
    }

    tfork(&stack[0], sizeof(stack[0]), &thread, &args[0], 0);
    tfork(&stack[1], sizeof(stack[1]), &thread, &args[1], 0);
    tfork(&stack[2], sizeof(stack[2]), &thread, &args[2], 0);

    for(;;) {
        sem_wait(&s);
        printf("0"); fflush(stdout);
        yield_to_others();
        sem_post(&s);
    }
    return( EXIT_SUCCESS );
}

yield_to_others() {      // give other processes a chance to run
    volatile long l;     // (all others should be
                         //  blocked on the semaphore)

    for(l = 1000 ; l ; --l)
        Yield();
}

Classification:

QNX

Safety:
Interrupt handler No
Signal handler Yes
Thread Yes

See also:

_beginthread(), dup(), errno, fork(), sem_destroy(), sem_init(), sem_post(), sem_trywait(), sem_wait(), sched_getparam(), sched_getscheduler(), sched_setparam(), sched_setscheduler(), sched_yield(), Yield()


[Previous]
[Contents]
[Next]