--threads option and stream writing progress
authorEric Biggers <ebiggers3@gmail.com>
Sun, 18 Nov 2012 18:18:46 +0000 (12:18 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 18 Nov 2012 18:18:46 +0000 (12:18 -0600)
doc/imagex-capture.1.in
doc/imagex-export.1.in
programs/imagex.c
src/dentry.c
src/modify.c
src/mount.c
src/wimlib.h
src/write.c

index 7365aa8d1bad18c49a1602b5293ec3f8599ab5f6..c29cf6059e257ae44f773b97c8bf7257d2c8cef4 100644 (file)
@@ -113,6 +113,12 @@ must be the same as that of the existing WIM.  \fITYPE\fR may be "none",
 You may also specify the actual names of the compression algorithms, "XPRESS"
 and "LZX", instead of "fast" and "maximum", respectively.
 .TP
+\fB--threads\fR=\fINUM_THREADS\fR
+Number of threads to use for compressing data.  Default: autodetect (number of
+processors).  Note: if creating or appending to an uncompressed WIM, additional
+threads will not be used, regardless of this parameter, since no compression
+needs to be done in this case.
+.TP
 \fB--flags\fR=\fIEDITIONID\fR
 Specify a string to use in the <FLAGS> element of the XML data for the new
 image.
index 164b1d01d2c33c029a7822d2aa8318b54eee6d5b..8ed3262e5c2eb47c6e813b329b805609f1e7f90b 100644 (file)
@@ -60,7 +60,13 @@ that of the input WIM file.
 
 You may also specify the actual names of the compression algorithms, "XPRESS"
 and "LZX", instead of "fast" and "maximum", respectively.
-
+.TP
+\fB--threads\fR=\fINUM_THREADS\fR
+Number of threads to use for compressing data.  Default: autodetect (number of
+processors).  Note: if exporting to an uncompressed WIM, or exporting to a WIM
+with the same compression type as the source WIM, additional threads will not
+be used, regardless of this parameter, since no data compression needs to be
+done in these cases.
 .TP
 \fB--ref\fR="\fIGLOB\fR"
 File glob of additional split WIM parts that are part of the split WIM being
index 6e78296f12d0e1de59762d2711d5630456b56f6b..2ba1a076795e326cbad4ceaa6103937e9b211c55 100644 (file)
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <errno.h>
 #include <libgen.h>
+#include <limits.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -62,7 +63,8 @@ static const char *usage_strings[] = {
 [APPEND] =
 "imagex append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
 "                     [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n"
-"                     [--verbose] [--dereference] [--config=FILE]\n",
+"                     [--verbose] [--dereference] [--config=FILE]\n"
+"                     [--threads=NUM_THREADS]\n",
 [APPLY] =
 "imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n"
 "                    (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n"
@@ -71,16 +73,16 @@ static const char *usage_strings[] = {
 "imagex capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
 "                      [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n"
 "                      [--flags EDITION_ID] [--verbose] [--dereference]\n"
-"                   [--config=FILE]\n",
+"                      [--config=FILE] [--threads=NUM_THREADS]\n",
 [DELETE] =
 "imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check]\n",
 [DIR] =
 "imagex dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all)\n",
 [EXPORT] =
 "imagex export SRC_WIMFILE (SRC_IMAGE_NUM | SRC_IMAGE_NAME | all ) \n"
-"                     DEST_WIMFILE [DEST_IMAGE_NAME]\n"
-"                     [DEST_IMAGE_DESCRIPTION] [--boot] [--check]\n"
-"                     [--compress=TYPE] [--ref=\"GLOB\"]\n",
+"              DEST_WIMFILE [DEST_IMAGE_NAME] [DEST_IMAGE_DESCRIPTION]\n"
+"              [--boot] [--check] [--compress=TYPE] [--ref=\"GLOB\"]\n"
+"              [--threads=NUM_THREADS]\n",
 [INFO] =
 "imagex info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n"
 "                   [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n"
@@ -122,6 +124,7 @@ static const struct option capture_or_append_options[] = {
        {"dereference", no_argument,       NULL, 'L'},
        {"flags",       required_argument, NULL, 'f'},
        {"verbose",     no_argument,       NULL, 'v'},
+       {"threads",     required_argument, NULL, 't'},
        {NULL, 0, NULL, 0},
 };
 static const struct option delete_options[] = {
@@ -134,6 +137,7 @@ static const struct option export_options[] = {
        {"check",      no_argument,       NULL, 'c'},
        {"compress",   required_argument, NULL, 'x'},
        {"ref",        required_argument, NULL, 'r'},
+       {"threads",    required_argument, NULL, 't'},
        {NULL, 0, NULL, 0},
 };
 
@@ -351,6 +355,19 @@ out:
 }
 
 
+static unsigned parse_num_threads(const char *optarg)
+{
+       char *tmp;
+       unsigned nthreads = strtoul(optarg, &tmp, 10);
+       if (nthreads == UINT_MAX || *tmp || tmp == optarg) {
+               imagex_error("Number of threads must be a non-negative integer!");
+               return UINT_MAX;
+       } else {
+               return nthreads;
+       }
+}
+
+
 /* Extract one image, or all images, from a WIM file into a directory. */
 static int imagex_apply(int argc, const char **argv)
 {
@@ -475,7 +492,7 @@ static int imagex_capture_or_append(int argc, const char **argv)
 {
        int c;
        int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
-       int add_image_flags = 0;
+       int add_image_flags = WIMLIB_ADD_IMAGE_FLAG_SHOW_PROGRESS;
        int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
        int compression_type = WIM_COMPRESSION_TYPE_XPRESS;
        const char *dir;
@@ -491,6 +508,7 @@ static int imagex_capture_or_append(int argc, const char **argv)
        int cur_image;
        char *default_name;
        int cmd = strcmp(argv[0], "append") ? CAPTURE : APPEND;
+       unsigned num_threads = 0;
 
        for_opt(c, capture_or_append_options) {
                switch (c) {
@@ -519,6 +537,11 @@ static int imagex_capture_or_append(int argc, const char **argv)
                        add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE;
                        write_flags |= WIMLIB_WRITE_FLAG_VERBOSE;
                        break;
+               case 't':
+                       num_threads = parse_num_threads(optarg);
+                       if (num_threads == UINT_MAX)
+                               return -1;
+                       break;
                default:
                        usage(cmd);
                        return -1;
@@ -594,10 +617,12 @@ out_write:
                if (ret != 0)
                        goto out;
        }
-       if (cmd == APPEND)
-               ret = wimlib_overwrite(w, write_flags);
-       else
-               ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags);
+       if (cmd == APPEND) {
+               ret = wimlib_overwrite(w, write_flags, num_threads);
+       } else {
+               ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags,
+                                  num_threads);
+       }
        if (ret != 0)
                imagex_error("Failed to write the WIM file `%s'", wimfile);
 out:
@@ -663,7 +688,7 @@ static int imagex_delete(int argc, const char **argv)
                goto out;
        }
 
-       ret = wimlib_overwrite(w, write_flags);
+       ret = wimlib_overwrite(w, write_flags, 0);
        if (ret != 0) {
                imagex_error("Failed to write the file `%s' with image "
                             "deleted", wimfile);
@@ -747,6 +772,7 @@ static int imagex_export(int argc, const char **argv)
        const char *swm_glob = NULL;
        WIMStruct **additional_swms = NULL;
        unsigned num_additional_swms = 0;
+       unsigned num_threads = 0;
 
        for_opt(c, export_options) {
                switch (c) {
@@ -766,6 +792,11 @@ static int imagex_export(int argc, const char **argv)
                case 'r':
                        swm_glob = optarg;
                        break;
+               case 't':
+                       num_threads = parse_num_threads(optarg);
+                       if (num_threads == UINT_MAX)
+                               return -1;
+                       break;
                default:
                        usage(EXPORT);
                        return -1;
@@ -859,9 +890,9 @@ static int imagex_export(int argc, const char **argv)
 
        if (wim_is_new)
                ret = wimlib_write(dest_w, dest_wimfile, WIM_ALL_IMAGES,
-                                  write_flags);
+                                  write_flags, num_threads);
        else
-               ret = wimlib_overwrite(dest_w, write_flags);
+               ret = wimlib_overwrite(dest_w, write_flags, num_threads);
 out:
        wimlib_free(src_w);
        wimlib_free(dest_w);
index 559d7e8ba71c5fca359c750a5cf94f86a0db3fb7..f4b8bb582e43c0dd0c4dbb3dc7b4f8d86905d6b0 100644 (file)
@@ -71,16 +71,6 @@ static u64 dentry_correct_length(const struct dentry *dentry)
        return (dentry_correct_length_unaligned(dentry) + 7) & ~7;
 }
 
-/* Return %true iff @dentry has the UTF-8 file name @name that has length
- * @name_len bytes. */
-static bool dentry_has_name(const struct dentry *dentry, const char *name,
-                           size_t name_len)
-{
-       if (dentry->file_name_utf8_len != name_len)
-               return false;
-       return memcmp(dentry->file_name_utf8, name, name_len) == 0;
-}
-
 /* Return %true iff the alternate data stream entry @entry has the UTF-8 stream
  * name @name that has length @name_len bytes. */
 static inline bool ads_entry_has_name(const struct ads_entry *entry,
index 7fc1a6cfe4f21eca877a581b0b5601a9a4aa3d85..8fb481dc219caa4ea0584e41cdc8947f389d3ce2 100644 (file)
@@ -968,6 +968,9 @@ int do_add_image(WIMStruct *w, const char *dir, const char *name,
        sd->refcnt = 1;
 
        DEBUG("Building dentry tree.");
+       if (flags & WIMLIB_ADD_IMAGE_FLAG_SHOW_PROGRESS) {
+               printf("Scanning `%s'...\n", dir);
+       }
        ret = (*capture_tree)(&root_dentry, dir, w->lookup_table, sd,
                              &config, flags | WIMLIB_ADD_IMAGE_FLAG_ROOT,
                              extra_arg);
index 83615c1db2204bd304549da6fb094e7e542d02ad..3c0b9290e1a209c12e79942a3dd5a659787df373 100644 (file)
@@ -889,7 +889,7 @@ static int rebuild_wim(struct wimfs_context *ctx, bool check_integrity)
 
        xml_update_image_info(w, w->current_image);
 
-       ret = wimlib_overwrite(w, check_integrity);
+       ret = wimlib_overwrite(w, check_integrity, 0);
        if (ret != 0) {
                ERROR("Failed to commit changes");
                return ret;
index a8b07ac08b2a0f89cce35056ed73d9d37e796562..a12a05f435c3e7125c1bcfdf8750ea9dd40845e8 100644 (file)
@@ -262,7 +262,8 @@ enum wim_compression_type {
 /** Include an integrity table in the new WIM file. */
 #define WIMLIB_WRITE_FLAG_CHECK_INTEGRITY      0x00000001
 
-/** Print progress information when writing the integrity table. */
+/** Print progress information when writing streams and when writing the
+ * integrity table. */
 #define WIMLIB_WRITE_FLAG_SHOW_PROGRESS                0x00000002
 
 /** Print file paths as we write then */
@@ -278,6 +279,9 @@ enum wim_compression_type {
 /** Follow symlinks; archive and dump the files they point to. */
 #define WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE      0x00000004
 
+/** Show progress information when scanning a directory tree */
+#define WIMLIB_ADD_IMAGE_FLAG_SHOW_PROGRESS    0x00000004
+
 /** See documentation for wimlib_export_image(). */
 #define WIMLIB_EXPORT_FLAG_BOOT                        0x00000001
 
@@ -399,7 +403,9 @@ enum wimlib_error_code {
  *     printed as it is scanned or captured.  If
  *     ::WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE is specified, the files or
  *     directories pointed to by symbolic links are archived rather than the
- *     symbolic links themselves.
+ *     symbolic links themselves.  If ::WIMLIB_ADD_IMAGE_FLAG_SHOW_PROGRESS is
+ *     specified, progress information will be printed (distinct from the
+ *     verbose information).
  *
  * @return 0 on success; nonzero on error.  On error, changes to @a wim are
  * discarded so that it appears to be in the same state as when this function
@@ -1076,9 +1082,11 @@ extern int wimlib_open_wim(const char *wim_file, int flags,
  *     Pointer to the ::WIMStruct for the WIM file to write.  There may have
  *     been in-memory changes made to it, which are then reflected in the
  *     output file.
- * @param flags
+ * @param write_flags
  *     Bitwise OR of ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY and/or
  *     ::WIMLIB_WRITE_FLAG_SHOW_PROGRESS.
+ * @param num_threads
+ *     Number of threads to use for compression (see wimlib_write()).
  *
  * @return 0 on success; nonzero on error.  This function may return any value
  * returned by wimlib_write() as well as the following error codes:
@@ -1089,7 +1097,8 @@ extern int wimlib_open_wim(const char *wim_file, int flags,
  *     The temporary file that the WIM was written to could not be renamed to
  *     the original filename of @a wim.
  */
-extern int wimlib_overwrite(WIMStruct *wim, int flags);
+extern int wimlib_overwrite(WIMStruct *wim, int write_flags,
+                           unsigned num_threads);
 
 /**
  * Updates the header and XML data of the WIM file, without the need to write
@@ -1104,7 +1113,7 @@ extern int wimlib_overwrite(WIMStruct *wim, int flags);
  *
  * @param wim
  *     Pointer to the ::WIMStruct for the WIM file to overwrite.
- * @param flags
+ * @param write_flags
  *     Bitwise OR of ::WIMLIB_WRITE_FLAG_CHECK_INTEGRITY and/or
  *     ::WIMLIB_WRITE_FLAG_SHOW_PROGRESS.
  *
@@ -1131,7 +1140,7 @@ extern int wimlib_overwrite(WIMStruct *wim, int flags);
  *     Failed to write the WIM header, the XML data, or the integrity table to
  *     the WIM file associated with @a wim.
  */
-extern int wimlib_overwrite_xml_and_header(WIMStruct *wim, int flags);
+extern int wimlib_overwrite_xml_and_header(WIMStruct *wim, int write_flags);
 
 /**
  * Prints information about one image, or all images, contained in a WIM.
@@ -1536,6 +1545,12 @@ extern int wimlib_unmount(const char *dir, int flags);
  *     included in the WIM being written.  If ::WIMLIB_WRITE_FLAG_SHOW_PROGRESS
  *     is given, the progress of the calculation of the integrity table is
  *     shown.
+ * @param num_threads
+ *     Number of threads to use for compressing data.  Autodetected if set to
+ *     0.  Note: if no data compression needs to be done, no threads will be
+ *     created regardless of this parameter (e.g. if writing an uncompressed
+ *     WIM, or exporting an image from a compressed WIM to another WIM of the
+ *     same compression type).
  *
  * @return 0 on success; nonzero on error.
  * @retval ::WIMLIB_ERR_DECOMPRESSION
@@ -1574,7 +1589,8 @@ extern int wimlib_unmount(const char *dir, int flags);
  *     An error occurred when trying to write data to the new WIM file at @a
  *     path.
  */
-extern int wimlib_write(WIMStruct *wim, const char *path, int image, int flags);
+extern int wimlib_write(WIMStruct *wim, const char *path, int image,
+                       int write_flags, unsigned num_threads);
 
 
 
index 56fbab0ea1c08e5b33c7671e01f768c257821962..a84c16b0c91e560ded0b13f6ea95b812899e9e70 100644 (file)
@@ -73,7 +73,8 @@ static int reopen_rw(WIMStruct *w)
 /*
  * Writes a WIM file to the original file that it was read from, overwriting it.
  */
-WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int write_flags)
+WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int write_flags,
+                              unsigned num_threads)
 {
        const char *wimfile_name;
        size_t wim_name_len;
@@ -99,7 +100,8 @@ WIMLIBAPI int wimlib_overwrite(WIMStruct *w, int write_flags)
        randomize_char_array_with_alnum(tmpfile + wim_name_len, 9);
        tmpfile[wim_name_len + 9] = '\0';
 
-       ret = wimlib_write(w, tmpfile, WIM_ALL_IMAGES, write_flags);
+       ret = wimlib_write(w, tmpfile, WIM_ALL_IMAGES, write_flags,
+                          num_threads);
        if (ret != 0) {
                ERROR("Failed to write the WIM file `%s'", tmpfile);
                if (unlink(tmpfile) != 0)
@@ -902,19 +904,56 @@ static void *compressor_thread_proc(void *arg)
        DEBUG("Compressor thread terminating");
 }
 
+static void show_stream_write_progress(u64 *cur_size, u64 *next_size,
+                                      u64 total_size, u64 one_percent,
+                                      unsigned *cur_percent,
+                                      const struct lookup_table_entry *cur_lte)
+{
+       if (*cur_size >= *next_size) {
+               printf("\r%"PRIu64" MiB of %"PRIu64" MiB "
+                      "(uncompressed) written (%u%% done)",
+                      *cur_size >> 20,
+                      total_size >> 20, *cur_percent);
+               fflush(stdout);
+               *next_size += one_percent;
+               (*cur_percent)++;
+       }
+       *cur_size += wim_resource_size(cur_lte);
+}
+
+static void finish_stream_write_progress(u64 total_size)
+{
+       printf("\r%"PRIu64" MiB of %"PRIu64" MiB "
+              "(uncompressed) written (100%% done)\n",
+              total_size >> 20, total_size >> 20);
+       fflush(stdout);
+}
+
 static int write_stream_list_serial(struct list_head *stream_list,
                                    FILE *out_fp, int out_ctype,
-                                   int write_flags)
+                                   int write_flags, u64 total_size)
 {
        struct lookup_table_entry *lte;
        int ret;
 
+       u64 one_percent = total_size / 100;
+       u64 cur_size = 0;
+       u64 next_size = 0;
+       unsigned cur_percent = 0;
+
        list_for_each_entry(lte, stream_list, staging_list) {
+               if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) {
+                       show_stream_write_progress(&cur_size, &next_size,
+                                                  total_size, one_percent,
+                                                  &cur_percent, lte);
+               }
                ret = write_wim_resource(lte, out_fp, out_ctype,
                                         &lte->output_resource_entry, 0);
                if (ret != 0)
                        return ret;
        }
+       if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS)
+               finish_stream_write_progress(total_size);
        return 0;
 }
 
@@ -940,6 +979,7 @@ static int write_wim_chunks(struct message *msg, FILE *out_fp,
        return 0;
 }
 
+
 /*
  * This function is executed by the main thread when the resources are being
  * compressed in parallel.  The main thread is in change of all reading of the
@@ -957,7 +997,9 @@ static int main_writer_thread_proc(struct list_head *stream_list,
                                   int out_ctype,
                                   struct shared_queue *res_to_compress_queue,
                                   struct shared_queue *compressed_res_queue,
-                                  size_t queue_size)
+                                  size_t queue_size,
+                                  int write_flags,
+                                  u64 total_size)
 {
        int ret;
 
@@ -1010,6 +1052,11 @@ static int main_writer_thread_proc(struct list_head *stream_list,
        struct lookup_table_entry *lte;
        struct message *msg;
 
+       u64 one_percent = total_size / 100;
+       u64 cur_size = 0;
+       u64 next_size = 0;
+       unsigned cur_percent = 0;
+
 #ifdef WITH_NTFS_3G
        ntfs_inode *ni = NULL;
 #endif
@@ -1206,6 +1253,18 @@ static int main_writer_thread_proc(struct list_head *stream_list,
                        DEBUG2("Complete msg (begin_chunk=%"PRIu64")", msg->begin_chunk);
                        if (msg->begin_chunk == 0) {
                                DEBUG2("Begin chunk tab");
+
+
+
+                               if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) {
+                                       show_stream_write_progress(&cur_size,
+                                                                  &next_size,
+                                                                  total_size,
+                                                                  one_percent,
+                                                                  &cur_percent,
+                                                                  cur_lte);
+                               }
+
                                // This is the first set of chunks.  Leave space
                                // for the chunk table in the output file.
                                off_t cur_offset = ftello(out_fp);
@@ -1288,6 +1347,15 @@ static int main_writer_thread_proc(struct list_head *stream_list,
                                                         &my_resources,
                                                         staging_list)
                                {
+                                       if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) {
+                                               show_stream_write_progress(&cur_size,
+                                                                          &next_size,
+                                                                          total_size,
+                                                                          one_percent,
+                                                                          &cur_percent,
+                                                                          lte);
+                                       }
+
                                        ret = write_wim_resource(lte,
                                                                 out_fp,
                                                                 out_ctype,
@@ -1315,7 +1383,17 @@ out:
                                                 0);
                        if (ret != 0)
                                break;
+                       if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) {
+                               show_stream_write_progress(&cur_size,
+                                                          &next_size,
+                                                          total_size,
+                                                          one_percent,
+                                                          &cur_percent,
+                                                          lte);
+                       }
                }
+               if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS)
+                       finish_stream_write_progress(total_size);
        } else {
                size_t num_available_msgs = 0;
                struct list_head *cur;
@@ -1346,26 +1424,30 @@ out:
 
 static int write_stream_list_parallel(struct list_head *stream_list,
                                      FILE *out_fp, int out_ctype,
-                                     int write_flags)
+                                     int write_flags, u64 total_size,
+                                     unsigned num_threads)
 {
        int ret;
-       long nthreads;
        struct shared_queue res_to_compress_queue;
        struct shared_queue compressed_res_queue;
 
-       nthreads = sysconf(_SC_NPROCESSORS_ONLN);
-       if (nthreads < 1) {
-               WARNING("Could not determine number of processors! Assuming 1");
-               goto out_serial;
+       if (num_threads == 0) {
+               long nthreads = sysconf(_SC_NPROCESSORS_ONLN);
+               if (nthreads < 1) {
+                       WARNING("Could not determine number of processors! Assuming 1");
+                       goto out_serial;
+               } else {
+                       num_threads = nthreads;
+               }
        }
 
        wimlib_assert(stream_list->next != stream_list);
 
        {
-               pthread_t compressor_threads[nthreads];
+               pthread_t compressor_threads[num_threads];
 
                static const double MESSAGES_PER_THREAD = 2.0;
-               size_t queue_size = (size_t)(nthreads * MESSAGES_PER_THREAD);
+               size_t queue_size = (size_t)(num_threads * MESSAGES_PER_THREAD);
 
                DEBUG("Initializing shared queues (queue_size=%zu)", queue_size);
 
@@ -1382,21 +1464,21 @@ static int write_stream_list_parallel(struct list_head *stream_list,
                params.compressed_res_queue = &compressed_res_queue;
                params.compress = get_compress_func(out_ctype);
 
-               for (long i = 0; i < nthreads; i++) {
-                       DEBUG("pthread_create thread %ld", i);
+               for (unsigned i = 0; i < num_threads; i++) {
+                       DEBUG("pthread_create thread %u", i);
                        ret = pthread_create(&compressor_threads[i], NULL,
                                             compressor_thread_proc, &params);
                        if (ret != 0) {
                                ERROR_WITH_ERRNO("Failed to create compressor "
-                                                "thread %ld", i);
-                               nthreads = i;
+                                                "thread %u", i);
+                               num_threads = i;
                                goto out_join;
                        }
                }
 
-               if (write_flags & WIMLIB_WRITE_FLAG_VERBOSE) {
-                       printf("Writing compressed data using %ld threads...\n",
-                              nthreads);
+               if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) {
+                       printf("Writing compressed data using %u threads...\n",
+                              num_threads);
                }
 
                ret = main_writer_thread_proc(stream_list,
@@ -1404,15 +1486,17 @@ static int write_stream_list_parallel(struct list_head *stream_list,
                                              out_ctype,
                                              &res_to_compress_queue,
                                              &compressed_res_queue,
-                                             queue_size);
+                                             queue_size,
+                                             write_flags,
+                                             total_size);
 
        out_join:
-               for (long i = 0; i < nthreads; i++)
+               for (unsigned i = 0; i < num_threads; i++)
                        shared_queue_put(&res_to_compress_queue, NULL);
 
-               for (long i = 0; i < nthreads; i++) {
+               for (unsigned i = 0; i < num_threads; i++) {
                        if (pthread_join(compressor_threads[i], NULL)) {
-                               WARNING("Failed to join compressor thread %ld: %s",
+                               WARNING("Failed to join compressor thread %u: %s",
                                        i, strerror(errno));
                        }
                }
@@ -1425,11 +1509,12 @@ out_destroy_res_to_compress_queue:
 out_serial:
        WARNING("Falling back to single-threaded compression");
        return write_stream_list_serial(stream_list, out_fp,
-                                       out_ctype, write_flags);
+                                       out_ctype, write_flags, total_size);
 }
 
 static int write_stream_list(struct list_head *stream_list, FILE *out_fp,
-                            int out_ctype, int write_flags)
+                            int out_ctype, int write_flags,
+                            unsigned num_threads)
 {
        struct lookup_table_entry *lte;
        size_t num_streams = 0;
@@ -1455,16 +1540,21 @@ static int write_stream_list(struct list_head *stream_list, FILE *out_fp,
                       wimlib_get_compression_type_string(out_ctype));
        }
 
-       if (compression_needed && total_size >= 1000000) {
+       if (compression_needed && total_size >= 1000000 && num_threads != 1) {
                return write_stream_list_parallel(stream_list, out_fp,
-                                                 out_ctype, write_flags);
+                                                 out_ctype, write_flags,
+                                                 total_size, num_threads);
        } else {
-               if (write_flags & WIMLIB_WRITE_FLAG_VERBOSE) {
-                       puts("Using 1 thread (no compression needed)");
+               if (write_flags & WIMLIB_WRITE_FLAG_SHOW_PROGRESS) {
+                       const char *reason = "";
+                       if (num_threads != 1)
+                               reason = " (no compression needed)";
+                       printf("Writing data using 1 thread%s\n", reason);
                }
 
                return write_stream_list_serial(stream_list, out_fp,
-                                               out_ctype, write_flags);
+                                               out_ctype, write_flags,
+                                               total_size);
        }
 }
 
@@ -1489,7 +1579,8 @@ static int find_streams_to_write(WIMStruct *w)
                                  dentry_find_streams_to_write, w);
 }
 
-static int write_wim_streams(WIMStruct *w, int image, int write_flags)
+static int write_wim_streams(WIMStruct *w, int image, int write_flags,
+                            unsigned num_threads)
 {
 
        LIST_HEAD(stream_list);
@@ -1497,7 +1588,8 @@ static int write_wim_streams(WIMStruct *w, int image, int write_flags)
        w->private = &stream_list;
        for_image(w, image, find_streams_to_write);
        return write_stream_list(&stream_list, w->out_fp,
-                                wimlib_get_compression_type(w), write_flags);
+                                wimlib_get_compression_type(w), write_flags,
+                                num_threads);
 }
 
 /*
@@ -1662,7 +1754,7 @@ int begin_write(WIMStruct *w, const char *path, int write_flags)
 
 /* Writes a stand-alone WIM to a file.  */
 WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path,
-                          int image, int write_flags)
+                          int image, int write_flags, unsigned num_threads)
 {
        int ret;
 
@@ -1692,7 +1784,7 @@ WIMLIBAPI int wimlib_write(WIMStruct *w, const char *path,
 
        for_lookup_table_entry(w->lookup_table, lte_zero_out_refcnt, NULL);
 
-       ret = write_wim_streams(w, image, write_flags);
+       ret = write_wim_streams(w, image, write_flags, num_threads);
 
        if (ret != 0) {
                /*ERROR("Failed to write WIM file resources to `%s'", path);*/