Logo Search packages:      
Sourcecode: lbzip2 version File versions

main.c

/* main.c,v 1.23 2009-03-15 20:10:19 lacos Exp */

#include <stdio.h>           /* flockfile() */
#include <string.h>          /* strerror() */
#include <sys/types.h>       /* getpid() */
#include <unistd.h>          /* getpid() */
#include <stdlib.h>          /* EXIT_FAILURE */
#include <assert.h>          /* assert() */
#include <errno.h>           /* errno */

#include "main.h"            /* pname */
#include "lbunzip2_single.h" /* lbunzip2_single() */
#include "lbunzip2.h"        /* lbunzip2() */
#include "lbzip2.h"          /* lbzip2() */


/* Public utility variables and functions. */
const char *pname;
void *(*mallocf)(size_t size);
void (*freef)(void *ptr);
void *(*lbzallocf)(void *ignored, int n, int m);
void (*lbzfreef)(void *ignored, void *ptr);


void
fail(const char *op, int err)
{
  /* Protect strerror() by locking stderr. */
  flockfile(stderr);
  (void)fprintf(stderr, "%s: %s: %s\n", pname, op, strerror(err));
  funlockfile(stderr);
  /* Stream stderr is never fully buffered originally. */
  _exit(EXIT_FAILURE);
}


void *
xalloc(size_t size)
{
  void *ret = (*mallocf)(size);

  if (0 == ret) {
    fail("(*mallocf)()", errno);
  }

  return ret;
}


void
xinit(struct cond *cond)
{
  int ret;
  pthread_mutexattr_t attr;

  ret = pthread_mutexattr_init(&attr);
  if (0 != ret) {
    fail("pthread_mutexattr_init()", ret);
  }

  ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
  if (0 != ret) {
    fail("pthread_mutexattr_settype()", ret);
  }

  ret = pthread_mutex_init(&cond->lock, &attr);
  if (0 != ret) {
    fail("pthread_mutex_init()", ret);
  }

  ret = pthread_mutexattr_destroy(&attr);
  if (0 != ret) {
    fail("pthread_mutexattr_destroy()", ret);
  }

  ret = pthread_cond_init(&cond->cond, 0);
  if (0 != ret) {
    fail("pthread_cond_init()", ret);
  }

  cond->ccount = 0lu;
  cond->wcount = 0lu;
}


void
xdestroy(struct cond *cond)
{
  int ret;

  ret = pthread_cond_destroy(&cond->cond);
  if (0 != ret) {
    fail("pthread_cond_destroy()", ret);
  }

  ret = pthread_mutex_destroy(&cond->lock);
  if (0 != ret) {
    fail("pthread_mutex_destroy()", ret);
  }
}


void
xlock(struct cond *cond)
{
  int ret;

  ret = pthread_mutex_lock(&cond->lock);
  if (0 != ret) {
    fail("pthread_mutex_lock()", ret);
  }
}


void
xlock_pred(struct cond *cond)
{
  xlock(cond);
  ++cond->ccount;
}


void
xunlock(struct cond *cond)
{
  int ret;

  ret = pthread_mutex_unlock(&cond->lock);
  if (0 != ret) {
    fail("pthread_mutex_unlock()", ret);
  }
}


void
xwait(struct cond *cond)
{
  int ret;

  ++cond->wcount;
  ret = pthread_cond_wait(&cond->cond, &cond->lock);
  if (0 != ret) {
    fail("pthread_cond_wait()", ret);
  }
  ++cond->ccount;
}


void
xsignal(struct cond *cond)
{
  int ret;

  ret = pthread_cond_signal(&cond->cond);
  if (0 != ret) {
    fail("pthread_cond_signal()", ret);
  }
}


void
xbroadcast(struct cond *cond)
{
  int ret;

  ret = pthread_cond_broadcast(&cond->cond);
  if (0 != ret) {
    fail("pthread_cond_broadcast()", ret);
  }
}


void
xcreate(pthread_t *thread, void *(*routine)(void *), void *arg)
{
  int ret;

  ret = pthread_create(thread, 0, routine, arg);
  if (0 != ret) {
    fail("pthread_create()", ret);
  }
}


void
xjoin(pthread_t thread)
{
  int ret;

  ret = pthread_join(thread, 0);
  if (0 != ret) {
    fail("pthread_join()", ret);
  }
}


/* Private stuff. */
struct opts
{
  int decompress;       /* Run in bunzip2 mode. */
  unsigned num_worker;  /* Start this many worker threads. */
  int print_cctrs;      /* Print condition variable counters in the end. */
  int trace_alloc;      /* Print allocation trace messages to stderr. */
};


