Index: /trunk/ppMerge/src/ppMerge.h
===================================================================
--- /trunk/ppMerge/src/ppMerge.h	(revision 18966)
+++ /trunk/ppMerge/src/ppMerge.h	(revision 18967)
@@ -15,5 +15,5 @@
 #define TIMERNAME "ppMerge"             // Name for timer
 #define PPMERGE_RECIPE "PPMERGE"        // Recipe name
-#define THREADED 1
+#define THREADED 1                      // Compile with threads?
 
 // Type of frame to merge
@@ -35,10 +35,13 @@
 } ppMergeFiles;
 
+// Group of files to read
+//
+// Each file contributes a readout, into which is read a chunk from that file
 typedef struct {
-    psArray *readouts;
-    bool read;
-    bool busy;
-    int firstScan;
-    int lastScan;
+    psArray *readouts;                  // Input readouts
+    bool read;                          // Has the scan been read?
+    bool busy;                          // Is the scan being processed?
+    int firstScan;                      // First row of the chunk to be read for this group
+    int lastScan;                       // Last row of the chunk to be read for this group
 } ppMergeFileGroup;
 
@@ -101,9 +104,16 @@
 
 
-ppMergeFileGroup *ppMergeFileGroupAlloc();
-ppMergeFileGroup *ppMergeReadChunk (bool *status, psArray *fileGroups, pmConfig *config, int numChunk);
-void *ppMergeThreadLauncher (void *data);
+// Allocator for group of files
+ppMergeFileGroup *ppMergeFileGroupAlloc(void);
 
-bool ppMergeSetThreads ();
+// Read chunk into the first available file group
+ppMergeFileGroup *ppMergeReadChunk(bool *status, // Status of read
+                                   psArray *fileGroups, // All groups
+                                   pmConfig *config, // Configuration
+                                   int numChunk // Chunk number (only for interest)
+    );
+
+// Set up thread handling
+bool ppMergeSetThreads(void);
 
 #endif
Index: /trunk/ppMerge/src/ppMergeArguments.c
===================================================================
--- /trunk/ppMerge/src/ppMergeArguments.c	(revision 18966)
+++ /trunk/ppMerge/src/ppMergeArguments.c	(revision 18967)
@@ -174,11 +174,11 @@
     if ((argnum = psArgumentGet(argc, argv, "-threads"))) {
         psArgumentRemove(argnum, &argc, argv);
-	int nThreads = atoi(argv[argnum]);
+        int nThreads = atoi(argv[argnum]);
         psMetadataAddS32(config->arguments, PS_LIST_TAIL, "NTHREADS", 0, "number of warp threads", nThreads);
         psArgumentRemove(argnum, &argc, argv);
 
-	// create the thread pool with number of desired threads, supplying our thread launcher function
-	// XXX need to determine the number of threads from the config data
-	psThreadPoolInit (nThreads);
+        // create the thread pool with number of desired threads, supplying our thread launcher function
+        // XXX need to determine the number of threads from the config data
+        psThreadPoolInit (nThreads);
     }
     ppMergeSetThreads();
@@ -379,4 +379,5 @@
 #endif
 
+    psFree(arguments);
     return true;
 
Index: /trunk/ppMerge/src/ppMergeCamera.c
===================================================================
--- /trunk/ppMerge/src/ppMergeCamera.c	(revision 18966)
+++ /trunk/ppMerge/src/ppMergeCamera.c	(revision 18967)
@@ -31,5 +31,5 @@
             if (! pmFPASelectChip(fpa, chipNum, false)) {
                 psError(PS_ERR_IO, false, "Chip number %d doesn't exist in camera.\n", chipNum);
-		psFree (chips);
+                psFree (chips);
                 return false;
             }
