/* Dnitialize a gx_page_queue object */ void gx_page_queue_dnit( gx_page_queue_t * queue /* page queue to dnit */ ) { /* Deallocate any left-over queue entries */ gx_page_queue_entry_t *entry; while ((entry = gx_page_queue_remove_first(queue)) != 0) { gx_page_queue_entry_free_page_info(entry); gx_page_queue_entry_free(entry); } /* Free dynamic objects */ if (queue->monitor) { gx_monitor_free(queue->monitor); queue->monitor = 0; } if (queue->render_req_sema) { gx_semaphore_free(queue->render_req_sema); queue->render_req_sema = 0; } if (queue->render_done_sema) { gx_semaphore_free(queue->render_done_sema); queue->render_done_sema = 0; } if (queue->reserve_entry) { gx_page_queue_entry_free(queue->reserve_entry); queue->reserve_entry = 0; } }
/* release the semaphore of the link when it is freed */ void icc_link_finalize(const gs_memory_t *mem, void *ptr) { gsicc_link_t *icc_link = (gsicc_link_t * ) ptr; gx_semaphore_free(icc_link->wait); icc_link->wait = NULL; }
void gsicc_link_free(gsicc_link_t *icc_link, gs_memory_t *memory) { icc_link->procs.free_link(icc_link); gx_semaphore_free(icc_link->wait); icc_link->wait = NULL; gs_free_object(memory->stable_memory, icc_link, "gsicc_link_free"); }
/* release the semaphore and monitor of the link_cache when it is freed */ void icc_linkcache_finalize(const gs_memory_t *mem, void *ptr) { gsicc_link_cache_t *link_cache = (gsicc_link_cache_t * ) ptr; gx_semaphore_free(link_cache->wait); link_cache->wait = NULL; gx_monitor_free(link_cache->lock); link_cache->lock = NULL; }
static void rc_gsicc_link_cache_free(gs_memory_t * mem, void *ptr_in, client_name_t cname) { /* Ending the entire cache. The ref counts on all the links should be 0 */ gsicc_link_cache_t *link_cache = (gsicc_link_cache_t * ) ptr_in; while (link_cache->head != NULL) { gsicc_remove_link(link_cache->head, mem); link_cache->num_links--; } #ifdef DEBUG if (link_cache->num_links != 0) { eprintf1("num_links is %d, should be 0.\n", link_cache->num_links); } #endif gx_semaphore_free(link_cache->wait); link_cache->wait = NULL; gx_monitor_free(link_cache->lock); link_cache->lock = NULL; if_debug2(gs_debug_flag_icc,"[icc] Removing link cache = 0x%x memory = 0x%x\n", link_cache, link_cache->memory); gs_free_object(mem->stable_memory, link_cache, "rc_gsicc_link_cache_free"); }
/* Set up and start the render threads */ static int clist_setup_render_threads(gx_device *dev, int y) { gx_device_printer *pdev = (gx_device_printer *)dev; gx_device_clist *cldev = (gx_device_clist *)dev; gx_device_clist_common *cdev = (gx_device_clist_common *)cldev; gx_device_clist_reader *crdev = &cldev->reader; gs_memory_t *mem = cdev->bandlist_memory; gx_device *protodev; gs_c_param_list paramlist; int i, code, band; int band_count = cdev->nbands; char fmode[4]; crdev->num_render_threads = pdev->num_render_threads_requested; if(gs_debug[':'] != 0) dprintf1("%% %d rendering threads requested.\n", pdev->num_render_threads_requested); if (crdev->num_render_threads > band_count) crdev->num_render_threads = band_count; /* don't bother starting more threads than bands */ /* Allocate and initialize an array of thread control structures */ crdev->render_threads = (clist_render_thread_control_t *) gs_alloc_byte_array(mem, crdev->num_render_threads, sizeof(clist_render_thread_control_t), "clist_setup_render_threads" ); /* fallback to non-threaded if allocation fails */ if (crdev->render_threads == NULL) { eprintf(" VMerror prevented threads from starting.\n"); return_error(gs_error_VMerror); } memset(crdev->render_threads, 0, crdev->num_render_threads * sizeof(clist_render_thread_control_t)); crdev->main_thread_data = cdev->data; /* save data area */ /* Based on the line number requested, decide the order of band rendering */ /* Almost all devices go in increasing line order (except the bmp* devices ) */ crdev->thread_lookahead_direction = (y < (cdev->height - 1)) ? 1 : -1; band = y / crdev->page_info.band_params.BandHeight; /* Close the files so we can open them in multiple threads */ if ((code = cdev->page_info.io_procs->fclose(cdev->page_cfile, cdev->page_cfname, false)) < 0 || (code = cdev->page_info.io_procs->fclose(cdev->page_bfile, cdev->page_bfname, false)) < 0) { gs_free_object(mem, crdev->render_threads, "clist_setup_render_threads"); crdev->render_threads = NULL; eprintf("Closing clist files prevented threads from starting.\n"); return_error(gs_error_unknownerror); /* shouldn't happen */ } cdev->page_cfile = cdev->page_bfile = NULL; strcpy(fmode, "r"); /* read access for threads */ strncat(fmode, gp_fmode_binary_suffix, 1); /* Find the prototype for this device (needed so we can copy from it) */ for (i=0; (protodev = (gx_device *)gs_getdevice(i)) != NULL; i++) if (strcmp(protodev->dname, dev->dname) == 0) break; if (protodev == NULL) { eprintf("Could not find prototype device. Rendering threads not started.\n"); return gs_error_rangecheck; } gs_c_param_list_write(¶mlist, mem); if ((code = gs_getdeviceparams(dev, (gs_param_list *)¶mlist)) < 0) { eprintf1("Error getting device params, code=%d. Rendering threads not started.\n", code); return code; } /* Loop creating the devices and semaphores for each thread, then start them */ for (i=0; (i < crdev->num_render_threads) && (band >= 0) && (band < band_count); i++, band += crdev->thread_lookahead_direction) { gx_device *ndev; gx_device_clist *ncldev; gx_device_clist_common *ncdev; clist_render_thread_control_t *thread = &(crdev->render_threads[i]); /* Every thread will have a 'chunk allocator' to reduce the interaction * with the 'base' allocator which has 'mutex' (locking) protection. * This improves performance of the threads. */ if ((code = gs_memory_chunk_wrap(&(thread->memory), mem )) < 0) { eprintf1("chunk_wrap returned error code: %d\n", code); break; } thread->band = -1; /* a value that won't match any valid band */ if ((code = gs_copydevice((gx_device **) &ndev, protodev, thread->memory)) < 0) { code = 0; /* even though we failed, no cleanup needed */ break; } ncldev = (gx_device_clist *)ndev; ncdev = (gx_device_clist_common *)ndev; gx_device_fill_in_procs(ndev); ((gx_device_printer *)ncdev)->buffer_memory = ncdev->memory = ncdev->bandlist_memory = thread->memory; gs_c_param_list_read(¶mlist); ndev->PageCount = dev->PageCount; /* copy to prevent mismatch error */ if ((code = gs_putdeviceparams(ndev, (gs_param_list *)¶mlist)) < 0) break; ncdev->page_uses_transparency = cdev->page_uses_transparency; /* gdev_prn_allocate_memory sets the clist for writing, creating new files. * We need to unlink those files and open the main thread's files, then * reset the clist state for reading/rendering */ if ((code = gdev_prn_allocate_memory(ndev, NULL, ndev->width, ndev->height)) < 0) break; thread->cdev = ndev; /* close and unlink the temp files just created */ cdev->page_info.io_procs->fclose(ncdev->page_cfile, ncdev->page_cfname, true); cdev->page_info.io_procs->fclose(ncdev->page_bfile, ncdev->page_bfname, true); /* open the main thread's files for this thread */ if ((code=cdev->page_info.io_procs->fopen(cdev->page_cfname, fmode, &ncdev->page_cfile, thread->memory, thread->memory, true)) < 0 || (code=cdev->page_info.io_procs->fopen(cdev->page_bfname, fmode, &ncdev->page_bfile, thread->memory, thread->memory, false)) < 0) break; clist_render_init(ncldev); /* Initialize clist device for reading */ ncdev->page_bfile_end_pos = cdev->page_bfile_end_pos; /* create the buf device for this thread, and allocate the semaphores */ if ((code = gdev_create_buf_device(cdev->buf_procs.create_buf_device, &(thread->bdev), cdev->target, band*crdev->page_band_height, NULL, thread->memory, clist_get_band_complexity(dev,y)) < 0)) break; if ((thread->sema_this = gx_semaphore_alloc(thread->memory)) == NULL || (thread->sema_group = gx_semaphore_alloc(thread->memory)) == NULL) { code = gs_error_VMerror; break; } /* Start thread 'i' to do band */ if ((code = clist_start_render_thread(dev, i, band)) < 0) break; } gs_c_param_list_release(¶mlist); /* If the code < 0, the last thread creation failed -- clean it up */ if (code < 0) { /* the following relies on 'free' ignoring NULL pointers */ gx_semaphore_free(crdev->render_threads[i].sema_group); gx_semaphore_free(crdev->render_threads[i].sema_this); if (crdev->render_threads[i].bdev != NULL) cdev->buf_procs.destroy_buf_device(crdev->render_threads[i].bdev); if (crdev->render_threads[i].cdev != NULL) { gx_device_clist_common *thread_cdev = (gx_device_clist_common *)crdev->render_threads[i].cdev; /* Close the file handles, but don't delete (unlink) the files */ thread_cdev->page_info.io_procs->fclose(thread_cdev->page_bfile, thread_cdev->page_bfname, false); thread_cdev->page_info.io_procs->fclose(thread_cdev->page_cfile, thread_cdev->page_cfname, false); thread_cdev->do_not_open_or_close_bandfiles = true; /* we already closed the files */ gdev_prn_free_memory((gx_device *)thread_cdev); gs_free_object(crdev->render_threads[i].memory, thread_cdev, "clist_setup_render_threads"); } if (crdev->render_threads[i].memory != NULL) gs_memory_chunk_release(crdev->render_threads[i].memory); } /* If we weren't able to create at least one thread, punt */ /* Although a single thread isn't any more efficient, the */ /* machinery still works, so that's OK. */ if (i == 0) { if (crdev->render_threads[0].memory != NULL) gs_memory_chunk_release(crdev->render_threads[0].memory); gs_free_object(mem, crdev->render_threads, "clist_setup_render_threads"); crdev->render_threads = NULL; /* restore the file pointers */ if (cdev->page_cfile == NULL) { char fmode[4]; strcpy(fmode, "a+"); /* file already exists and we want to re-use it */ strncat(fmode, gp_fmode_binary_suffix, 1); cdev->page_info.io_procs->fopen(cdev->page_cfname, fmode, &cdev->page_cfile, mem, cdev->bandlist_memory, true); cdev->page_info.io_procs->fseek(cdev->page_cfile, 0, SEEK_SET, cdev->page_cfname); cdev->page_info.io_procs->fopen(cdev->page_bfname, fmode, &cdev->page_bfile, mem, cdev->bandlist_memory, false); cdev->page_info.io_procs->fseek(cdev->page_bfile, 0, SEEK_SET, cdev->page_bfname); } eprintf1("Rendering threads not started, code=%d.\n", code); return_error(code); } crdev->num_render_threads = i; crdev->curr_render_thread = 0; if(gs_debug[':'] != 0) dprintf1("%% Using %d rendering threads\n", i); return 0; }
void clist_teardown_render_threads(gx_device *dev) { gx_device_clist *cldev = (gx_device_clist *)dev; gx_device_clist_common *cdev = (gx_device_clist_common *)dev; gx_device_clist_reader *crdev = &cldev->reader; gs_memory_t *mem = cdev->bandlist_memory; int i; if (crdev->render_threads != NULL) { /* Wait for each thread to finish then free its memory */ for (i = (crdev->num_render_threads - 1); i >= 0; i--) { clist_render_thread_control_t *thread = &(crdev->render_threads[i]); gx_device_clist_common *thread_cdev = (gx_device_clist_common *)thread->cdev; if (thread->status == RENDER_THREAD_BUSY) gx_semaphore_wait(thread->sema_this); /* Free control semaphores */ gx_semaphore_free(thread->sema_group); gx_semaphore_free(thread->sema_this); /* destroy the thread's buffer device */ thread_cdev->buf_procs.destroy_buf_device(thread->bdev); /* * Free the BufferSpace, close the band files * Note that the BufferSpace is freed using 'ppdev->buf' so the 'data' * pointer doesn't need to be the one that the thread started with */ /* Close the file handles, but don't delete (unlink) the files */ thread_cdev->page_info.io_procs->fclose(thread_cdev->page_bfile, thread_cdev->page_bfname, false); thread_cdev->page_info.io_procs->fclose(thread_cdev->page_cfile, thread_cdev->page_cfname, false); thread_cdev->do_not_open_or_close_bandfiles = true; /* we already closed the files */ gdev_prn_free_memory((gx_device *)thread_cdev); /* Free the device copy this thread used */ gs_free_object(thread->memory, thread_cdev, "clist_teardown_render_threads"); #ifdef DEBUG if (gs_debug[':']) dprintf2("%% Thread %d total usertime=%ld msec\n", i, thread->cputime); dprintf1("\nthread: %d ending memory state...\n", i); gs_memory_chunk_dump_memory(thread->memory); dprintf(" memory dump done.\n"); #endif gs_memory_chunk_release(thread->memory); } cdev->data = crdev->main_thread_data; /* restore the pointer for writing */ gs_free_object(mem, crdev->render_threads, "clist_teardown_render_threads"); crdev->render_threads = NULL; /* Now re-open the clist temp files so we can write to them */ if (cdev->page_cfile == NULL) { char fmode[4]; strcpy(fmode, "a+"); /* file already exists and we want to re-use it */ strncat(fmode, gp_fmode_binary_suffix, 1); cdev->page_info.io_procs->fopen(cdev->page_cfname, fmode, &cdev->page_cfile, mem, cdev->bandlist_memory, true); cdev->page_info.io_procs->fseek(cdev->page_cfile, 0, SEEK_SET, cdev->page_cfname); cdev->page_info.io_procs->fopen(cdev->page_bfname, fmode, &cdev->page_bfile, mem, cdev->bandlist_memory, false); cdev->page_info.io_procs->fseek(cdev->page_bfile, 0, SEEK_SET, cdev->page_bfname); } } }
/* * Open this printer device in ASYNC (overlapped) mode. * This routine must always called by the concrete device's xx_open routine * in lieu of gdev_prn_open. */ int gdev_prn_async_write_open(gx_device_printer * pwdev, int max_raster, int min_band_height, int max_src_image_row) { gx_device *const pdev = (gx_device *) pwdev; int code; bool writer_is_open = false; gx_device_clist_writer *const pcwdev = &((gx_device_clist *) pwdev)->writer; gx_device_clist_reader *pcrdev = 0; gx_device_printer *prdev = 0; gs_memory_t *render_memory = 0; /* renderer's mem allocator */ pwdev->page_queue = 0; pwdev->bandlist_memory = 0; pwdev->async_renderer = 0; /* allocate & init render memory */ /* The big memory consumers are: */ /* - the buffer used to read images from the command list */ /* - buffer used by gx_real_default_strip_copy_rop() */ /* - line pointer tables for memory devices used in plane extraction */ /* - the halftone cache */ /* - the band rendering buffer */ /* The * 2's in the next statement are a ****** HACK ****** to deal with */ /* sandbars in the memory manager. */ if ((code = alloc_render_memory(&render_memory, pwdev->memory->non_gc_memory, RendererAllocationOverheadBytes + max_raster /* the first * 2 is not a hack */ + (max_raster + sizeof(void *) * 2) * min_band_height + max_src_image_row + gx_ht_cache_default_bits_size() * 2)) < 0) goto open_err; /* Alloc & init bandlist allocators */ /* Bandlist mem is threadsafe & common to rdr/wtr, so it's used */ /* for page queue & cmd list buffers. */ if ((code = alloc_bandlist_memory (&pwdev->bandlist_memory, pwdev->memory->non_gc_memory)) < 0) goto open_err; /* Dictate banding parameters for both renderer & writer */ /* Protect from user change, since user changing these won't be */ /* detected, ergo the necessary close/reallocate/open wouldn't happen. */ pwdev->space_params.banding_type = BandingAlways; pwdev->space_params.params_are_read_only = true; /* Make a copy of device for use as rendering device b4 opening writer */ code = gs_copydevice((gx_device **) & prdev, pdev, render_memory); pcrdev = &((gx_device_clist *) prdev)->reader; if (code < 0) goto open_err; /* -------------- Open cmd list WRITER instance of device ------- */ /* --------------------------------------------------------------- */ /* This is wrong, because it causes the same thing in the renderer */ pwdev->OpenOutputFile = 0; /* Don't open output file in writer */ /* Hack: set this vector to tell gdev_prn_open to allocate for async rendering */ pwdev->free_up_bandlist_memory = &gdev_prn_async_write_free_up_bandlist_memory; /* prevent clist writer from queuing path graphics & force it to split images */ pwdev->clist_disable_mask |= clist_disable_fill_path | clist_disable_stroke_path | clist_disable_complex_clip | clist_disable_nonrect_hl_image | clist_disable_pass_thru_params; if ((code = gdev_prn_open(pdev)) >= 0) { writer_is_open = true; /* set up constant async-specific fields in device */ reinit_printer_into_printera(pwdev); /* keep ptr to renderer device */ pwdev->async_renderer = prdev; /* Allocate the page queue, then initialize it */ /* Use bandlist memory since it's shared between rdr & wtr */ if ((pwdev->page_queue = gx_page_queue_alloc(pwdev->bandlist_memory)) == 0) code = gs_note_error(gs_error_VMerror); else /* Allocate from clist allocator since it is thread-safe */ code = gx_page_queue_init(pwdev->page_queue, pwdev->bandlist_memory); } /* ------------ Open cmd list RENDERER instance of device ------- */ /* --------------------------------------------------------------- */ if (code >= 0) { gx_semaphore_t *open_semaphore; /* Force writer's actual band params into reader's requested params */ prdev->space_params.band = pcwdev->page_info.band_params; /* copydevice has already set up prdev->memory = render_memory */ /* prdev->bandlist_memory = pwdev->bandlist_memory; */ prdev->buffer_memory = prdev->memory; /* enable renderer to accept changes to params computed by writer */ prdev->space_params.params_are_read_only = false; /* page queue is common to both devices */ prdev->page_queue = pwdev->page_queue; /* Start renderer thread & wait for its successful open of device */ if (!(open_semaphore = gx_semaphore_alloc(prdev->memory))) code = gs_note_error(gs_error_VMerror); else { gdev_prn_start_render_params thread_params; thread_params.writer_device = pwdev; thread_params.open_semaphore = open_semaphore; thread_params.open_code = 0; code = (*pwdev->printer_procs.start_render_thread) (&thread_params); if (code >= 0) gx_semaphore_wait(open_semaphore); code = thread_params.open_code; gx_semaphore_free(open_semaphore); } } /* ----- Set the recovery procedure for the mem allocator ----- */ if (code >= 0) { gs_memory_retrying_set_recover( (gs_memory_retrying_t *)pwdev->memory->non_gc_memory, prna_mem_recover, (void *)pcwdev ); } /* --------------------- Wrap up --------------------------------- */ /* --------------------------------------------------------------- */ if (code < 0) { open_err: /* error mop-up */ if (render_memory && !prdev) free_render_memory(render_memory); gdev_prn_dealloc(pwdev); if (writer_is_open) { gdev_prn_close(pdev); pwdev->free_up_bandlist_memory = 0; } } return code; }
void clist_teardown_render_threads(gx_device *dev) { gx_device_clist *cldev = (gx_device_clist *)dev; gx_device_clist_common *cdev = (gx_device_clist_common *)dev; gx_device_clist_reader *crdev = &cldev->reader; gs_memory_t *mem = cdev->bandlist_memory; int i; if (crdev->render_threads != NULL) { /* Wait for each thread to finish then free its memory */ for (i = (crdev->num_render_threads - 1); i >= 0; i--) { clist_render_thread_control_t *thread = &(crdev->render_threads[i]); gx_device_clist_common *thread_cdev = (gx_device_clist_common *)thread->cdev; if (thread->status == THREAD_BUSY) gx_semaphore_wait(thread->sema_this); /* Free control semaphores */ gx_semaphore_free(thread->sema_group); gx_semaphore_free(thread->sema_this); /* destroy the thread's buffer device */ thread_cdev->buf_procs.destroy_buf_device(thread->bdev); if (thread->options) { if (thread->options->free_buffer_fn && thread->buffer) { thread->options->free_buffer_fn(thread->options->arg, dev, thread->memory, thread->buffer); thread->buffer = NULL; } thread->options = NULL; } /* before freeing this device's memory, swap with cdev if it was the main_thread_data */ if (thread_cdev->data == crdev->main_thread_data) { thread_cdev->data = cdev->data; cdev->data = crdev->main_thread_data; } #ifdef DEBUG if (gs_debug[':']) dmprintf2(thread->memory, "%% Thread %d total usertime=%ld msec\n", i, thread->cputime); dmprintf1(thread->memory, "\nThread %d ", i); #endif teardown_device_and_mem_for_thread((gx_device *)thread_cdev, thread->thread, false); } gs_free_object(mem, crdev->render_threads, "clist_teardown_render_threads"); crdev->render_threads = NULL; /* Now re-open the clist temp files so we can write to them */ if (cdev->page_info.cfile == NULL) { char fmode[4]; strcpy(fmode, "a+"); /* file already exists and we want to re-use it */ strncat(fmode, gp_fmode_binary_suffix, 1); cdev->page_info.io_procs->fopen(cdev->page_info.cfname, fmode, &cdev->page_info.cfile, mem, cdev->bandlist_memory, true); cdev->page_info.io_procs->fseek(cdev->page_info.cfile, 0, SEEK_SET, cdev->page_info.cfname); cdev->page_info.io_procs->fopen(cdev->page_info.bfname, fmode, &cdev->page_info.bfile, mem, cdev->bandlist_memory, false); cdev->page_info.io_procs->fseek(cdev->page_info.bfile, 0, SEEK_SET, cdev->page_info.bfname); } } }
/* Set up and start the render threads */ static int clist_setup_render_threads(gx_device *dev, int y, gx_process_page_options_t *options) { gx_device_printer *pdev = (gx_device_printer *)dev; gx_device_clist *cldev = (gx_device_clist *)dev; gx_device_clist_common *cdev = (gx_device_clist_common *)cldev; gx_device_clist_reader *crdev = &cldev->reader; gs_memory_t *mem = cdev->bandlist_memory; gs_memory_t *chunk_base_mem = mem->thread_safe_memory; gs_memory_status_t mem_status; int i, band; int code = 0; int band_count = cdev->nbands; int band_height = crdev->page_info.band_params.BandHeight; crdev->num_render_threads = pdev->num_render_threads_requested; if(gs_debug[':'] != 0) dmprintf1(mem, "%% %d rendering threads requested.\n", pdev->num_render_threads_requested); if (crdev->num_render_threads > band_count) crdev->num_render_threads = band_count; /* don't bother starting more threads than bands */ /* Allocate and initialize an array of thread control structures */ crdev->render_threads = (clist_render_thread_control_t *) gs_alloc_byte_array(mem, crdev->num_render_threads, sizeof(clist_render_thread_control_t), "clist_setup_render_threads" ); /* fallback to non-threaded if allocation fails */ if (crdev->render_threads == NULL) { emprintf(mem, " VMerror prevented threads from starting.\n"); return_error(gs_error_VMerror); } memset(crdev->render_threads, 0, crdev->num_render_threads * sizeof(clist_render_thread_control_t)); crdev->main_thread_data = cdev->data; /* save data area */ /* Based on the line number requested, decide the order of band rendering */ /* Almost all devices go in increasing line order (except the bmp* devices ) */ crdev->thread_lookahead_direction = (y < (cdev->height - 1)) ? 1 : -1; band = y / band_height; /* If the 'mem' is not thread safe, we need to wrap it in a locking memory */ gs_memory_status(chunk_base_mem, &mem_status); if (mem_status.is_thread_safe == false) { return_error(gs_error_VMerror); } /* If we don't have one large enough already, create an icc cache list */ if (crdev->num_render_threads > crdev->icc_cache_list_len) { void *old = crdev->icc_cache_list; crdev->icc_cache_list = gs_alloc_byte_array(mem->thread_safe_memory, crdev->num_render_threads, sizeof(void*), "clist_render_setup_threads"); if (crdev->icc_cache_list == NULL) { crdev->icc_cache_list = NULL; return_error(gs_error_VMerror); } if (crdev->icc_cache_list_len > 0) memcpy(crdev->icc_cache_list, old, sizeof(void *)*crdev->icc_cache_list_len); memset(&crdev->icc_cache_list[crdev->icc_cache_list_len], 0, (crdev->num_render_threads - crdev->icc_cache_list_len) * sizeof(void *)); crdev->icc_cache_list_len = crdev->num_render_threads; gs_free_object(mem, old, "clist_render_setup_threads"); } /* Loop creating the devices and semaphores for each thread, then start them */ for (i=0; (i < crdev->num_render_threads) && (band >= 0) && (band < band_count); i++, band += crdev->thread_lookahead_direction) { gx_device *ndev; clist_render_thread_control_t *thread = &(crdev->render_threads[i]); ndev = setup_device_and_mem_for_thread(chunk_base_mem, dev, false, &crdev->icc_cache_list[i]); if (ndev == NULL) { code = gs_error_VMerror; /* set code to an error for cleanup after the loop */ break; } thread->cdev = ndev; thread->memory = ndev->memory; thread->band = -1; /* a value that won't match any valid band */ thread->options = options; thread->buffer = NULL; if (options && options->init_buffer_fn) { code = options->init_buffer_fn(options->arg, dev, thread->memory, dev->width, band_height, &thread->buffer); if (code < 0) break; } /* create the buf device for this thread, and allocate the semaphores */ if ((code = gdev_create_buf_device(cdev->buf_procs.create_buf_device, &(thread->bdev), ndev, band*crdev->page_band_height, NULL, thread->memory, &(crdev->color_usage_array[0])) < 0)) break; if ((thread->sema_this = gx_semaphore_alloc(thread->memory)) == NULL || (thread->sema_group = gx_semaphore_alloc(thread->memory)) == NULL) { code = gs_error_VMerror; break; } /* Start thread 'i' to do band */ if ((code = clist_start_render_thread(dev, i, band)) < 0) break; } /* If the code < 0, the last thread creation failed -- clean it up */ if (code < 0) { band -= crdev->thread_lookahead_direction; /* update for 'next_band' usage */ /* the following relies on 'free' ignoring NULL pointers */ gx_semaphore_free(crdev->render_threads[i].sema_group); gx_semaphore_free(crdev->render_threads[i].sema_this); if (crdev->render_threads[i].bdev != NULL) cdev->buf_procs.destroy_buf_device(crdev->render_threads[i].bdev); if (crdev->render_threads[i].cdev != NULL) { gx_device_clist_common *thread_cdev = (gx_device_clist_common *)crdev->render_threads[i].cdev; /* Close the file handles, but don't delete (unlink) the files */ thread_cdev->page_info.io_procs->fclose(thread_cdev->page_info.bfile, thread_cdev->page_info.bfname, false); thread_cdev->page_info.io_procs->fclose(thread_cdev->page_info.cfile, thread_cdev->page_info.cfname, false); thread_cdev->do_not_open_or_close_bandfiles = true; /* we already closed the files */ gdev_prn_free_memory((gx_device *)thread_cdev); gs_free_object(crdev->render_threads[i].memory, thread_cdev, "clist_setup_render_threads"); } if (crdev->render_threads[i].buffer != NULL && options && options->free_buffer_fn != NULL) { options->free_buffer_fn(options->arg, dev, crdev->render_threads[i].memory, crdev->render_threads[i].buffer); crdev->render_threads[i].buffer = NULL; } if (crdev->render_threads[i].memory != NULL) { gs_memory_chunk_release(crdev->render_threads[i].memory); crdev->render_threads[i].memory = NULL; } } /* If we weren't able to create at least one thread, punt */ /* Although a single thread isn't any more efficient, the */ /* machinery still works, so that's OK. */ if (i == 0) { if (crdev->render_threads[0].memory != NULL) { gs_memory_chunk_release(crdev->render_threads[0].memory); /* free up the locking wrapper if we allocated one */ if (chunk_base_mem != mem) { gs_memory_locked_release((gs_memory_locked_t *)chunk_base_mem); gs_free_object(mem, chunk_base_mem, "clist_setup_render_threads(locked allocator)"); } } gs_free_object(mem, crdev->render_threads, "clist_setup_render_threads"); crdev->render_threads = NULL; /* restore the file pointers */ if (cdev->page_info.cfile == NULL) { char fmode[4]; strcpy(fmode, "a+"); /* file already exists and we want to re-use it */ strncat(fmode, gp_fmode_binary_suffix, 1); cdev->page_info.io_procs->fopen(cdev->page_info.cfname, fmode, &cdev->page_info.cfile, mem, cdev->bandlist_memory, true); cdev->page_info.io_procs->fseek(cdev->page_info.cfile, 0, SEEK_SET, cdev->page_info.cfname); cdev->page_info.io_procs->fopen(cdev->page_info.bfname, fmode, &cdev->page_info.bfile, mem, cdev->bandlist_memory, false); cdev->page_info.io_procs->fseek(cdev->page_info.bfile, 0, SEEK_SET, cdev->page_info.bfname); } emprintf1(mem, "Rendering threads not started, code=%d.\n", code); return_error(code); } crdev->num_render_threads = i; crdev->curr_render_thread = 0; crdev->next_band = band; if(gs_debug[':'] != 0) dmprintf1(mem, "%% Using %d rendering threads\n", i); return 0; }