Showing posts with label c. Show all posts
Showing posts with label c. Show all posts

Saturday, January 29, 2011

Parallel and Bar

I recently discovered two UNIX command line tools that are really useful. For both tools, I had simple solutions that I've been using for a long time to provide similar functionality. The first tool is GNU Parallel. As the name implies, this tool provides a way to run many commands in parallel. Whats more is that it works in a manner that is consistent with xargs. To get an idea of all the cool things you can do with this tool, I highly recommend looking through the examples in the man page.

Before finding GNU Parallel, I used a simple shell script to accomplish similar tasks. Of course, my version is much more limited in what it can do, it reads one command per line from stdin and runs it in the background. The number of parallel jobs is controlled by using the bash jobs builtin to determine how many tasks are already running. The complete script is shown below.
#!/bin/bash

# Print usage
function usage {
    local prg=`basename $0`
    cat <<USAGE

Usage: $prg [options]

    Options:
    -h                   Print this help message
    -v                   Be verbose with log messages
    -n <tasks>           The number of tasks to run at a time.

USAGE
    exit 1
}

# Check arguments
numProcs=4
verbose="false"
while getopts "n:vh" option; do
    case $option in
        (n) numProcs="$OPTARG" ;;
        (v) verbose="true" ;;
        (h) usage ;;
    esac
done
shift $(($OPTIND-1))

# Read commands from stdin
while read line; do

    # Can we run another?
    while [ "$(jobs -r | wc -l)" -ge "$numProcs" ]; do
        sleep 1
    done

    # Run task
    sh -c "$line" &
done

# Wait for jobs to finish
wait
The second command is the command line progress bar. This command writes progress information to stderr while copying data from stdin to stdout. If you ever need to copy a big file and want some basic stats so you can see that things are still working, this is the tool for you. The output and options are certainly much nicer than the crude C program I was using:
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <unistd.h>

#define PAGESIZE 4096
#define STDIN    0
#define STDOUT   1

int
main() {
    char buffer[PAGESIZE];
    ssize_t length;
    int counter;
    uint64_t bytesWritten = 0;

    while ((length = read(STDIN, buffer, PAGESIZE)) > 0) {
        write(STDOUT, buffer, length);
        bytesWritten += length;
        ++counter;
        if (counter % 10 == 0) {
            fprintf(stderr, "%"PRIu64" bytes\r", bytesWritten);
        }
    }
    fprintf(stderr, "%"PRIu64" bytes\n", bytesWritten);

    return EXIT_SUCCESS;
}

Saturday, October 9, 2010

Damn Java Socket Exception Messages

When creating an error message you should think about what information would be useful for understanding what went wrong. This should especially be true if you are creating a library that is likely to be used by many other systems. Providing good error messages up front means that even if the programmers using the library do not check and customize the messages, the user will still get a reasonable result. For some use cases, such as scripting, it can also be useful because it may be a quick one-off program where the goal is to quickly automate a repetitive task or perform some analysis. In this case, having useful default error messages can speed up the initial development so you get your answer faster.

In my case, I was checking logs for a system that crawls pages and hence attempts to resolve and connect to thousands of hosts. This system logs the exceptions, but unfortunately did not provide a customized message when doing so. Analyzing the logs showed that the two most common error messages were: 1) a failure to resolve the hostname and 2) failing to connect to an HTTP server on the host. An example error message for the first case is java.net.UnknownHostException: some-host-that-does-not-exist. This message is quite useful as the exception name explains the problem and the message tells me the name of the host that could not be resolved. An example message for the second case is java.net.SocketTimeoutException: connect timed out. This message is explains the problem, but doesn't given me the crucial information of what it was trying to connect to.

Though this can easily be fixed in the application code, it is disappointing that the default message is so bad. I have noticed that my opinion of a programming language or technology seems to go down steadily the longer I am forced to use it at work. Is the grass greener on one of the other sides? How do other languages, or rather the networking libraries they provide, fair for this use case? I looked at 13 options to see how many would give a decent error message for both use cases. The results were not very encouraging. Only one option, Go, had reasonable messages for both. For the host not found case 4 options included the hostname. Only two options provided the host and port in the failed to connect case. The results are summarized in the table below the fold with links to the source code and raw error messages.

Sunday, July 25, 2010

Forking File Descriptors