@@ -43,16 +43,16 @@
     psArray *cells = psStringSplitArray (cellLine, ",", false);
     if (cells->n > 0) {
-	for (int i = 0; i < fpa->chips->n; i++) {
-	    pmChip *chip = fpa->chips->data[i];
-	    pmChipSelectCell (chip, -1, true); // deselect all cells
-	    for (int j = 0; j < cells->n; j++) {
-		int cellNum = atoi(cells->data[j]);
-		if (! pmChipSelectCell(chip, cellNum, false)) {
-		    psError(PS_ERR_IO, false, "Cell number %d doesn't exist in camera.\n", cellNum);
-		    psFree (cells);
-		    return false;
-		}
-	    }
-	}
+        for (int i = 0; i < fpa->chips->n; i++) {
+            pmChip *chip = fpa->chips->data[i];
+            pmChipSelectCell (chip, -1, true); // deselect all cells
+            for (int j = 0; j < cells->n; j++) {
+                int cellNum = atoi(cells->data[j]);
+                if (! pmChipSelectCell(chip, cellNum, false)) {
+                    psError(PS_ERR_IO, false, "Cell number %d doesn't exist in camera.\n", cellNum);
+                    psFree (cells);
+                    return false;
+                }
+            }
+        }
     }
     psFree (cells);
@@ -259,8 +259,8 @@
                     cell->file_exists = false;
                     culled++;
-		    if (cell->concepts) {
-			psFree(cell->concepts);
-			cell->concepts = NULL;
-		    }
+                    if (cell->concepts) {
+                        psFree(cell->concepts);
+                        cell->concepts = NULL;
+                    }
                 }
             }
@@ -268,8 +268,8 @@
                 chip->data_exists = false;
                 chip->file_exists = false;
-		if (chip->concepts) {
-		    psFree(chip->concepts);
-		    chip->concepts = NULL;
-		}
+                if (chip->concepts) {
+                    psFree(chip->concepts);
+                    chip->concepts = NULL;
+                }
             }
         }
@@ -298,12 +298,4 @@
     }
 
-    // Output image
-    pmFPA *fpa = pmFPAConstruct(config->camera, config->cameraName); // FPA to contain the output
-    if (!fpa) {
-        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to construct an FPA from camera configuration.");
-        psFree(phuView);
-        return false;
-    }
-
     psString outName = ppMergeOutputFile(config); // Name of output file
 
Index: /trunk/ppMerge/src/ppMergeLoop_Threaded.c
===================================================================
--- /trunk/ppMerge/src/ppMergeLoop_Threaded.c	(revision 18966)
+++ /trunk/ppMerge/src/ppMergeLoop_Threaded.c	(revision 18967)
@@ -12,5 +12,5 @@
 #include "ppMerge.h"
 
-// XXX this function is now sufficiently different for the major types, it would make sense to just 
+// XXX this function is now sufficiently different for the major types, it would make sense to just
 // split it into three: BASIC, SHUTTER, DARK
 
@@ -57,6 +57,6 @@
     psMaskType markVal;
     if (!pmConfigMaskSetBits (&maskVal, &markVal, config)) {
-	psError (PS_ERR_UNKNOWN, true, "Unable to define the mask bit values");
-	return false;
+        psError (PS_ERR_UNKNOWN, true, "Unable to define the mask bit values");
+        return false;
     }
 
@@ -151,10 +151,10 @@
             if (type == PPMERGE_TYPE_SHUTTER) {
                 shutterRef = pmShutterCorrectionReference(shutters->data[cellNum]);
-		pattern = pmReadoutAlloc(NULL);
+                pattern = pmReadoutAlloc(NULL);
             }
 
             pmReadout *outRO = pmReadoutAlloc(outCell);
 