static const char version[] = "0.14";

/* Backlog factor for all workers together. */
static const unsigned blf = 4u;

/* Name of environtment variable that sets the number of worker threads. */
static const char env_num_worker[] = "LBZIP2_WORKER_THREADS";

/* Name of environtment variable that sets statistics printing. */
static const char env_print_cctrs[] = "LBZIP2_PRINT_STATS";

/* Name of environtment variable that sets allocation tracing. */
static const char env_trace_alloc[] = "LBZIP2_TRACE_ALLOC";

static pid_t pid;


static long
xstrtol(const char *str, const char *source, long lower, long upper)
{
  long tmp;
  char *endptr;

  errno = 0;
  tmp = strtol(str, &endptr, 10);
  if ('\0' == *str || '\0' != *endptr || 0 != errno
      || tmp < lower || tmp > upper) {
    (void)fprintf(stderr, "%s: failed to parse \"%s\" from %s as a long in"
        " [%ld..%ld], specify \"-h\" for help\n", pname, str, source, lower,
        upper);
    _exit(EXIT_FAILURE);
  }

  return tmp;
}


static void
usage(unsigned mx_worker)
{
  (void)fprintf(stderr,
      "lbzip2: Parallel bzip2 filter.\n"
      "Copyright (C) 2008, 2009 Laszlo Ersek. Released under the GNU GPLv2+.\n"
      "Version %1$s.\n"
      "\n"
      "Usage:\n"
      "1. %2$s [-d] [-n WORKER-THREADS] [-v] [-t]\n"
      "2. %2$s -h\n"
      "\n", version, pname);

  (void)fprintf(stderr,
      "Options:\n"
      "  -d                : Decompress.\n"
      "  -n WORKER-THREADS : Set number of (de)compressor threads to\n"
      "                      WORKER-THREADS. WORKER-THREADS must be in\n"
      "                      [1, %1$u]. If this option is not specified, the\n"
      "                      environment variable %2$s is\n"
      "                      consulted. If %2$s is unset or empty,\n"
#ifdef _SC_NPROCESSORS_ONLN
      "                      %3$s queries the system for the number of\n"
      "                      online processors.\n",
#else
      "                      %3$s exits with an error.\n",
#endif
      mx_worker, env_num_worker, pname);

  (void)fprintf(stderr,
      "  -v                : Print condition variable statistics in the end.\n"
      "                      If this option is not specified, the\n"
      "                      environment variable %1$s\n"
      "                      is consulted: statistics will be printed if and\n"
      "                      only if %1$s has a non-empty value.\n",
      env_print_cctrs);

  (void)fprintf(stderr,
      "  -t                : Print memory allocation trace. If this option\n"
      "                      is not specified, the environment variable\n"
      "                      %1$s is consulted: allocation trace will be\n"
      "                      printed if and only if %1$s has a non-empty\n"
      "                      value. Check trace with \"malloc_trace.pl\".\n"
      "  -h                : Print this help and exit successfully.\n",
      env_trace_alloc);

  _exit(ferror(stderr) ? EXIT_FAILURE : EXIT_SUCCESS);
}


static int
issetenv(const char *var)
{
  char *value;

  value = getenv(var);
  return 0 != value && '\0' != value[0];
}


