pthread_atfork() - Register fork handlers

Standards

Standards / Extensions C or C++ Dependencies

Single UNIX Specification, Version 3

both

z/OS V1R9
POSIX(ON)

Format

#define _UNIX03_THREADS
#include <pthread.h>

int pthread_atfork(void (*prepare)(void), void (*parent)(void),
void (*child)(void));

General description

The pthread_atfork() function registers fork handlers to be called before and after fork(), in the context of the thread that called fork(). Fork handler functions may be named for execution at the following three points in thread processing:

If any argument to pthread_atfork() is NULL, the call does not register a handler to be invoked at the point corresponding to that argument.

The order of calls to pthread_atfork() is significant. The parent and child fork handlers are called in the order in which they were established by calls to pthread_atfork(). The prepare fork handlers are called in the opposite order.

The intended purpose of pthread_atfork() is to provide a mechanism for maintaining the consistency of mutex locks between parent and child processes. The handlers are expected to be straightforward programs, designed simply to manage the synchronization variables and must return to ensure that all registered handlers are called. Historically, the prepare handler acquired needed mutex locks, and the parent and child handlers released them. Unfortunately, this usage is not practical on the z/OS® platform and is not guaranteed to be portable in the current UNIX standards. When the parent process is multi-threaded (invoked pthread_create() at least once), the child process can only safely call async-signal-safe functions before it invokes an exec() family function. This restriction was added to the POSIX.1 standard in 1996. Because functions such as pthread_mutex_lock() and pthread_mutex_unlock() are not async-signal-safe, unpredictable results may occur if they are used in a child handler.

Special behavior for z/OS XL C: The C Library pthread_atfork() function has the following restrictions:
  • Any fork handler registered by a fetched module that has been released is removed from the list at the time of release. See fetch() — Get a load module,fetchep() — Share writable static, and release() — Delete a load module for details about fetching and releasing modules.
  • Any handler registered in an explicitly loaded DLL (using dllload() or dlopen()) that has been freed (using dllfree() or dlclose()) is removed from the list, except when the DLL has also been implicitly loaded.
  • Use of non-C subroutines or functions as fork handlers will result in undefined behavior.
Special behavior for z/OS XL C++:
  • All of the behaviors listed under "Special Behavior for z/OS XL C."
  • The pthread_atfork() function cannot receive C++ function pointers compiled as 31-bit non-XPLINK objects, because C and C++ linkage conventions are incompatible in that environment. If you attempt to pass a C++ function pointer to pthread_atfork(), the compiler will flag it as an error. In C++, you must ensure that all handlers registered for use by pthread_atfork() have C linkage by declaring them as extern "C".

Returned value

If successful, pthread_atfork() returns zero; otherwise, it returns an error number.

Error Code
Description
ENOMEM
Insufficient table space exists to record the fork handler addresses.

Example

CELEBP60

⁄* CELEBP60 *⁄                                   
⁄* Example using SUSv3 pthread_atfork() interface *⁄ 

#define _UNIX03_THREADS 1                                                       

#include <pthread.h>                                                            
#include <stdio.h>                                                              
#include <unistd.h>   
#include <fcntl.h>   
#include <sys⁄types.h>
#include <sys⁄wait.h>
#include <stdlib.h>
#include <errno.h>                                                              
                                                                                
char fn_c[] = "childq.out";
char fn_p[] = "parentside.out";
int  fd_c;
int  fd_p;

void prep1(void)  {
  char buff[80] = "prep1\n";
  write(4,buff,sizeof(buff));
}

void prep2(void)  {
  char buff[80] = "prep2\n";
  write(4,buff,sizeof(buff));
}

void prep3(void)  {
  char buff[80] = "prep3\n";
  write(4,buff,sizeof(buff));
}

                                                                               
void parent1(void)  {
  char buff[80] = "parent1\n";
  write(4,buff,sizeof(buff));
}