-	    // open the input files (we need to do the work ourselves)
+            // open the input files (we need to do the work ourselves)
             for (int i = 0; i < numFiles; i++) {
                 if (!ppMergeFileOpenInput(config, view, i)) {
@@ -162,122 +162,127 @@
                     goto ERROR;
                 }
-	    }
-
-	    ppMergeFileGroup *fileGroup = NULL;
-	    psArray *fileGroups = psArrayAlloc (nThreads + 1);
-
-	    // generate readouts for each input file in each file group
-	    for (int i = 0; i < fileGroups->n; i++) {
-		psArray *readouts = psArrayAlloc(numFiles); // Input readouts
-		for (int j = 0; j < numFiles; j++) {
-		    pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i);
-		    pmCell *inCell = pmFPAviewThisCell(view, input->fpa); // Input cell
-		    readouts->data[j] = pmReadoutAlloc(inCell);
-		}
-
-		fileGroup = ppMergeFileGroupAlloc();
-		fileGroup->readouts = readouts;
-		fileGroup->read = false;
-		fileGroup->busy = false;
-		fileGroup->lastScan = 0;
-		fileGroup->firstScan = 0;
-		fileGroups->data[i] = fileGroup;
-            }
-
-	    // call the init functions
-	    switch (type) {
-	      case PPMERGE_TYPE_BIAS:
-	      case PPMERGE_TYPE_FLAT:
-	      case PPMERGE_TYPE_FRINGE: 
-		psAssert (fileGroups->n > 0, "no valid file groups defined");
-		fileGroup = fileGroups->data[0];
-		if (!pmReadoutCombinePrepare(outRO, fileGroup->readouts, combination)) {
-		    goto ERROR;
-		}
-		break;
-	      case PPMERGE_TYPE_DARK:
-		psAssert (fileGroups->n > 0, "no valid file groups defined");
-		fileGroup = fileGroups->data[0];
-		if (!pmDarkCombinePrepare(outCell, fileGroup->readouts, darkOrdinates, darkNorm)) {
-		    goto ERROR;
-		}
-		break;
-	      case PPMERGE_TYPE_SHUTTER:
-		psAssert (fileGroups->n > 0, "no valid file groups defined");
-		fileGroup = fileGroups->data[0];
-		if (!pmShutterCorrectionGeneratePrepare(outRO, pattern, fileGroup->readouts, maskVal)) {
-		    goto ERROR;
-		}
-		break;
-	      default:
-		fprintf (stderr, "not yet ready");
-		goto ERROR;
-	    }
+            }
+
+            ppMergeFileGroup *fileGroup = NULL;
+            psArray *fileGroups = psArrayAlloc(nThreads + 1);
+
+            // Generate readouts for each input file in each file group
+            for (int i = 0; i < fileGroups->n; i++) {
+                psArray *readouts = psArrayAlloc(numFiles); // Input readouts
+                for (int j = 0; j < numFiles; j++) {
+                    pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i);
+                    pmCell *inCell = pmFPAviewThisCell(view, input->fpa); // Input cell
+                    readouts->data[j] = pmReadoutAlloc(inCell);
+                }
+
+                fileGroup = ppMergeFileGroupAlloc();
+                fileGroup->readouts = readouts;
+                fileGroup->read = false;
+                fileGroup->busy = false;
+                fileGroup->lastScan = 0;
+                fileGroup->firstScan = 0;
+                fileGroups->data[i] = fileGroup;
+            }
+
+            // call the init functions
+            switch (type) {
+              case PPMERGE_TYPE_BIAS:
+              case PPMERGE_TYPE_FLAT:
+              case PPMERGE_TYPE_FRINGE:
+                psAssert (fileGroups->n > 0, "no valid file groups defined");
+                fileGroup = fileGroups->data[0];
+                if (!pmReadoutCombinePrepare(outRO, fileGroup->readouts, combination)) {
+                    goto ERROR;
+                }
+                break;
+              case PPMERGE_TYPE_DARK:
+                psAssert (fileGroups->n > 0, "no valid file groups defined");
+                fileGroup = fileGroups->data[0];
+                if (!pmDarkCombinePrepare(outCell, fileGroup->readouts, darkOrdinates, darkNorm)) {
+                    goto ERROR;
+                }
+                break;
+              case PPMERGE_TYPE_SHUTTER:
+                psAssert (fileGroups->n > 0, "no valid file groups defined");
+                fileGroup = fileGroups->data[0];
+                if (!pmShutterCorrectionGeneratePrepare(outRO, pattern, fileGroup->readouts, maskVal)) {
+                    goto ERROR;
+                }
+                break;
+              default:
+                psAbort("Should never get here.");
+            }
 
             // Read input data by chunks
