Back to libut API Reference


SYNOPSIS

    #include "libut/ut.h"
    pid_t UT_fork_coprocess( char *name, UT_coproc *fcn, void *data, UT_exithdlr *exhdlr, int fds[2] );


DESCRIPTION

Overview

This function forks a new process to execute the specified function. The new process exits when that function returns. The original (parent) process and new (child) process can communicate with each other via pipes. The child process will be collected after it has exited, and the parent can choose to be notified via a callback when that occurs.

This function provides a convenient mechanism to conduct lengthy or blocking operations. If the parent conducted such operations itself, it would not be able to remain responsive to other activity in the event loop (e.g., timer expirations, network I/O including the control port, etc.)

By forking child processes to conduct lengthy or blocking operations, the parent remains responsive to its own event loop. Conversely, each child process has only its task to do-- the libut event loop does not run in the child process. (Only certain non-event-driven libut API functions may be used by the child. These are listed below in the CHILD-SAFE LIBUT API FUNCTIONS section.)

The child process is of course free to exec(3) into a different program completely.

Parameters

This function's arguments include a descriptive name (which will be copied and silently truncated if too long). The name is used only in the output of the cops and fds control port command and in log messages.

Function to execute in the child process

The fcn argument is a pointer to the function to execute in the child process. It must have this prototype:

    int (UT_coproc)(char *name, void *data);

The name and opaque data arguments are passed through to it. The child process will exit when this function returns, and the integer it returns becomes the process exit status.

The exit handler

The exhdlr argument specifies a callback which will be invoked in the parent process when the child has exited. It may be NULL if the parent does not require notification. (Libut takes care of collecting the child process). The callback specified by exhdlr (if non-NULL) must have this prototype:

    int (UT_exithdlr)(pid_t pid, int status, char *name, void *data);

The exhdlr receives the pid and status of the exited child, as well as the name and opaque data specified when it was created. The status integer can be interpreted using the macros WIFEXITED, WEXITSTATUS, WIFSIGNALED and WTERMSIG documented with the wait(2) system call.

Pipe communication between parent and child

If the fds argument is non-NULL, it must point to an array of two integers; these are output parameters. UT_fork_coprocess(3) populates them with two file descriptors: the first may be used to write to the child's standard input and the second may be used to read from the child's standard output. Their use is optional, and their closure is automatically handled by libut after the child exits.

Child logging feature

Since UT_LOG(3) should not be used by the child process, it has its own simple logging mechanism: anything the child writes to its standard error will be written to the parent's log.

For example, if the child process (whose name is dbworker) calls:

    fprintf(stderr, "Connecting to database\n");

a log entry would be created in the parent like this:

    Oct 26 12:48:09 Info (coproc.c:110) dbworker: [Connecting to database]

Note that when the parent reads text to be logged from the child it may not always get a full line (ending with a newline, \n) at a time. So, single lines can get split up into multiple log entries; this is indicated by an asterisk:

    Oct 26 12:48:09 Info (coproc.c:110) dbworker: [Connecting ]*
    Oct 26 12:48:09 Info (coproc.c:110) dbworker: [to database]


RETURN VALUE

The process ID of the newly-created process is returned upon success. On error, -1 is returned and a message is printed to the log.


CHILD-SAFE LIBUT API FUNCTIONS

These libut API functions may be used by the child process:

UT_mem_alloc(3), UT_mem_free(3) UT_iob_create(3), UT_iob_append(3), UT_iob_printf(3), UT_iob_flatten(3), UT_iob_bufcmp(3), UT_iob_free(3), UT_iob_len(3) UT_stridx(3), UT_strncpy(3) UT_var_get(3)

All other libut API functions are not supported for child processes, since the libut event loop does not run in child processes.


EXAMPLE

    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdio.h>
    #include "libut/ut.h"
    
    /* This function runs in the child process, which exits upon return */
    int childFcn( char*name, void*data ) {
        fprintf(stderr,"this goes to parent's log\n");
        /* could read stdin (from parent) or write stdout (to parent) */
        return 0;  /* becomes exit status */
    }
    
    /* This is the exit handler. It runs in the parent when child exits */
    int exHdlr(pid_t pid, int sts, char*name, void*data) {
        if (WIFSIGNALED(sts)) 
           UT_LOG(Info,"Pid %d killed by signal %d", pid, WTERMSIG(sts));
        else if (WIFEXITED(sts)) 
           UT_LOG(Info,"Pid %d exited with status %d", pid, WEXITSTATUS(sts));
    }
    
    int main() {
        UT_init(INIT_END);
        UT_fork_coprocess("test", childFcn, "testdata", exHdlr, NULL);
        UT_event_loop();
    }


RELATED CONTROL PORT COMMANDS

The cops control port command lists the currently-executing coprocesses.


AUTHOR

Troy D. Hanson <thanson@users.sourceforge.net>