void parent2(void)  {
  char buff[80] = "parent2\n";
  write(4,buff,sizeof(buff));
}

void parent3(void)  {
  char buff[80] = "parent3\n";
  write(4,buff,sizeof(buff));
}


void child1(void)  {
  char buff[80] = "child1\n";
  write(3,buff,sizeof(buff));
}

void child2(void)  {
  char buff[80] = "child2\n";
  write(3,buff,sizeof(buff));
}

void child3(void)  {
  char buff[80] = "child3\n";
  write(3,buff,sizeof(buff));
}

void *thread1(void *arg) {
                                                                                
  printf("Thread1: Hello from the thread.\n");

} 
                                                                                

int main(void)
{                                                                        
  pthread_t thid;
  int       rc, ret;
  pid_t     pid;
  int       status;
  char   header[30] = "Called Child Handlers\n";


  if (pthread_create(&thid, NULL, thread1, NULL) != 0) {
    perror("pthread_create() error");                                           
    exit(3);                                                                    
  }

  if (pthread_join(thid, NULL) != 0) {
    perror("pthread_join() error");
    exit(5);                                                                    
  } else {
    printf("IPT: pthread_join success!  Thread 1 should be finished now.\n");
    printf("IPT: Prepare to fork!!!\n");
  }
 

  ⁄*-----------------------------------------*⁄
  ⁄*|  Start atfork handler calls in parent  *⁄
  ⁄*-----------------------------------------*⁄
  ⁄* Register call 1 *⁄
  rc = pthread_atfork(&prep1, &parent2, &child3);
  if (rc != 0) {
     perror("IPT: pthread_atfork() error [Call #1]"); 
     printf("  rc= %d, errno: %d, ejr: %08x\n", rc, errno, __errno2()); 
  }
 

  ⁄* Register call 2 *⁄
  rc = pthread_atfork(&prep2, &parent3, &child1);
  if (rc != 0) {
     perror("IPT: pthread_atfork() error [Call #2]"); 
     printf("  rc= %d, errno: %d, ejr: %08x\n", rc, errno, __errno2()); 
  }
  

  ⁄* Register call 3 *⁄
  rc = pthread_atfork(&prep3, &parent1, NULL);
  if (rc != 0) {
     perror("IPT: pthread_atfork() error [Call #3]"); 
     printf("  rc= %d, errno: %d, ejr: %08x\n", rc, errno, __errno2()); 
  }
 
  ⁄* Create output files to expose the execution of fork handlers. *⁄
  if ((fd_c = creat(fn_c, S_IWUSR)) < 0)
    perror("creat() error");
  else
    printf("Created %s and assigned fd= %d\n", fn_c, fd_c);
  if ((ret = write(fd_c,header,30)) == -1)
    perror("write() error");
  else
    printf("Write() wrote %d bytes in %s\n", ret, fn_c);

  if ((fd_p = creat(fn_p, S_IWUSR)) < 0)
    perror("creat() error");
  else
    printf("Created %s and assigned fd= %d\n", fn_p, fd_p);
  if ((ret = write(fd_p,header,30)) == -1)
    perror("write() error");
  else
    printf("Write() wrote %d bytes in %s\n", ret, fn_p);

  pid = fork();

  if (pid < 0) 
    perror("IPT: fork() error"); 
  else {
    if (pid == 0) {
      printf("Child: I am the child!\n");
      printf("Child: My PID= %d, parent= %d\n", (int)getpid(), 
              (int)getppid());
      exit(0);

    } else {
      printf("Parent: I am the parent!\n");
      printf("Parent: My PID= %d, child PID= %d\n", (int)getpid(), (int)pid);

      if (wait(&status) == -1)  
        perror("Parent: wait() error");
      else if (WIFEXITED(status))
             printf("Child exited with status: %d\n",WEXITSTATUS(status)); 
           else
             printf("Child did not exit successfully\n");

    close(fd_c);
    close(fd_p);

    }

  }


}

Related information