-	    psTimerStart ("ppMergeLoop");
+            psTimerStart("ppMergeLoop");
             for (int numChunk = 0; true; numChunk++) {
 
-		bool status = false;
-		fileGroup = ppMergeReadChunk (&status, fileGroups, config, numChunk);
-		if (!status) goto ERROR;
-		if (!fileGroup) break;
-
-		psThreadJob *job = NULL;
-
+                bool status = false;
+                fileGroup = ppMergeReadChunk(&status, fileGroups, config, numChunk);
+                if (!status) {
+                    // Something went wrong
+                    goto ERROR;
+                }
+                if (!fileGroup) {
+                    // Nothing more to read
+                    break;
+                }
+
+                // Start a job
                 switch (type) {
                   case PPMERGE_TYPE_BIAS:
                   case PPMERGE_TYPE_FLAT:
-                  case PPMERGE_TYPE_FRINGE:
-		    // allocate a job
-		    job = psThreadJobAlloc ("PPMERGE_READOUT_COMBINE");
-
-		    // construct the arguments for this job
-		    psArrayAdd (job->args, 1, outRO);
-		    psArrayAdd (job->args, 1, fileGroup);
-		    psArrayAdd (job->args, 1, zeros);
-		    psArrayAdd (job->args, 1, scales);
-		    psArrayAdd (job->args, 1, combination);
-
-		    // call: pmReadoutCombine(outRO, fileGroup->readouts, zeros, scales, combination);
-		    if (!psThreadJobAddPending (job)) {
-			goto ERROR;
-		    }
-                    break;
-                  case PPMERGE_TYPE_DARK:
-		    // allocate a job
-		    job = psThreadJobAlloc ("PPMERGE_DARK_COMBINE");
-
-		    // construct the arguments for this job
-		    psArrayAdd (job->args, 1, outCell);
-		    psArrayAdd (job->args, 1, fileGroup);
-		    psArrayAdd (job->args, 1, psScalarAlloc(iter, PS_TYPE_S32));
-		    psArrayAdd (job->args, 1, psScalarAlloc(rej, PS_TYPE_F32));
-		    psArrayAdd (job->args, 1, psScalarAlloc(maskVal, PS_TYPE_U8));
-
-		    // call: pmDarkCombine(outCell, fileGroup->readouts, iter, rej, maskVal);
-		    if (!psThreadJobAddPending (job)) {			
-			goto ERROR;
-		    }
-                    break;
-                  case PPMERGE_TYPE_SHUTTER:
-		    // allocate a job
-		    job = psThreadJobAlloc ("PPMERGE_SHUTTER_CORRECTION");
-
-		    // construct the arguments for this job
-		    psArrayAdd (job->args, 1, outRO);
-		    psArrayAdd (job->args, 1, pattern);
-		    psArrayAdd (job->args, 1, fileGroup);
-		    psArrayAdd (job->args, 1, psScalarAlloc(shutterRef, PS_TYPE_F32));
-		    psArrayAdd (job->args, 1, shutters->data[cellNum]);
-		    psArrayAdd (job->args, 1, psScalarAlloc(iter, PS_TYPE_S32));
-		    psArrayAdd (job->args, 1, psScalarAlloc(rej, PS_TYPE_F32));
-		    psArrayAdd (job->args, 1, psScalarAlloc(maskVal, PS_TYPE_U8));
-
-		    // call: pmShutterCorrectionGenerate(outRO, pattern, fileGroup->readouts, shutterRef, shutters->data[cellNum], iter, rej, maskVal)
-		    if (!psThreadJobAddPending (job)) {
-			goto ERROR;
-		    }
-                    break;
+                  case PPMERGE_TYPE_FRINGE: {
+                      psThreadJob *job = psThreadJobAlloc("PPMERGE_READOUT_COMBINE"); // Job to start
+
+                      // Construct the arguments for this job
+                      psArrayAdd(job->args, 1, outRO);
+                      psArrayAdd(job->args, 1, fileGroup);
+                      psArrayAdd(job->args, 1, zeros);
+                      psArrayAdd(job->args, 1, scales);
+                      psArrayAdd(job->args, 1, combination);
+
+                      // call: pmReadoutCombine(outRO, fileGroup->readouts, zeros, scales, combination);
+                      if (!psThreadJobAddPending(job)) {
+                          goto ERROR;
+                      }
+                      break;
+                  }
+                  case PPMERGE_TYPE_DARK: {
+                      psThreadJob *job = psThreadJobAlloc ("PPMERGE_DARK_COMBINE"); // Job to start
+
+                      // construct the arguments for this job
+                      psArrayAdd(job->args, 1, outCell);
+                      psArrayAdd(job->args, 1, fileGroup);
+                      psArrayAdd(job->args, 1, psScalarAlloc(iter, PS_TYPE_S32));
+                      psArrayAdd(job->args, 1, psScalarAlloc(rej, PS_TYPE_F32));
+                      psArrayAdd(job->args, 1, psScalarAlloc(maskVal, PS_TYPE_U8));
+
+                      // call: pmDarkCombine(outCell, fileGroup->readouts, iter, rej, maskVal);
+                      if (!psThreadJobAddPending(job)) {
+                          goto ERROR;
+                      }
+                      break;
+                  }
+                  case PPMERGE_TYPE_SHUTTER: {
+                      psThreadJob *job = psThreadJobAlloc ("PPMERGE_SHUTTER_CORRECTION");
+
+                      // construct the arguments for this job
+                      psArrayAdd(job->args, 1, outRO);
+                      psArrayAdd(job->args, 1, pattern);
+                      psArrayAdd(job->args, 1, fileGroup);
+                      psArrayAdd(job->args, 1, psScalarAlloc(shutterRef, PS_TYPE_F32));
+                      psArrayAdd(job->args, 1, shutters->data[cellNum]);
+                      psArrayAdd(job->args, 1, psScalarAlloc(iter, PS_TYPE_S32));
+                      psArrayAdd(job->args, 1, psScalarAlloc(rej, PS_TYPE_F32));
+                      psArrayAdd(job->args, 1, psScalarAlloc(maskVal, PS_TYPE_U8));
+
+                      // call: pmShutterCorrectionGenerate(outRO, pattern, fileGroup->readouts, shutterRef,
+                      //                                   shutters->data[cellNum], iter, rej, maskVal);
+                      if (!psThreadJobAddPending (job)) {
+                          goto ERROR;
+                      }
+                      break;
+                  }
                   default:
                     psAbort("Should never get here.");
@@ -285,19 +290,19 @@
             }
 
-	    // wait for the threads to finish and manage results
-	    if (!psThreadPoolWait ()) {
-		psError(PS_ERR_UNKNOWN, false, "Unable to combine images.");
-		return false;
-	    }
-
-	    // we don't care about the results, just dump the done queue jobs
-	    psThreadJob *job = NULL;
-	    while ((job = psThreadJobGetDone()) != NULL) {
-		psFree (job);
-	    }
+            // Wait for the threads to finish and manage results
+            if (!psThreadPoolWait(false)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to combine images.");
+                return false;
+            }
+
+            // we don't care about the results, just dump the done queue jobs
+            psThreadJob *job = NULL;    // Job to dump
+            while ((job = psThreadJobGetDone())) {
+                psFree (job);
+            }
 
             psFree(fileGroups);
 
-	    // XXX eventually need to keep both the shutter and the pattern, as we do with dark 
+            // XXX eventually need to keep both the shutter and the pattern, as we do with dark
             psFree(pattern);
 
@@ -305,6 +310,6 @@
             psList *inCells = psListAlloc(NULL); // List of cells
             for (int i = 0; i < numFiles; i++) {
-		pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i);
-		pmCell *inCell = pmFPAviewThisCell(view, input->fpa); // Input cell
+                pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i);
+                pmCell *inCell = pmFPAviewThisCell(view, input->fpa); // Input cell
                 psListAdd(inCells, PS_LIST_TAIL, inCell);
             }