When create a new process with a call to fork, one thing the child process will retain is a copy of the parent's open file descriptors:
The child process shall have its own copy of the parent's file descriptors. Each of the child's file descriptors shall refer to the same open file description with the corresponding file descriptor of the parent.
This can be useful, for example, it enables IPC by remapping the standard input, output, and error descriptors to pipes that were created before the fork and then using exec to replace the child image with a new command. However, this behavior can also be an annoyance if one is not careful. An example I hit recently was that the application was occasionally hanging when it shouldn't be. Poking at the system it was blocked on a read to a file descriptor that should have been closed. Debugging further the file descriptor for writing to the pipe was indeed closed in the parent process but, a fork and exec had occurred before it was closed in a different part of the code and the child process still had an open handle to write to that pipe. This failure was of course sporadic and difficult to reproduce because it only occurred if the timing was just right.

Once the problem was diagnosed though, it seemed like it should be a trivial problem to fix. We just need to make sure we close out all of the file descriptors we aren't interested in. In the past I did this using a closefrom system call. This call does not seem to be widely supported though. The naive way to do this is to simply close everything in the range of possible values for file descriptors. On POSIX systems the range can be determined via the _SC_OPEN_MAX setting:

int
closefrom_v1(int fd) {
    int max = sysconf(_SC_OPEN_MAX);
    for (int i = fd; i <= max; ++i) {
        close(i);
    }
    return 0;
}
This method is simple and it basically works, but it bothers me that it is closing lots of file descriptors that aren't open. More importantly though, with this implementation we cannot determine if the close of some descriptors failed. So here is an improved version that checks the error codes and returns -1 on failure and the number of closed descriptors on success:
int
closefrom_v2(int fd) {
    int count = 0;
    int max = sysconf(_SC_OPEN_MAX);
    for (int i = fd; i <= max; ++i) {
        if (close(i) == -1) {
            if (errno != EBADF) {
                return -1;
            }
        } else {
            ++count;
        }
    }
    return count;
}
Notice that we are still closing file descriptors that are not open. I'm not aware of a simple and portable scheme for listing the set of open descriptors for a process so I'll live with the annoyance for now. To make sure it works as expected we can create a simple test program that opens some files, forks a child, has the child use closefrom, and then exec's a process that will list out the file descriptors that are open. Here is the example:
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>

int
closefrom_v1(int fd) {
    int max = sysconf(_SC_OPEN_MAX);
    for (int i = fd; i <= max; ++i) {
        close(i);
    }
    return 0;
}

int
closefrom_v2(int fd) {
    int count = 0;
    int max = sysconf(_SC_OPEN_MAX);
    for (int i = fd; i <= max; ++i) {
        if (close(i) == -1) {
            if (errno != EBADF) {
                return -1;
            }
        } else {
            ++count;
        }
    }
    return count;
}

int
main(int argc, char **argv) {
    if (argc != 2) {
        printf("Usage: %s <cmd>\n", argv[0]);
        exit(1);
    }

    // Open a bunch of files so the child will have something to close
    int numFDs = 42;
    for (int i = 0; i < numFDs; ++i) {
        FILE *f = fopen("/dev/null", "w");
        if (f == NULL) {
            perror("open failed");
            exit(1);
        }
        assert(f != NULL);
    }
    printf("opened %d files\n", numFDs);

    // Fork a child and exec
    pid_t pid = fork();
    if (pid == -1) {
        perror("could not fork");
    } else if (pid == 0) {
        printf("closed %d files\n", closefrom_v2(3));
        if (execlp(argv[1], (char *) 0) == -1) {
            perror("could not exec");
        }
    } else {
        int status;
        waitpid(pid, &status, 0);
        printf("child %d exited with status %d\n", pid, status);
    }
    return 0;
}
For the program to exec, I'll use a simple shell script that lists the contents of the procfs directory /proc/PID/fd:
#!/bin/sh
ls /proc/$$/fd
Note that using procfs to get the listing of filed descriptors is not portable and may not work for you. If you need a more general scheme looking at what lsof is probably a good starting point. Back to the main topic, if the script works you should get output like:
$ ./testclose listfds.sh
opened 42 files
closed 42 files
0  1  2  255
child 31588 exited with status 0
Note file descriptor 255 shown in the listing is added by the shell that is executing the script. It was not open until the after the exec so it was not closed.