/* Worker thread. */ static void * start_thread (void *thread_data_vp) { struct thread_data *thread_data = thread_data_vp; int quit = 0; int err; size_t i; guestfs_h *g; unsigned errors = 0; char id[64]; for (;;) { CLEANUP_FREE char *log_file = NULL; CLEANUP_FCLOSE FILE *log_fp = NULL; /* Take the next process. */ err = pthread_mutex_lock (&mutex); if (err != 0) { fprintf (stderr, "%s: pthread_mutex_lock: %s", guestfs_int_program_name, strerror (err)); goto error; } i = n; if (i > 0) { printf ("%zu to go ... \r", n); fflush (stdout); n--; } else quit = 1; err = pthread_mutex_unlock (&mutex); if (err != 0) { fprintf (stderr, "%s: pthread_mutex_unlock: %s", guestfs_int_program_name, strerror (err)); goto error; } if (quit) /* Work finished. */ break; g = guestfs_create (); if (g == NULL) { perror ("guestfs_create"); errors++; if (!ignore_errors) goto error; } /* Only if using --log, set up a callback. See examples/debug-logging.c */ if (log_template != NULL) { size_t j, k; log_file = malloc (log_file_size + 1); if (log_file == NULL) abort (); for (j = 0, k = 0; j < strlen (log_template); ++j) { if (log_template[j] == '%') { snprintf (&log_file[k], log_file_size - k, "%zu", i); k += strlen (&log_file[k]); } else log_file[k++] = log_template[j]; } log_file[k] = '\0'; log_fp = fopen (log_file, "w"); if (log_fp == NULL) { perror (log_file); abort (); } guestfs_set_event_callback (g, message_callback, event_bitmask, 0, log_fp); } snprintf (id, sizeof id, "%zu", i); guestfs_set_identifier (g, id); guestfs_set_trace (g, trace); guestfs_set_verbose (g, verbose); if (guestfs_add_drive_ro (g, "/dev/null") == -1) { errors++; if (!ignore_errors) goto error; } if (guestfs_launch (g) == -1) { errors++; if (!ignore_errors) goto error; } if (guestfs_shutdown (g) == -1) { errors++; if (!ignore_errors) goto error; } guestfs_close (g); } if (errors > 0) { fprintf (stderr, "%s: thread %d: %u errors were ignored\n", guestfs_int_program_name, thread_data->thread_num, errors); goto error; } thread_data->r = 0; return &thread_data->r; error: thread_data->r = -1; return &thread_data->r; }
/* Worker thread. */ static void * worker_thread (void *thread_data_vp) { struct thread_data *thread_data = thread_data_vp; thread_data->r = 0; if (thread_data->verbose) fprintf (stderr, "parallel: thread %zu starting\n", thread_data->thread_num); while (1) { size_t i; /* The current domain we're working on. */ FILE *fp; CLEANUP_FREE char *output = NULL; size_t output_len = 0; guestfs_h *g; int err; char id[64]; /* Take the next domain from the list. */ if (thread_data->verbose) fprintf (stderr, "parallel: thread %zu waiting to get work\n", thread_data->thread_num); err = pthread_mutex_lock (&take_mutex); if (err != 0) { thread_failure ("pthread_mutex_lock", err); thread_data->r = -1; return &thread_data->r; } i = next_domain_to_take++; err = pthread_mutex_unlock (&take_mutex); if (err != 0) { thread_failure ("pthread_mutex_unlock", err); thread_data->r = -1; return &thread_data->r; } if (i >= nr_domains) /* Work finished. */ break; if (thread_data->verbose) fprintf (stderr, "parallel: thread %zu taking domain %zu\n", thread_data->thread_num, i); fp = open_memstream (&output, &output_len); if (fp == NULL) { perror ("open_memstream"); thread_data->r = -1; return &thread_data->r; } /* Create a guestfs handle. */ g = guestfs_create (); if (g == NULL) { perror ("guestfs_create"); thread_data->r = -1; return &thread_data->r; } /* Set the handle identifier so we can tell threads apart. */ snprintf (id, sizeof id, "thread_%zu_domain_%zu", thread_data->thread_num, i); guestfs_set_identifier (g, id); /* Copy some settings from the options guestfs handle. */ guestfs_set_trace (g, thread_data->trace); guestfs_set_verbose (g, thread_data->verbose); /* Do work. */ if (thread_data->work (g, i, fp) == -1) { thread_data->r = -1; if (thread_data->verbose) fprintf (stderr, "parallel: thread %zu work function returned an error\n", thread_data->thread_num); } fclose (fp); guestfs_close (g); /* Retire this domain. We have to retire domains in order, which * may mean waiting for another thread to finish here. */ if (thread_data->verbose) fprintf (stderr, "parallel: thread %zu waiting to retire domain %zu\n", thread_data->thread_num, i); err = pthread_mutex_lock (&retire_mutex); if (err != 0) { thread_failure ("pthread_mutex_lock", err); thread_data->r = -1; return &thread_data->r; } while (next_domain_to_retire != i) { err = pthread_cond_wait (&retire_cond, &retire_mutex); if (err != 0) { thread_failure ("pthread_cond_wait", err); thread_data->r = -1; ignore_value (pthread_mutex_unlock (&retire_mutex)); return &thread_data->r; } } if (thread_data->verbose) fprintf (stderr, "parallel: thread %zu retiring domain %zu\n", thread_data->thread_num, i); /* Retire domain. */ printf ("%s", output); /* Update next_domain_to_retire and tell other threads. */ next_domain_to_retire = i+1; pthread_cond_broadcast (&retire_cond); err = pthread_mutex_unlock (&retire_mutex); if (err != 0) { thread_failure ("pthread_mutex_unlock", err); thread_data->r = -1; return &thread_data->r; } } if (thread_data->verbose) fprintf (stderr, "parallel: thread %zu exiting (r = %d)\n", thread_data->thread_num, thread_data->r); return &thread_data->r; }