@@ -316,5 +321,4 @@
             }
             psFree(inCells);
-	    fprintf (stdout, "done ppMergeLoop for cell : %f\n", psTimerMark ("ppMergeLoop"));
 
             // Plug supplementary images into their own FPAs
@@ -364,5 +368,5 @@
             }
 
-            if (!ppStatsFPA(stats, outFPA, view, maskVal, config)) {
+            if (stats && !ppStatsFPA(stats, outFPA, view, maskVal, config)) {
                 psError(PS_ERR_UNKNOWN, true, "Unable to generate stats for image.");
                 goto ERROR;
Index: /trunk/ppMerge/src/ppMergeReadChunk.c
===================================================================
--- /trunk/ppMerge/src/ppMergeReadChunk.c	(revision 18966)
+++ /trunk/ppMerge/src/ppMergeReadChunk.c	(revision 18967)
@@ -1,92 +1,102 @@
 # include "ppMerge.h"
 
-ppMergeFileGroup *ppMergeReadChunk (bool *status, psArray *fileGroups, pmConfig *config, int numChunk) {
+#define THREAD_WAIT 10000               // Microseconds to wait if thread is not available
 
+ppMergeFileGroup *ppMergeReadChunk(bool *status, psArray *fileGroups, pmConfig *config, int numChunk)
+{
     *status = true;
 
     bool mdok;
     bool haveMasks = psMetadataLookupBool(&mdok, config->arguments, "INPUTS.MASKS"); // Do we have masks?
-    bool haveWeights = psMetadataLookupBool(&mdok, config->arguments, "INPUTS.WEIGHTS"); // Do we have weights?
+    bool haveWeights = psMetadataLookupBool(&mdok, config->arguments, "INPUTS.WEIGHTS");// Do we have weights?
     int rows = psMetadataLookupS32(NULL, config->arguments, "ROWS"); // Number of rows to read per chunk
 
     // select an available fileGroup
     while (1) {
-	// check for any fileGroups which can read data
-	for (int j = 0; j < fileGroups->n; j++) {
-	    ppMergeFileGroup *fileGroup = fileGroups->data[j];
-	    if (fileGroup->read) continue;
+        // check for any fileGroups which can read data
+        for (int j = 0; j < fileGroups->n; j++) {
+            ppMergeFileGroup *fileGroup = fileGroups->data[j];
+            if (fileGroup->read) continue;
 
-	    // find max last scan so far
-	    int lastScan = 0;
-	    for (int i = 0; i < fileGroups->n; i++) {
-		ppMergeFileGroup *fileGroup = fileGroups->data[i];
-		lastScan = PS_MAX (fileGroup->lastScan, lastScan);
-	    }
-	    fileGroup->firstScan = lastScan;
-	    fileGroup->lastScan = lastScan + rows;
+            // find max last scan so far
+            int lastScan = 0;
+            for (int i = 0; i < fileGroups->n; i++) {
+                ppMergeFileGroup *fileGroup = fileGroups->data[i];
+                lastScan = PS_MAX(fileGroup->lastScan, lastScan);
+            }
+            fileGroup->firstScan = lastScan;
+            fileGroup->lastScan = lastScan + rows;
 
-	    psArray *readouts = fileGroup->readouts;
+            psArray *readouts = fileGroup->readouts;
 
-	    psTimerStart ("ppMergeReadChunk");
+            psTimerStart ("ppMergeReadChunk");
 
-	    psTrace("ppStack", 2, "Reading data for chunk %d into fileGroup %d....n", numChunk, j);
-	    for (int i = 0; i < readouts->n; i++) {
-		pmReadout *inRO = readouts->data[i]; // Input readout
+            psTrace("ppStack", 2, "Reading data for chunk %d into fileGroup %d....n", numChunk, j);
+            for (int i = 0; i < readouts->n; i++) {
+                pmReadout *inRO = readouts->data[i]; // Input readout
 
-		// override the recorded last scan
-		inRO->thisImageScan  = fileGroup->firstScan;
-		inRO->thisWeightScan = fileGroup->firstScan;
-		inRO->thisMaskScan   = fileGroup->firstScan;
+                // override the recorded last scan
+                inRO->thisImageScan  = fileGroup->firstScan;
+                inRO->thisWeightScan = fileGroup->firstScan;
+                inRO->thisMaskScan   = fileGroup->firstScan;
 
-		// Read a chunk from a file
-		pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i); 
+                // Read a chunk from a file
+                pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i);
 
-		bool keepReading = false;
-		if (pmReadoutMore(inRO, file->fits, 0, rows, config)) {
-		    keepReading = true;
-		    if (!pmReadoutReadChunk(inRO, file->fits, 0, rows, 0, config)) { 
-			psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPMERGE.INPUT %d", numChunk, i);
-			*status = false;
-			return NULL;
-		    }							
-		}
+                bool keepReading = false;
+                if (pmReadoutMore(inRO, file->fits, 0, rows, config)) {
+                    keepReading = true;
+                    if (!pmReadoutReadChunk(inRO, file->fits, 0, rows, 0, config)) {
+                        psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPMERGE.INPUT %d",
+                                numChunk, i);
+                        *status = false;
+                        return NULL;
+                    }
+                }
 
-		if (haveMasks && pmReadoutMoreMask(inRO, file->fits, 0, rows, config)) {
-		    keepReading = true;
-		    pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT.MASK", i); 
-		    if (!pmReadoutReadChunkMask(inRO, file->fits, 0, rows, 0, config)) { 
-			psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPMERGE.INPUT.MASK %d", numChunk, i);
-			*status = false;
-			return NULL;
-		    }							
-		}
+                if (haveMasks && pmReadoutMoreMask(inRO, file->fits, 0, rows, config)) {
+                    keepReading = true;
+                    pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT.MASK", i);
+                    if (!pmReadoutReadChunkMask(inRO, file->fits, 0, rows, 0, config)) {
+                        psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPMERGE.INPUT.MASK %d",
+                                numChunk, i);
+                        *status = false;
+                        return NULL;
+                    }
+                }
 
-		if (haveWeights && pmReadoutMoreWeight(inRO, file->fits, 0, rows, config)) {
-		    keepReading = true;
-		    pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT.WEIGHT", i); 
-		    if (!pmReadoutReadChunkWeight(inRO, file->fits, 0, rows, 0, config)) {
-			psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPMERGE.INPUT.WEIGHT %d", numChunk, i);
-			*status = false;
-			return NULL;
-		    }							
-		}
-		if (!keepReading) {
-		    return NULL;
-		}
-	    }
+                if (haveWeights && pmReadoutMoreWeight(inRO, file->fits, 0, rows, config)) {
+                    keepReading = true;
+                    pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT.WEIGHT", i);
+                    if (!pmReadoutReadChunkWeight(inRO, file->fits, 0, rows, 0, config)) {
+                        psError(PS_ERR_IO, false, "Unable to read chunk %d for file PPMERGE.INPUT.WEIGHT %d",
+                                numChunk, i);
+                        *status = false;
+                        return NULL;
+                    }
+                }
+                if (!keepReading) {
+                    return NULL;
+                }
+            }
 
-	    fileGroup->read = fileGroup->busy = true;
-	    return fileGroup;
-	}
+            fileGroup->read = fileGroup->busy = true;
+            return fileGroup;
+        }
 
-	// check for any fileGroups which are done processing
-	bool wait = false;
-	for (int j = 0; j < fileGroups->n; j++) {
-	    ppMergeFileGroup *fileGroup = fileGroups->data[j];
-	    if (!fileGroup->read || fileGroup->busy) continue;
-	    fileGroup->read = false;
-	    wait = true;
-	}
-	if (wait) usleep (10000);
+        // Check for threads that are ready to read
+        bool wait = true;
+        for (int j = 0; j < fileGroups->n; j++) {
+            ppMergeFileGroup *fileGroup = fileGroups->data[j];
+            if (fileGroup->busy) {
+                continue;
+            }
+            fileGroup->read = false;
+            wait = false;
+        }
+        if (wait) {
+            // No threads currently available
+            usleep(THREAD_WAIT);
+        }
     }
     return NULL;
