static int gx_output_saved_page(gx_device_printer *pdev, gx_saved_page *page) { int code; /* Note that banding_type is NOT a device parameter handled in the paramlist */ gdev_banding_type save_banding_type = pdev->space_params.banding_type; gx_device_clist_reader *crdev = (gx_device_clist_reader *)pdev; pdev->space_params.banding_type = BandingAlways; if ((code = gx_saved_page_load(pdev, page)) < 0) goto out; /* don't want the band files touched after printing */ crdev->do_not_open_or_close_bandfiles = true; /* load the color_usage_array */ if ((code = clist_read_color_usage_array(crdev)) < 0) goto out; if ((code = clist_read_icctable(crdev)) < 0) goto out; code = (crdev->icc_cache_cl = gsicc_cache_new(crdev->memory)) == NULL ? gs_error_VMerror : code; if (code < 0) goto out; /* After setting params, make sure bg_printing is off */ pdev->bg_print_requested = false; /* Note: we never flush pages allowing for re-printing from the list */ /* data (files) will be deleted when the list is flushed or freed. */ code = (*dev_proc(pdev, output_page)) ((gx_device *) pdev, (pdev->IgnoreNumCopies || pdev->NumCopies_set <= 0) ? 1 : pdev->NumCopies, false); clist_free_icc_table(crdev->icc_table, crdev->memory); crdev->icc_table = NULL; rc_decrement(crdev->icc_cache_cl,"clist_finish_page"); crdev->icc_cache_cl = NULL; /* Close the clist files */ code = crdev->page_info.io_procs->fclose(crdev->page_info.cfile, crdev->page_info.cfname, false); if (code >= 0) { crdev->page_info.cfile = NULL; code = crdev->page_info.io_procs->fclose(crdev->page_info.bfile, crdev->page_info.bfname, false); } if (code < 0) goto out; crdev->page_info.bfile = NULL; out: pdev->space_params.banding_type = save_banding_type; return code; }
/* This is also exported for teardown after background printing */ void teardown_device_and_mem_for_thread(gx_device *dev, gp_thread_id thread_id, bool bg_print) { gx_device_clist_common *thread_cdev = (gx_device_clist_common *)dev; gx_device_clist_reader *thread_crdev = (gx_device_clist_reader *)dev; gs_memory_t *thread_memory = dev->memory; /* First finish the thread */ gp_thread_finish(thread_id); if (bg_print) { /* we are cleaning up a background printing thread, so we clean up similarly to */ /* what is done by clist_finish_page, but without re-opening the clist files. */ clist_teardown_render_threads(dev); /* we may have used multiple threads */ /* free the thread's icc_table since this was not done by clist_finish_page */ clist_free_icc_table(thread_crdev->icc_table, thread_memory); thread_crdev->icc_table = NULL; /* NB: gdev_prn_free_memory below will free the color_usage_array */ } else { /* make sure this doesn't get freed by gdev_prn_free_memory below */ ((gx_device_clist_reader *)thread_cdev)->color_usage_array = NULL; } rc_decrement(thread_crdev->icc_cache_cl, "teardown_render_thread"); thread_crdev->icc_cache_cl = NULL; /* * Free the BufferSpace, close the band files, optionally unlinking them. * We unlink the files if this call is cleaning up from bg printing. * 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 */ /* If this thread was being used for background printing and NumRenderingThreads > 0 */ /* the clist_setup_render_threads may have already closed these files */ if (thread_cdev->page_info.cfile != NULL) thread_cdev->page_info.io_procs->fclose(thread_cdev->page_info.bfile, thread_cdev->page_info.bfname, bg_print); if (thread_cdev->page_info.bfile != NULL) thread_cdev->page_info.io_procs->fclose(thread_cdev->page_info.cfile, thread_cdev->page_info.cfname, bg_print); 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. Note that the deviceN stuff if was allocated and copied earlier for the device will be freed with this call and the icc_struct ref count will be decremented. */ gs_free_object(thread_memory, thread_cdev, "clist_teardown_render_threads"); #ifdef DEBUG dmprintf(thread_memory, "rendering thread ending memory state...\n"); gs_memory_chunk_dump_memory(thread_memory); dmprintf(thread_memory, " memory dump done.\n"); #endif gs_memory_chunk_release(thread_memory); }
/* Exported for use by background printing. */ gx_device * setup_device_and_mem_for_thread(gs_memory_t *chunk_base_mem, gx_device *dev, bool bg_print, gsicc_link_cache_t **cachep) { int i, code; char fmode[4]; gs_memory_t *thread_mem; gx_device_clist *cldev = (gx_device_clist *)dev; gx_device_printer *pdev = (gx_device_printer *)dev; gx_device_clist_common *cdev = (gx_device_clist_common *)cldev; gx_device *ndev; gx_device_clist *ncldev; gx_device_clist_common *ncdev; gx_device_printer *npdev; gx_device *protodev; gs_c_param_list paramlist; gs_devn_params *pclist_devn_params; /* 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_mem), chunk_base_mem )) < 0) { emprintf1(dev->memory, "chunk_wrap returned error code: %d\n", code); return NULL; } /* 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; /* Clone the device from the prototype device */ if (protodev == NULL || (code = gs_copydevice((gx_device **) &ndev, protodev, thread_mem)) < 0) { gs_memory_chunk_release(thread_mem); return NULL; } ncldev = (gx_device_clist *)ndev; ncdev = (gx_device_clist_common *)ndev; npdev = (gx_device_printer *)ndev; gx_device_fill_in_procs(ndev); ((gx_device_printer *)ncdev)->buffer_memory = ncdev->memory = ncdev->bandlist_memory = thread_mem; ndev->PageCount = dev->PageCount; /* copy to prevent mismatch error */ npdev->file = pdev->file; /* For background printing when doing N copies with %d */ strcpy((npdev->fname), (pdev->fname)); ndev->color_info = dev->color_info; /* copy before putdeviceparams */ ndev->pad = dev->pad; ndev->log2_align_mod = dev->log2_align_mod; ndev->is_planar = dev->is_planar; #if CMM_THREAD_SAFE ndev->icc_struct = dev->icc_struct; /* Set before put params */ rc_increment(ndev->icc_struct); #endif /* get the current device parameters to put into the cloned device */ gs_c_param_list_write(¶mlist, dev->memory); if ((code = gs_getdeviceparams(dev, (gs_param_list *)¶mlist)) < 0) { emprintf1(dev->memory, "Error getting device params, code=%d. Rendering threads not started.\n", code); goto out_cleanup; } gs_c_param_list_read(¶mlist); if ((code = gs_putdeviceparams(ndev, (gs_param_list *)¶mlist)) < 0) goto out_cleanup; gs_c_param_list_release(¶mlist); /* In the case of a separation device, we need to make sure we get the devn params copied over */ pclist_devn_params = dev_proc(dev, ret_devn_params)(dev); if (pclist_devn_params != NULL) { code = devn_copy_params(dev, ndev); if (code < 0) { #ifdef DEBUG /* suppress a warning on a release build */ gs_note_error(gs_error_VMerror); #endif goto out_cleanup; } } /* Also make sure supports_devn is set correctly */ ndev->icc_struct->supports_devn = cdev->icc_struct->supports_devn; ncdev->page_uses_transparency = cdev->page_uses_transparency; if_debug3m(gs_debug_flag_icc, cdev->memory, "[icc] MT clist device = 0x%p profile = 0x%p handle = 0x%p\n", ncdev, ncdev->icc_struct->device_profile[0], ncdev->icc_struct->device_profile[0]->profile_handle); /* If the device is_planar, then set the flag in the new_device and the procs */ if ((ncdev->is_planar = cdev->is_planar)) gdev_prn_set_procs_planar(ndev); /* 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) goto out_cleanup; /* close and unlink the temp files just created */ ncdev->page_info.io_procs->fclose(ncdev->page_info.cfile, ncdev->page_info.cfname, true); ncdev->page_info.io_procs->fclose(ncdev->page_info.bfile, ncdev->page_info.bfname, true); ncdev->page_info.cfile = ncdev->page_info.bfile = NULL; /* open the main thread's files for this thread */ strcpy(fmode, "r"); /* read access for threads */ strncat(fmode, gp_fmode_binary_suffix, 1); if ((code=cdev->page_info.io_procs->fopen(cdev->page_info.cfname, fmode, &ncdev->page_info.cfile, thread_mem, thread_mem, true)) < 0 || (code=cdev->page_info.io_procs->fopen(cdev->page_info.bfname, fmode, &ncdev->page_info.bfile, thread_mem, thread_mem, false)) < 0) goto out_cleanup; strcpy((ncdev->page_info.cfname), (cdev->page_info.cfname)); strcpy((ncdev->page_info.bfname), (cdev->page_info.bfname)); clist_render_init(ncldev); /* Initialize clist device for reading */ ncdev->page_info.bfile_end_pos = cdev->page_info.bfile_end_pos; /* The threads are maintained until clist_finish_page. At which point, the threads are torn down, the master clist reader device is changed to writer, and the icc_table and the icc_cache_cl freed */ #if CMM_THREAD_SAFE /* safe to share the link cache */ ncdev->icc_cache_cl = cdev->icc_cache_cl; rc_increment(cdev->icc_cache_cl, "setup_render_thread"); #else /* each thread needs its own link cache */ if (cachep != NULL) { if (*cachep == NULL) { /* We don't have one cached that we can reuse, so make one. */ if ((*cachep = gsicc_cache_new(thread_mem->thread_safe_memory)) == NULL) goto out_cleanup; } rc_increment(*cachep); ncdev->icc_cache_cl = *cachep; } else if ((ncdev->icc_cache_cl = gsicc_cache_new(thread_mem)) == NULL) goto out_cleanup; #endif if (bg_print) { gx_device_clist_reader *ncrdev = (gx_device_clist_reader *)ncdev; if (cdev->icc_table != NULL) { /* This is a background printing thread, so it cannot share the icc_table */ /* since this probably was created with a GC'ed allocator and the bg_print */ /* thread can't deal with the relocation. Free the cdev->icc_table and get */ /* a new one from the clist. */ clist_free_icc_table(cdev->icc_table, cdev->memory); cdev->icc_table = NULL; if ((code = clist_read_icctable((gx_device_clist_reader *)ncdev)) < 0) goto out_cleanup; } /* Similarly for the color_usage_array, when the foreground device switches to */ /* writer mode, the foreground's array will be freed. */ if ((code = clist_read_color_usage_array(ncrdev)) < 0) goto out_cleanup; } else { /* Use the same profile table and color usage array in each thread */ ncdev->icc_table = cdev->icc_table; /* OK for multiple rendering threads */ ((gx_device_clist_reader *)ncdev)->color_usage_array = ((gx_device_clist_reader *)cdev)->color_usage_array; } /* Needed for case when the target has cielab profile and pdf14 device has a RGB profile stored in the profile list of the clist */ ncdev->trans_dev_icc_hash = cdev->trans_dev_icc_hash; /* success */ return ndev; out_cleanup: /* Close the file handles, but don't delete (unlink) the files */ if (ncdev->page_info.bfile != NULL) ncdev->page_info.io_procs->fclose(ncdev->page_info.bfile, ncdev->page_info.bfname, false); if (ncdev->page_info.cfile != NULL) ncdev->page_info.io_procs->fclose(ncdev->page_info.cfile, ncdev->page_info.cfname, false); ncdev->do_not_open_or_close_bandfiles = true; /* we already closed the files */ if (ndev != NULL) { gdev_prn_free_memory(ndev); gs_free_object(thread_mem, ndev, "setup_device_and_mem_for_thread"); } gs_memory_chunk_release(thread_mem); return NULL; }