create a new thread of execution
#include <process.h> pid_t tfork( char *stk_addr, unsigned stk_size, int (*func)(void *), void *arg, int flags );
The tfork() (thread fork) function creates a new thread of execution within the same address space as the calling process.
You should call
_beginthread()
instead of using tfork() directly:
|
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:
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.
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.
#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(); }
QNX
Safety: | |
---|---|
Interrupt handler | No |
Signal handler | Yes |
Thread | Yes |
_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()