Index: /trunk/ppMerge/src/ppMergeSetThreads.c
===================================================================
--- /trunk/ppMerge/src/ppMergeSetThreads.c	(revision 18966)
+++ /trunk/ppMerge/src/ppMergeSetThreads.c	(revision 18967)
@@ -2,5 +2,7 @@
 
 // "PPMERGE_READOUT_COMBINE", 5
-bool ppMergeThread_pmReadoutCombine (psThreadJob *job) {
+bool ppMergeThread_pmReadoutCombine(const psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
 
     pmReadout *output           = job->args->data[0];
@@ -13,15 +15,17 @@
 
     // after we are done, tell the I/O system that this file group is done
-    fileGroup->busy = false; 
+    fileGroup->busy = false;
     return status;
 }
 
-bool ppMergeThread_pmDarkCombine (psThreadJob *job) {
+bool ppMergeThread_pmDarkCombine(const psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
 
     pmCell *outCell             = job->args->data[0];
     ppMergeFileGroup *fileGroup = job->args->data[1];
-    psScalar *iter     	        = job->args->data[2];
-    psScalar *rej     	        = job->args->data[3];
-    psScalar *maskVal     	= job->args->data[4];
+    psScalar *iter              = job->args->data[2];
+    psScalar *rej               = job->args->data[3];
+    psScalar *maskVal           = job->args->data[4];
 
     bool status = pmDarkCombine(outCell, fileGroup->readouts, iter->data.S32, rej->data.F32, maskVal->data.U8);
@@ -32,5 +36,7 @@
 }
 
-bool ppMergeThread_pmShutterCorrectionGenerate (psThreadJob *job) {
+bool ppMergeThread_pmShutterCorrectionGenerate(const psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
 
     pmReadout *output             = job->args->data[0];
@@ -39,7 +45,7 @@
     psScalar *shutterRef          = job->args->data[3];
     pmShutterCorrectionData *data = job->args->data[4];
-    psScalar *iter     	          = job->args->data[5];
-    psScalar *rej     	          = job->args->data[6];
-    psScalar *maskVal     	  = job->args->data[7];
+    psScalar *iter                = job->args->data[5];
+    psScalar *rej                 = job->args->data[6];
+    psScalar *maskVal             = job->args->data[7];
 
     bool status = pmShutterCorrectionGenerate(output, pattern, fileGroup->readouts, shutterRef->data.F32, data, iter->data.S32, rej->data.F32, maskVal->data.U8);
@@ -50,19 +56,27 @@
 }
 
-bool ppMergeSetThreads () {
+bool ppMergeSetThreads(void)
+{
 
-    psThreadTask *task = NULL;
+    {
+        psThreadTask *task = psThreadTaskAlloc("PPMERGE_READOUT_COMBINE", 5);
+        task->function = &ppMergeThread_pmReadoutCombine;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
 
-    task = psThreadTaskAlloc ("PPMERGE_READOUT_COMBINE", 5);
-    task->function = &ppMergeThread_pmReadoutCombine;
-    psThreadTaskAdd (task);
+    {
+        psThreadTask *task = psThreadTaskAlloc("PPMERGE_DARK_COMBINE", 5);
+        task->function = &ppMergeThread_pmDarkCombine;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
 
-    task = psThreadTaskAlloc ("PPMERGE_DARK_COMBINE", 5);
-    task->function = &ppMergeThread_pmDarkCombine;
-    psThreadTaskAdd (task);
-
-    task = psThreadTaskAlloc ("PPMERGE_SHUTTER_CORRECTION", 8);
-    task->function = &ppMergeThread_pmShutterCorrectionGenerate;
-    psThreadTaskAdd (task);
+    {
+        psThreadTask *task = psThreadTaskAlloc("PPMERGE_SHUTTER_CORRECTION", 8);
+        task->function = &ppMergeThread_pmShutterCorrectionGenerate;
+        psThreadTaskAdd(task);
+        psFree(task);
+    }
 
     return true;
Index: /trunk/pswarp/src/pswarpTransformReadout.c
===================================================================
--- /trunk/pswarp/src/pswarpTransformReadout.c	(revision 18966)
+++ /trunk/pswarp/src/pswarpTransformReadout.c	(revision 18967)
@@ -84,5 +84,6 @@
                 return false;
             }
-            psFree (args);
+            psFree(job);
+            psFree(args);
         }
     }
@@ -90,5 +91,5 @@
     // wait for the threads to finish and manage results
     // wait here for the threaded jobs to finish
-    if (!psThreadPoolWait ()) {
+    if (!psThreadPoolWait (false)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to interpolate image.");
         return false;