static void
setup_opts(struct opts *opts, int argc, char **argv)
{
  int finished;
  long mx_worker;
  int opt_num_worker;

  finished = 0;

  mx_worker = sysconf(_SC_THREAD_THREADS_MAX);
  if (-1l == mx_worker) {
    mx_worker = LONG_MAX;
  }
  if (UINT_MAX < (long unsigned)mx_worker) {
    mx_worker = UINT_MAX;
  }
  if (UINT_MAX / blf < (unsigned)mx_worker) {
    mx_worker = UINT_MAX / blf;
  }
  if ((size_t)-1 / sizeof(pthread_t) < (unsigned)mx_worker) {
    mx_worker = (size_t)-1 / sizeof(pthread_t);
  }

  opt_num_worker = 0;

  opts->decompress = 0;
  opts->print_cctrs = 0;
  opts->trace_alloc = 0;

  do {
    switch (getopt(argc, argv, ":dn:vth")) {
      case -1:
        assert(optind <= argc);
        if (optind < argc) {
          (void)fprintf(stderr, "%s: no operands allowed, specify \"-h\" for"
              " help\n", pname);
          _exit(EXIT_FAILURE);
        }
        finished = 1;
        break;

      case 'd':
        opts->decompress = 1;
        break;

      case 'n':
        opts->num_worker = xstrtol(optarg, "\"-n\"", 1l, mx_worker);
        opt_num_worker = 1;
        break;

      case 'v':
        opts->print_cctrs = 1;
        break;

      case 't':
        opts->trace_alloc = 1;
        break;

      case 'h':
        usage(mx_worker);

      case '?':
        (void)fprintf(stderr, "%s: unknown option \"-%c\", specify \"-h\" for"
            " help\n", pname, optopt);
        _exit(EXIT_FAILURE);

      case ':':
        (void)fprintf(stderr, "%s: option \"-%c\" requires an argument,"
            " specify \"-h\" for help\n", pname, optopt);
        _exit(EXIT_FAILURE);

      default:
        assert(0);
    }
  } while (!finished);

  if (isatty(opts->decompress ? STDIN_FILENO : STDOUT_FILENO)) {
    (void)fprintf(stderr, "%s: won't %s compressed data %s a terminal,"
        " specify \"-h\" for help\n", pname,
        opts->decompress ? "read" : "write", opts->decompress ? "from" : "to");
    _exit(EXIT_FAILURE);
  }

  if (!opt_num_worker) {
    char *envval_nw;

    envval_nw = getenv(env_num_worker);
    if (0 == envval_nw || '\0' == envval_nw[0]) {
#ifdef _SC_NPROCESSORS_ONLN
      long num_online;

      num_online = sysconf(_SC_NPROCESSORS_ONLN);
      if (-1 == num_online) {
        (void)fprintf(stderr, "%s: number of online processors unavailable,"
            " specify \"-h\" for help\n", pname);
        _exit(EXIT_FAILURE);
      }
      assert(1l <= num_online);
      opts->num_worker = mx_worker < num_online ? mx_worker : num_online;
#else
      (void)fprintf(stderr, "%s: WORKER-THREADS not set, specify \"-h\" for"
          " help\n", pname);
      _exit(EXIT_FAILURE);
#endif
    }
    else {
      opts->num_worker = xstrtol(envval_nw, env_num_worker, 1l,
          mx_worker);
    }
  }

  if (!opts->print_cctrs) {
    opts->print_cctrs = issetenv(env_print_cctrs);
  }

  if (!opts->trace_alloc) {
    opts->trace_alloc = issetenv(env_trace_alloc);
  }
}


static void *
trace_malloc(size_t size)
{
  void *ret;
  int save_errno;

  ret = malloc(size);
  if (0 == ret) {
    save_errno = errno;
  }

  if (0 > fprintf(stderr, "%lu: malloc(%lu) == %p\n", (long unsigned)pid,
      (long unsigned)size, ret)
  ) {
    _exit(EXIT_FAILURE);
  }

  if (0 == ret) {
    errno = save_errno;
  }

  return ret;
}


static void
trace_free(void *ptr)
{
  if (0 > fprintf(stderr, "%lu: free(%p)\n", (long unsigned)pid, ptr)) {
    _exit(EXIT_FAILURE);
  }
  free(ptr);
}


static void *
trace_bzalloc(void *ignored, int n, int m)
{
  assert(n >= 0 && m >= 0);
  if (n > 0) {
    assert((size_t)-1 / (size_t)n >= (size_t)m);
  }
  else {
    if (m > 0) {
      assert((size_t)-1 / (size_t)m >= (size_t)n);
    }
  }

  return trace_malloc((size_t)n * (size_t)m);
}


static void
trace_bzfree(void *ignored, void *ptr)
{
  trace_free(ptr);
}


int
main(int argc, char **argv)
{
  struct opts opts;
  unsigned num_slot;

  pname = strrchr(argv[0], '/');
  pname = pname ? pname + 1 : argv[0];

  setup_opts(&opts, argc, argv);
  assert(UINT_MAX / blf >= opts.num_worker);
  num_slot = opts.num_worker * blf;

  if (opts.trace_alloc) {
    mallocf = trace_malloc;
    freef = trace_free;
    lbzallocf = trace_bzalloc;
    lbzfreef = trace_bzfree;
    pid = getpid();
  }
  else {
    mallocf = malloc;
    freef = free;
    lbzallocf = 0;
    lbzfreef = 0;
  }

  if (opts.decompress) {
    if (1u == opts.num_worker) {
      lbunzip2_single(num_slot, opts.print_cctrs);
    }
    else {
      lbunzip2(opts.num_worker, num_slot, opts.print_cctrs);
    }
  }
  else {
    lbzip2(opts.num_worker, num_slot, opts.print_cctrs);
  }

  _exit(EXIT_SUCCESS);
}

Generated by  Doxygen 1.6.0   Back to index