123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797 |
- /* Copyright (C) 1998, 1999 Aladdin Enterprises. All rights reserved.
-
- This file is part of AFPL Ghostscript.
-
- AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author or
- distributor accepts any responsibility for the consequences of using it, or
- for whether it serves any particular purpose or works at all, unless he or
- she says so in writing. Refer to the Aladdin Free Public License (the
- "License") for full details.
-
- Every copy of AFPL Ghostscript must include a copy of the License, normally
- in a plain ASCII text file named PUBLIC. The License grants you the right
- to copy, modify and redistribute AFPL Ghostscript, but only under certain
- conditions described in the License. Among other things, the License
- requires that the copyright notice and this notice be preserved on all
- copies.
- */
- /*$Id: gdevprna.c,v 1.2 2000/09/19 19:00:21 lpd Exp $ */
- /* Generic asynchronous printer driver support */
- /* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
- /* Revised 8/7/98 by L. Peter Deutsch (ghost@aladdin.com) for */
- /* memory manager changes */
- /* 12/1/98 soho@crl.com - Removed unnecessary flush & reopen in */
- /* gdev_prn_async_write_get_hardware_params */
- #include "gdevprna.h"
- #include "gsalloc.h"
- #include "gsdevice.h"
- #include "gsmemlok.h"
- #include "gsmemret.h"
- #include "gsnogc.h"
- #include "gxcldev.h"
- #include "gxclpath.h"
- #include "gxpageq.h"
- #include "gzht.h" /* for gx_ht_cache_default_bits */
- /* ----------------- Constants ----------------------- */
- /*
- * Fixed overhead # bytes to run renderer in (+ driver-spec'd variable bytes):
- * empirical & still very subject to change.
- */
- #define RendererAllocationOverheadBytes 503000 /* minimum is 503,000 as of 4/26/99 */
- #ifdef DEBUG
- /* 196000 is pretty much the minimum, given 16K phys memfile blocks */
- /*# define DebugBandlistMemorySize 196000*/ /* comment out to disable fixed (debug) bandlist size */
- #endif /* defined(DEBUG) */
- /* ---------------- Standard device procedures ---------------- */
- private dev_proc_close_device(gdev_prn_async_write_close_device);
- private dev_proc_output_page(gdev_prn_async_write_output_page);
- private dev_proc_put_params(gdev_prn_async_write_put_params);
- private dev_proc_get_hardware_params(gdev_prn_async_write_get_hardware_params);
- private dev_proc_put_params(gdev_prn_async_render_put_params);
- /* ---------------- Forward Declarations ---------------------- */
- private void gdev_prn_dealloc(P1(gx_device_printer *));
- private proc_free_up_bandlist_memory(gdev_prn_async_write_free_up_bandlist_memory);
- private int flush_page(P2(gx_device_printer *, bool));
- private int reopen_clist_after_flush(P1(gx_device_printer *));
- private void reinit_printer_into_printera(P1(gx_device_printer * const));
- private int alloc_bandlist_memory(P2(gs_memory_t **, gs_memory_t *));
- private void free_bandlist_memory(P1(gs_memory_t *));
- private int alloc_render_memory(P3(gs_memory_t **, gs_memory_t *, long));
- private void free_render_memory(P1(gs_memory_t *));
- private gs_memory_recover_status_t
- prna_mem_recover(P2(gs_memory_retrying_t *rmem, void *proc_data));
- /* ------ Open/close ------ */
- /*
- * 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,
- &gs_memory_default, 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() * 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, &gs_memory_default)) < 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(&gs_memory_default)))
- 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 *)&gs_memory_default,
- 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;
- }
- /* This procedure is called from within the memory allocator when regular */
- /* malloc's fail -- this procedure tries to free up pages from the queue */
- /* and returns a status code indicating whether any more can be freed. */
- private gs_memory_recover_status_t
- prna_mem_recover(gs_memory_retrying_t *rmem, void *proc_data)
- {
- int pages_remain = 0;
- gx_device_clist_writer *cldev = proc_data;
- if (cldev->free_up_bandlist_memory != NULL)
- pages_remain =
- (*cldev->free_up_bandlist_memory)( (gx_device *)cldev, false );
- return (pages_remain > 0) ? RECOVER_STATUS_RETRY_OK : RECOVER_STATUS_NO_RETRY;
- }
- /* (Re)set printer device fields which get trampled by gdevprn_open & put_params */
- private void
- reinit_printer_into_printera(
- gx_device_printer * const pdev /* printer to convert */
- )
- {
- /* Change some of the procedure vector to point at async procedures */
- /* Originals were already saved by gdev_prn_open */
- if (dev_proc(pdev, close_device) == gdev_prn_close)
- set_dev_proc(pdev, close_device, gdev_prn_async_write_close_device);
- set_dev_proc(pdev, output_page, gdev_prn_async_write_output_page);
- set_dev_proc(pdev, put_params, gdev_prn_async_write_put_params);
- set_dev_proc(pdev, get_xfont_procs, gx_default_get_xfont_procs);
- set_dev_proc(pdev, get_xfont_device, gx_default_get_xfont_device);
- set_dev_proc(pdev, get_hardware_params, gdev_prn_async_write_get_hardware_params);
- /* clist writer calls this if it runs out of memory & wants to retry */
- pdev->free_up_bandlist_memory = &gdev_prn_async_write_free_up_bandlist_memory;
- }
- /* Generic closing for the writer device. */
- private int
- gdev_prn_async_write_close_device(gx_device * pdev)
- {
- gx_device_printer *const pwdev = (gx_device_printer *) pdev;
- /* Signal render thread to close & terminate when done */
- gx_page_queue_add_page(pwdev->page_queue,
- GX_PAGE_QUEUE_ACTION_TERMINATE, 0, 0);
- /* Wait for renderer to finish all pages & terminate req */
- gx_page_queue_wait_until_empty(pwdev->page_queue);
- /* Cascade down to original close rtn */
- gdev_prn_close(pdev);
- pwdev->free_up_bandlist_memory = 0;
- /* Deallocte dynamic stuff */
- gdev_prn_dealloc(pwdev);
- return 0;
- }
- /* Deallocte dynamic memory attached to device. Aware of possible imcomplete open */
- private void
- gdev_prn_dealloc(gx_device_printer * pwdev)
- {
- gx_device_printer *const prdev = pwdev->async_renderer;
- /* Delete renderer device & its memory allocator */
- if (prdev) {
- gs_memory_t *render_alloc = prdev->memory;
- gs_free_object(render_alloc, prdev, "gdev_prn_dealloc");
- free_render_memory(render_alloc);
- }
- /* Free page queue */
- if (pwdev->page_queue) {
- gx_page_queue_dnit(pwdev->page_queue);
- gs_free_object(pwdev->bandlist_memory, pwdev->page_queue,
- "gdev_prn_dealloc");
- pwdev->page_queue = 0;
- }
- /* Free memory bandlist allocators */
- if (pwdev->bandlist_memory)
- free_bandlist_memory(pwdev->bandlist_memory);
- }
- /* Open the render portion of a printer device in ASYNC (overlapped) mode.
- * This routine is always called by concrete device's xx_open_render_device
- * in lieu of gdev_prn_open.
- */
- int
- gdev_prn_async_render_open(gx_device_printer * prdev)
- {
- gx_device *const pdev = (gx_device *) prdev;
- prdev->is_async_renderer = true;
- return gdev_prn_open(pdev);
- }
- /* Generic closing for the rendering device. */
- int
- gdev_prn_async_render_close_device(gx_device_printer * prdev)
- {
- gx_device *const pdev = (gx_device *) prdev;
- return gdev_prn_close(pdev);
- }
- /* (Re)set renderer device fields which get trampled by gdevprn_open & put_params */
- private void
- reinit_printer_into_renderer(
- gx_device_printer * const pdev /* printer to convert */
- )
- {
- set_dev_proc(pdev, put_params, gdev_prn_async_render_put_params);
- }
- /* ---------- Start rasterizer thread ------------ */
- /*
- * Must be called by async device driver implementation (see gdevprna.h
- * under "Synchronizing the Instances"). This is the rendering loop, which
- * requires its own thread for as long as the device is open. This proc only
- * returns after the device is closed, or if the open failed. NB that an
- * open error leaves things in a state where the writer thread will not be
- * able to close since it's expecting the renderer to acknowledge its
- * requests before the writer can close. Ergo, if this routine fails you'll
- * crash unless the caller fixes the problem & successfully retries this.
- */
- int /* rets 0 ok, -ve error code if open failed */
- gdev_prn_async_render_thread(
- gdev_prn_start_render_params * params
- )
- {
- gx_device_printer *const pwdev = params->writer_device;
- gx_device_printer *const prdev = pwdev->async_renderer;
- gx_page_queue_entry_t *entry;
- int code;
- /* Open device, but don't use default if user didn't override */
- if (prdev->printer_procs.open_render_device ==
- gx_default_open_render_device)
- code = gdev_prn_async_render_open(prdev);
- else
- code = (*prdev->printer_procs.open_render_device) (prdev);
- reinit_printer_into_renderer(prdev);
- /* The cmd list logic assumes reader's & writer's tile caches are same size */
- if (code >= 0 &&
- ((gx_device_clist *) pwdev)->writer.page_tile_cache_size !=
- ((gx_device_clist *) prdev)->writer.page_tile_cache_size) {
- gdev_prn_async_render_close_device(prdev);
- code = gs_note_error(gs_error_VMerror);
- }
- params->open_code = code;
- gx_semaphore_signal(params->open_semaphore);
- if (code < 0)
- return code;
- /* fake open, since not called by gs_opendevice */
- prdev->is_open = true;
- /* Successful open */
- while ((entry = gx_page_queue_start_dequeue(prdev->page_queue))
- && entry->action != GX_PAGE_QUEUE_ACTION_TERMINATE) {
- /* Force printer open again if it mysteriously closed. */
- /* This shouldn't ever happen, but... */
- if (!prdev->is_open) {
- if (prdev->printer_procs.open_render_device ==
- gx_default_open_render_device)
- code = gdev_prn_async_render_open(prdev);
- else
- code = (*prdev->printer_procs.open_render_device) (prdev);
- reinit_printer_into_renderer(prdev);
- if (code >= 0) {
- prdev->is_open = true;
- gdev_prn_output_page((gx_device *) prdev, 0, true);
- }
- }
- if (prdev->is_open) {
- /* Force retrieved entry onto render device */
- ((gx_device_clist *) prdev)->common.page_info = entry->page_info;
- /* Set up device geometry */
- if (clist_setup_params((gx_device *) prdev) >= 0)
- /* Go this again, since setup_params may have trashed it */
- ((gx_device_clist *) prdev)->common.page_info = entry->page_info;
- /* Call appropriate renderer routine to deal w/buffer */
- /* Ignore status, since we don't know how to deal w/errors! */
- switch (entry->action) {
- case GX_PAGE_QUEUE_ACTION_FULL_PAGE:
- (*dev_proc(prdev, output_page))((gx_device *) prdev,
- entry->num_copies, true);
- break;
- case GX_PAGE_QUEUE_ACTION_PARTIAL_PAGE:
- case GX_PAGE_QUEUE_ACTION_COPY_PAGE:
- (*dev_proc(prdev, output_page))((gx_device *) prdev,
- entry->num_copies, false);
- break;
- }
- /*
- * gx_page_queue_finish_dequeue will close and free the band
- * list files, so we don't need to call clist_close_output_file.
- */
- }
- /* Finalize dequeue & free retrieved queue entry */
- gx_page_queue_finish_dequeue(entry);
- }
- /* Close device, but don't use default if user hasn't overriden. */
- /* Ignore status, since returning bad status means open failed */
- if (prdev->printer_procs.close_render_device ==
- gx_default_close_render_device)
- gdev_prn_async_render_close_device(prdev);
- else
- (*prdev->printer_procs.close_render_device)(prdev);
- /* undo fake open, since not called by gs_closedevice */
- prdev->is_open = false;
- /* Now that device is closed, acknowledge gx_page_queue_terminate */
- gx_page_queue_finish_dequeue(entry);
- return 0;
- }
- /* ------ Get/put parameters ------ */
- /* Put parameters. */
- private int
- gdev_prn_async_write_put_params(gx_device * pdev, gs_param_list * plist)
- {
- gx_device_clist_writer *const pclwdev =
- &((gx_device_clist *) pdev)->writer;
- gx_device_printer *const pwdev = (gx_device_printer *) pdev;
- gdev_prn_space_params save_sp = pwdev->space_params;
- int save_height = pwdev->height;
- int save_width = pwdev->width;
- int code, ecode;
- if (!pwdev->is_open)
- return (*pwdev->orig_procs.put_params) (pdev, plist);
- /*
- * First, cascade to real device's put_params.
- * If that put_params made any changes that require re-opening
- * the device, just flush the page; the parameter block at the
- * head of the next page will reflect the changes just made.
- * If the put_params requires no re-open, just slip it into the
- * stream in the command buffer. This way, the
- * writer device should parallel the renderer status at the same point
- * in their respective executions.
- *
- * NB. that all this works only because we take the position that
- * put_params can make no change that actually affects hardware's state
- * before the final output_page on the RASTERIZER.
- */
- /* Call original procedure, but "closed" to prevent closing device */
- pwdev->is_open = false; /* prevent put_params from closing device */
- code = (*pwdev->orig_procs.put_params) (pdev, plist);
- pwdev->is_open = true;
- pwdev->OpenOutputFile = 0; /* This is wrong, because it causes the same thing in the renderer */
- /* Flush device or emit to command list, depending if device changed geometry */
- if (memcmp(&pwdev->space_params, &save_sp, sizeof(save_sp)) != 0 ||
- pwdev->width != save_width || pwdev->height != save_height
- ) {
- int pageq_remaining;
- int new_width = pwdev->width;
- int new_height = pwdev->height;
- gdev_prn_space_params new_sp = pwdev->space_params;
- /* Need to start a new page, reallocate clist memory */
- pwdev->width = save_width;
- pwdev->height = save_height;
- pwdev->space_params = save_sp;
- /* First, get rid of any pending partial pages */
- code = flush_page(pwdev, false);
- /* Free and reallocate the printer memory. */
- pageq_remaining = 1; /* assume there are pages left in queue */
- do {
- ecode =
- gdev_prn_reallocate_memory(pdev,
- &new_sp, new_width, new_height);
- if (ecode >= 0)
- break; /* managed to recover enough memory */
- if (!pdev->is_open) {
- /* Disaster! Device was forced closed, which async drivers */
- /* aren't suppsed to do. */
- gdev_prn_async_write_close_device(pdev);
- return ecode; /* caller 'spozed to know could be closed now */
- }
- pclwdev->error_is_retryable = (ecode == gs_error_VMerror);
- }
- while (pageq_remaining >= 1 &&
- (pageq_remaining = ecode =
- clist_VMerror_recover(pclwdev, ecode)) >= 0);
- if (ecode < 0) {
- gdev_prn_free_memory(pdev);
- pclwdev->is_open = false;
- code = ecode;
- }
- } else if (code >= 0) {
- do
- if ((ecode = cmd_put_params(pclwdev, plist)) >= 0)
- break;
- while ((ecode = clist_VMerror_recover(pclwdev, ecode)) >= 0);
- if (ecode < 0 && pclwdev->error_is_retryable &&
- pclwdev->driver_call_nesting == 0
- )
- ecode = clist_VMerror_recover_flush(pclwdev, ecode);
- if (ecode < 0)
- code = ecode;
- }
- /* Reset fields that got trashed by gdev_prn_put_params and/or gdev_prn_open */
- reinit_printer_into_printera(pwdev);
- return code;
- }
- /* Get hardware-detected params. Drain page queue, then call renderer version */
- private int
- gdev_prn_async_write_get_hardware_params(gx_device * pdev, gs_param_list * plist)
- {
- gx_device_printer *const pwdev = (gx_device_printer *) pdev;
- gx_device_printer *const prdev = pwdev->async_renderer;
- if (!pwdev->is_open || !prdev)
- /* if not open, just use device's get hw params */
- return (dev_proc(pwdev, get_hardware_params))(pdev, plist);
- else {
- /* wait for empty pipeline */
- gx_page_queue_wait_until_empty(pwdev->page_queue);
- /* get reader's h/w params, now that writer & reader are sync'ed */
- return (dev_proc(prdev, get_hardware_params))
- ((gx_device *) prdev, plist);
- }
- }
- /* Put parameters on RENDERER. */
- private int /* returns -ve err code only if FATAL error (can't keep rendering) */
- gdev_prn_async_render_put_params(gx_device * pdev, gs_param_list * plist)
- {
- gx_device_printer *const prdev = (gx_device_printer *) pdev;
- bool save_is_open = prdev->is_open;
- /* put_parms from clist are guaranteed to never re-init device */
- /* They're also pretty much guaranteed to always succeed */
- (*prdev->orig_procs.put_params) (pdev, plist);
- /* If device closed itself, try to open & clear it */
- if (!prdev->is_open && save_is_open) {
- int code;
- if (prdev->printer_procs.open_render_device ==
- gx_default_open_render_device)
- code = gdev_prn_async_render_open(prdev);
- else
- code = (*prdev->printer_procs.open_render_device) (prdev);
- reinit_printer_into_renderer(prdev);
- if (code >= 0)
- /****** CLEAR PAGE SOMEHOW ******/;
- else
- return code; /* this'll cause clist to stop processing this band! */
- }
- return 0; /* return this unless FATAL status */
- }
- /* ------ Others ------ */
- /* Output page causes file to get added to page queue for later rasterizing */
- private int
- gdev_prn_async_write_output_page(gx_device * pdev, int num_copies, int flush)
- {
- gx_device_printer *const pwdev = (gx_device_printer *) pdev;
- gx_device_clist_writer *const pcwdev =
- &((gx_device_clist *) pdev)->writer;
- int flush_code;
- int add_code;
- int open_code;
- int one_last_time = 1;
- /* do NOT close files before sending to page queue */
- flush_code = clist_end_page(pcwdev);
- add_code = gx_page_queue_add_page(pwdev->page_queue,
- (flush ? GX_PAGE_QUEUE_ACTION_FULL_PAGE :
- GX_PAGE_QUEUE_ACTION_COPY_PAGE),
- &pcwdev->page_info, num_copies);
- if (flush && (flush_code >= 0) && (add_code >= 0)) {
- /* This page is finished */
- gx_finish_output_page(pdev, num_copies, flush);
- }
- /* Open new band files to take the place of ones added to page queue */
- while ((open_code = (*gs_clist_device_procs.open_device)
- ((gx_device *) pdev)) == gs_error_VMerror) {
- /* Open failed, try after a page gets rendered */
- if (!gx_page_queue_wait_one_page(pwdev->page_queue)
- && one_last_time-- <= 0)
- break;
- }
- return
- (flush_code < 0 ? flush_code : open_code < 0 ? open_code :
- add_code < 0 ? add_code : 0);
- }
- /* Free bandlist memory waits until the rasterizer runs enough to free some mem */
- private int /* -ve err, 0 if no pages remain to rasterize, 1 if more pages to go */
- gdev_prn_async_write_free_up_bandlist_memory(gx_device * pdev, bool flush_current)
- {
- gx_device_printer *const pwdev = (gx_device_printer *) pdev;
- if (flush_current) {
- int code = flush_page(pwdev, true);
- if (code < 0)
- return code;
- }
- return gx_page_queue_wait_one_page(pwdev->page_queue);
- }
- /* -------- Utility Routines --------- */
- /* Flush out any partial pages accumulated in device */
- /* LEAVE DEVICE in a state where it must be re-opened/re-init'd */
- private int /* ret 0 ok no flush, -ve error code */
- flush_page(
- gx_device_printer * pwdev, /* async writer device to flush */
- bool partial /* true if only partial page */
- )
- {
- gx_device_clist *const pcldev = (gx_device_clist *) pwdev;
- gx_device_clist_writer *const pcwdev = &pcldev->writer;
- int flush_code = 0;
- int add_code = 0;
- /* do NOT close files before sending to page queue */
- flush_code = clist_end_page(pcwdev);
- add_code = gx_page_queue_add_page(pwdev->page_queue,
- (partial ? GX_PAGE_QUEUE_ACTION_PARTIAL_PAGE :
- GX_PAGE_QUEUE_ACTION_FULL_PAGE),
- &pcwdev->page_info, 0);
- /* Device no longer has BANDFILES, so it must be re-init'd by caller */
- pcwdev->page_info.bfile = pcwdev->page_info.cfile = 0;
- /* return the worst of the status. */
- if (flush_code < 0)
- return flush_code;
- return add_code;
- }
- /* Flush any pending partial pages, re-open device */
- private int
- reopen_clist_after_flush(
- gx_device_printer * pwdev /* async writer device to flush */
- )
- {
- int open_code;
- int one_last_time = 1;
- /* Open new band files to take the place of ones added to page queue */
- while ((open_code = (*gs_clist_device_procs.open_device)
- ((gx_device *) pwdev)) == gs_error_VMerror) {
- /* Open failed, try after a page gets rendered */
- if (!gx_page_queue_wait_one_page(pwdev->page_queue)
- && one_last_time-- <= 0)
- break;
- }
- return open_code;
- }
- /*
- * The bandlist does allocations on the writer's thread & deallocations on
- * the reader's thread, so it needs to have mutual exclusion from itself, as
- * well as from other memory allocators since the reader can run at the same
- * time as the interpreter. The bandlist allocator therefore consists of
- * a monitor-locking wrapper around either a direct heap allocator or (for
- * testing) a fixed-limit allocator.
- */
- /* Create a bandlist allocator. */
- private int
- alloc_bandlist_memory(gs_memory_t ** final_allocator,
- gs_memory_t * base_allocator)
- {
- gs_memory_t *data_allocator = 0;
- gs_memory_locked_t *locked_allocator = 0;
- int code = 0;
- #if defined(DEBUG) && defined(DebugBandlistMemorySize)
- code = alloc_render_memory(&data_allocator, base_allocator,
- DebugBandlistMemorySize);
- if (code < 0)
- return code;
- #else
- data_allocator = (gs_memory_t *)gs_malloc_memory_init();
- if (!data_allocator)
- return_error(gs_error_VMerror);
- #endif
- locked_allocator = (gs_memory_locked_t *)
- gs_alloc_bytes_immovable(data_allocator, sizeof(gs_memory_locked_t),
- "alloc_bandlist_memory(locked allocator)");
- if (!locked_allocator)
- goto alloc_err;
- code = gs_memory_locked_init(locked_allocator, data_allocator);
- if (code < 0)
- goto alloc_err;
- *final_allocator = (gs_memory_t *)locked_allocator;
- return 0;
- alloc_err:
- if (locked_allocator)
- free_bandlist_memory((gs_memory_t *)locked_allocator);
- else if (data_allocator)
- gs_memory_free_all(data_allocator, FREE_ALL_EVERYTHING,
- "alloc_bandlist_memory(data allocator)");
- return (code < 0 ? code : gs_note_error(gs_error_VMerror));
- }
- /* Free a bandlist allocator. */
- private void
- free_bandlist_memory(gs_memory_t *bandlist_allocator)
- {
- gs_memory_locked_t *const lmem = (gs_memory_locked_t *)bandlist_allocator;
- gs_memory_t *data_mem = gs_memory_locked_target(lmem);
- gs_memory_free_all(bandlist_allocator,
- FREE_ALL_STRUCTURES | FREE_ALL_ALLOCATOR,
- "free_bandlist_memory(locked allocator)");
- if (data_mem)
- gs_memory_free_all(data_mem, FREE_ALL_EVERYTHING,
- "free_bandlist_memory(data allocator)");
- }
- /* Create an allocator with a fixed memory limit. */
- private int
- alloc_render_memory(gs_memory_t **final_allocator,
- gs_memory_t *base_allocator, long space)
- {
- gs_ref_memory_t *rmem =
- ialloc_alloc_state((gs_raw_memory_t *)base_allocator, space);
- vm_spaces spaces;
- int i, code;
- if (rmem == 0)
- return_error(gs_error_VMerror);
- code = ialloc_add_chunk(rmem, space, "alloc_render_memory");
- if (code < 0) {
- gs_memory_free_all((gs_memory_t *)rmem, FREE_ALL_EVERYTHING,
- "alloc_render_memory");
- return code;
- }
- *final_allocator = (gs_memory_t *)rmem;
- /* Call the reclaim procedure to delete the string marking tables */
- /* Only need this once since no other chunks will ever exist */
- for ( i = 0; i < countof(spaces_indexed); ++i )
- spaces_indexed[i] = 0;
- space_local = space_global = (gs_ref_memory_t *)rmem;
- spaces.vm_reclaim = gs_nogc_reclaim; /* no real GC on this chunk */
- GS_RECLAIM(&spaces, false);
- return 0;
- }
- /* Free an allocator with a fixed memory limit. */
- private void
- free_render_memory(gs_memory_t *render_allocator)
- {
- if (render_allocator)
- gs_memory_free_all(render_allocator, FREE_ALL_EVERYTHING,
- "free_render_memory");
- }
|