Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Copyright (C) 1998, 1999 Aladdin Enterprises.  All rights reserved.
2
 
3
  This software is provided AS-IS with no warranty, either express or
4
  implied.
5
 
6
  This software is distributed under license and may not be copied,
7
  modified or distributed except as expressly authorized under the terms
8
  of the license contained in the file LICENSE in this distribution.
9
 
10
  For more information about licensing, please refer to
11
  http://www.ghostscript.com/licensing/. For information on
12
  commercial licensing, go to http://www.artifex.com/licensing/ or
13
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15
*/
16
 
17
/* $Id: gdevprna.c,v 1.6 2004/08/04 19:36:12 stefan Exp $ */
18
/* Generic asynchronous printer driver support */
19
 
20
/* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
21
/* Revised 8/7/98 by L. Peter Deutsch (ghost@aladdin.com) for */
22
/*   memory manager changes */
23
/* 12/1/98 soho@crl.com - Removed unnecessary flush & reopen in */
24
/*         gdev_prn_async_write_get_hardware_params */
25
#include "gdevprna.h"
26
#include "gsalloc.h"
27
#include "gsdevice.h"
28
#include "gsmemlok.h"
29
#include "gsmemret.h"
30
#include "gsnogc.h"
31
#include "gxcldev.h"
32
#include "gxclpath.h"
33
#include "gxpageq.h"
34
#include "gzht.h"		/* for gx_ht_cache_default_bits */
35
 
36
/* ----------------- Constants ----------------------- */
37
/*
38
 * Fixed overhead # bytes to run renderer in (+ driver-spec'd variable bytes):
39
 * empirical & still very subject to change.
40
 */
41
#define RendererAllocationOverheadBytes 503000 /* minimum is 503,000 as of 4/26/99 */
42
 
43
#ifdef DEBUG
44
/* 196000 is pretty much the minimum, given 16K phys memfile blocks */
45
/*# define DebugBandlistMemorySize 196000*/ /* comment out to disable fixed (debug) bandlist size */
46
#endif /* defined(DEBUG) */
47
 
48
/* ---------------- Standard device procedures ---------------- */
49
private dev_proc_close_device(gdev_prn_async_write_close_device);
50
private dev_proc_output_page(gdev_prn_async_write_output_page);
51
private dev_proc_put_params(gdev_prn_async_write_put_params);
52
private dev_proc_get_hardware_params(gdev_prn_async_write_get_hardware_params);
53
private dev_proc_put_params(gdev_prn_async_render_put_params);
54
 
55
/* ---------------- Forward Declarations ---------------------- */
56
private void gdev_prn_dealloc(gx_device_printer *);
57
private proc_free_up_bandlist_memory(gdev_prn_async_write_free_up_bandlist_memory);
58
private int flush_page(gx_device_printer *, bool);
59
private int reopen_clist_after_flush(gx_device_printer *);
60
private void reinit_printer_into_printera(gx_device_printer * const);
61
private int alloc_bandlist_memory(gs_memory_t **, gs_memory_t *);
62
private void free_bandlist_memory(gs_memory_t *);
63
private int alloc_render_memory(gs_memory_t **, gs_memory_t *, long);
64
private void free_render_memory(gs_memory_t *);
65
private gs_memory_recover_status_t
66
    prna_mem_recover(gs_memory_retrying_t *rmem, void *proc_data);
67
 
68
/* ------ Open/close ------ */
69
 
70
/*
71
 * Open this printer device in ASYNC (overlapped) mode.
72
 * This routine must always called by the concrete device's xx_open routine
73
 * in lieu of gdev_prn_open.
74
 */
75
int
76
gdev_prn_async_write_open(gx_device_printer * pwdev, int max_raster,
77
			  int min_band_height, int max_src_image_row)
78
{
79
    gx_device *const pdev = (gx_device *) pwdev;
80
    int code;
81
    bool writer_is_open = false;
82
    gx_device_clist_writer *const pcwdev =
83
	&((gx_device_clist *) pwdev)->writer;
84
    gx_device_clist_reader *pcrdev = 0;
85
    gx_device_printer *prdev = 0;
86
    gs_memory_t *render_memory = 0;	/* renderer's mem allocator */
87
 
88
    pwdev->page_queue = 0;
89
    pwdev->bandlist_memory = 0;
90
    pwdev->async_renderer = 0;
91
 
92
    /* allocate & init render memory */
93
    /* The big memory consumers are: */
94
    /* - the buffer used to read images from the command list */
95
    /* - buffer used by gx_real_default_strip_copy_rop() */
96
    /* - line pointer tables for memory devices used in plane extraction */
97
    /* - the halftone cache */
98
    /* - the band rendering buffer */
99
    /* The * 2's in the next statement are a ****** HACK ****** to deal with */
100
    /* sandbars in the memory manager. */
101
    if ((code = alloc_render_memory(&render_memory,
102
	    pwdev->memory->non_gc_memory, RendererAllocationOverheadBytes + max_raster
103
				    /* the first * 2 is not a hack */
104
		   + (max_raster + sizeof(void *) * 2) * min_band_height
105
		   + max_src_image_row + gx_ht_cache_default_bits() * 2)) < 0)
106
	     goto open_err;
107
 
108
    /* Alloc & init bandlist allocators */
109
    /* Bandlist mem is threadsafe & common to rdr/wtr, so it's used */
110
    /* for page queue & cmd list buffers. */
111
    if ((code = alloc_bandlist_memory
112
	 (&pwdev->bandlist_memory, pwdev->memory->non_gc_memory)) < 0)
113
	goto open_err;
114
 
115
    /* Dictate banding parameters for both renderer & writer */
116
    /* Protect from user change, since user changing these won't be */
117
    /* detected, ergo the necessary close/reallocate/open wouldn't happen. */
118
    pwdev->space_params.banding_type = BandingAlways;
119
    pwdev->space_params.params_are_read_only = true;
120
 
121
    /* Make a copy of device for use as rendering device b4 opening writer */
122
    code = gs_copydevice((gx_device **) & prdev, pdev, render_memory);
123
    pcrdev = &((gx_device_clist *) prdev)->reader;
124
    if (code < 0)
125
	goto open_err;
126
 
127
    /* -------------- Open cmd list WRITER instance of device ------- */
128
    /* --------------------------------------------------------------- */
129
    /* This is wrong, because it causes the same thing in the renderer */
130
    pwdev->OpenOutputFile = 0;	/* Don't open output file in writer */
131
 
132
    /* Hack: set this vector to tell gdev_prn_open to allocate for async rendering */
133
    pwdev->free_up_bandlist_memory = &gdev_prn_async_write_free_up_bandlist_memory;
134
 
135
    /* prevent clist writer from queuing path graphics & force it to split images */
136
    pwdev->clist_disable_mask |= clist_disable_fill_path |
137
	clist_disable_stroke_path | clist_disable_complex_clip |
138
	clist_disable_nonrect_hl_image | clist_disable_pass_thru_params;
139
 
140
    if ((code = gdev_prn_open(pdev)) >= 0) {
141
	writer_is_open = true;
142
 
143
	/* set up constant async-specific fields in device */
144
	reinit_printer_into_printera(pwdev);
145
 
146
	/* keep ptr to renderer device */
147
	pwdev->async_renderer = prdev;
148
 
149
	/* Allocate the page queue, then initialize it */
150
	/* Use bandlist memory since it's shared between rdr & wtr */
151
	if ((pwdev->page_queue = gx_page_queue_alloc(pwdev->bandlist_memory)) == 0)
152
	    code = gs_note_error(gs_error_VMerror);
153
	else
154
	    /* Allocate from clist allocator since it is thread-safe */
155
	    code = gx_page_queue_init(pwdev->page_queue, pwdev->bandlist_memory);
156
    }
157
    /* ------------ Open cmd list RENDERER instance of device ------- */
158
    /* --------------------------------------------------------------- */
159
    if (code >= 0) {
160
	gx_semaphore_t *open_semaphore;
161
 
162
	/* Force writer's actual band params into reader's requested params */
163
	prdev->space_params.band = pcwdev->page_info.band_params;
164
 
165
	/* copydevice has already set up prdev->memory = render_memory */
166
	/* prdev->bandlist_memory = pwdev->bandlist_memory; */
167
	prdev->buffer_memory = prdev->memory;
168
 
169
	/* enable renderer to accept changes to params computed by writer */
170
	prdev->space_params.params_are_read_only = false;
171
 
172
	/* page queue is common to both devices */
173
	prdev->page_queue = pwdev->page_queue;
174
 
175
	/* Start renderer thread & wait for its successful open of device */
176
	if (!(open_semaphore = gx_semaphore_alloc(prdev->memory)))
177
	    code = gs_note_error(gs_error_VMerror);
178
	else {
179
	    gdev_prn_start_render_params thread_params;
180
 
181
	    thread_params.writer_device = pwdev;
182
	    thread_params.open_semaphore = open_semaphore;
183
	    thread_params.open_code = 0;
184
	    code = (*pwdev->printer_procs.start_render_thread)
185
		(&thread_params);
186
	    if (code >= 0)
187
		gx_semaphore_wait(open_semaphore);
188
	    code = thread_params.open_code;
189
	    gx_semaphore_free(open_semaphore);
190
	}
191
    }
192
    /* ----- Set the recovery procedure for the mem allocator ----- */
193
    if (code >= 0) {
194
	gs_memory_retrying_set_recover(
195
		(gs_memory_retrying_t *)pwdev->memory->non_gc_memory,
196
		prna_mem_recover,
197
		(void *)pcwdev
198
	    );
199
    }
200
    /* --------------------- Wrap up --------------------------------- */
201
    /* --------------------------------------------------------------- */
202
    if (code < 0) {
203
open_err:
204
	/* error mop-up */
205
	if (render_memory && !prdev)
206
	    free_render_memory(render_memory);
207
 
208
	gdev_prn_dealloc(pwdev);
209
	if (writer_is_open) {
210
	    gdev_prn_close(pdev);
211
	    pwdev->free_up_bandlist_memory = 0;
212
	}
213
    }
214
    return code;
215
}
216
 
217
/* This procedure is called from within the memory allocator when regular */
218
/* malloc's fail -- this procedure tries to free up pages from the queue  */
219
/* and returns a status code indicating whether any more can be freed.    */
220
private gs_memory_recover_status_t
221
prna_mem_recover(gs_memory_retrying_t *rmem, void *proc_data)
222
{
223
    int pages_remain = 0;
224
    gx_device_clist_writer *cldev = proc_data;
225
 
226
    if (cldev->free_up_bandlist_memory != NULL)
227
	pages_remain =
228
	    (*cldev->free_up_bandlist_memory)( (gx_device *)cldev, false );
229
    return (pages_remain > 0) ? RECOVER_STATUS_RETRY_OK : RECOVER_STATUS_NO_RETRY;
230
}
231
 
232
/* (Re)set printer device fields which get trampled by gdevprn_open & put_params */
233
private void
234
reinit_printer_into_printera(
235
			     gx_device_printer * const pdev	/* printer to convert */
236
)
237
{
238
    /* Change some of the procedure vector to point at async procedures */
239
    /* Originals were already saved by gdev_prn_open */
240
    if (dev_proc(pdev, close_device) == gdev_prn_close)
241
	set_dev_proc(pdev, close_device, gdev_prn_async_write_close_device);
242
    set_dev_proc(pdev, output_page, gdev_prn_async_write_output_page);
243
    set_dev_proc(pdev, put_params, gdev_prn_async_write_put_params);
244
    set_dev_proc(pdev, get_xfont_procs, gx_default_get_xfont_procs);
245
    set_dev_proc(pdev, get_xfont_device, gx_default_get_xfont_device);
246
    set_dev_proc(pdev, get_hardware_params, gdev_prn_async_write_get_hardware_params);
247
 
248
    /* clist writer calls this if it runs out of memory & wants to retry */
249
    pdev->free_up_bandlist_memory = &gdev_prn_async_write_free_up_bandlist_memory;
250
}
251
 
252
/* Generic closing for the writer device. */
253
private int
254
gdev_prn_async_write_close_device(gx_device * pdev)
255
{
256
    gx_device_printer *const pwdev = (gx_device_printer *) pdev;
257
 
258
    /* Signal render thread to close & terminate when done */
259
    gx_page_queue_add_page(pwdev->page_queue,
260
			   GX_PAGE_QUEUE_ACTION_TERMINATE, 0, 0);
261
 
262
    /* Wait for renderer to finish all pages & terminate req */
263
    gx_page_queue_wait_until_empty(pwdev->page_queue);
264
 
265
    /* Cascade down to original close rtn */
266
    gdev_prn_close(pdev);
267
    pwdev->free_up_bandlist_memory = 0;
268
 
269
    /* Deallocte dynamic stuff */
270
    gdev_prn_dealloc(pwdev);
271
    return 0;
272
}
273
 
274
/* Deallocte dynamic memory attached to device. Aware of possible imcomplete open */
275
private void
276
gdev_prn_dealloc(gx_device_printer * pwdev)
277
{
278
    gx_device_printer *const prdev = pwdev->async_renderer;
279
 
280
    /* Delete renderer device & its memory allocator */
281
    if (prdev) {
282
	gs_memory_t *render_alloc = prdev->memory;
283
 
284
	gs_free_object(render_alloc, prdev, "gdev_prn_dealloc");
285
	free_render_memory(render_alloc);
286
    }
287
    /* Free page queue */
288
    if (pwdev->page_queue) {
289
	gx_page_queue_dnit(pwdev->page_queue);
290
	gs_free_object(pwdev->bandlist_memory, pwdev->page_queue,
291
		       "gdev_prn_dealloc");
292
	pwdev->page_queue = 0;
293
    }
294
    /* Free memory bandlist allocators */
295
    if (pwdev->bandlist_memory)
296
	free_bandlist_memory(pwdev->bandlist_memory);
297
}
298
 
299
/* Open the render portion of a printer device in ASYNC (overlapped) mode.
300
 
301
 * This routine is always called by concrete device's xx_open_render_device
302
 * in lieu of gdev_prn_open.
303
 */
304
int
305
gdev_prn_async_render_open(gx_device_printer * prdev)
306
{
307
    gx_device *const pdev = (gx_device *) prdev;
308
 
309
    prdev->is_async_renderer = true;
310
    return gdev_prn_open(pdev);
311
}
312
 
313
/* Generic closing for the rendering device. */
314
int
315
gdev_prn_async_render_close_device(gx_device_printer * prdev)
316
{
317
    gx_device *const pdev = (gx_device *) prdev;
318
 
319
    return gdev_prn_close(pdev);
320
}
321
 
322
/* (Re)set renderer device fields which get trampled by gdevprn_open & put_params */
323
private void
324
reinit_printer_into_renderer(
325
			     gx_device_printer * const pdev	/* printer to convert */
326
)
327
{
328
    set_dev_proc(pdev, put_params, gdev_prn_async_render_put_params);
329
}
330
 
331
/* ---------- Start rasterizer thread ------------ */
332
/*
333
 * Must be called by async device driver implementation (see gdevprna.h
334
 * under "Synchronizing the Instances"). This is the rendering loop, which
335
 * requires its own thread for as long as the device is open. This proc only
336
 * returns after the device is closed, or if the open failed. NB that an
337
 * open error leaves things in a state where the writer thread will not be
338
 * able to close since it's expecting the renderer to acknowledge its
339
 * requests before the writer can close.  Ergo, if this routine fails you'll
340
 * crash unless the caller fixes the problem & successfully retries this.
341
 */
342
int				/* rets 0 ok, -ve error code if open failed */
343
gdev_prn_async_render_thread(
344
			     gdev_prn_start_render_params * params
345
)
346
{
347
    gx_device_printer *const pwdev = params->writer_device;
348
    gx_device_printer *const prdev = pwdev->async_renderer;
349
    gx_page_queue_entry_t *entry;
350
    int code;
351
 
352
    /* Open device, but don't use default if user didn't override */
353
    if (prdev->printer_procs.open_render_device ==
354
	  gx_default_open_render_device)
355
	code = gdev_prn_async_render_open(prdev);
356
    else
357
	code = (*prdev->printer_procs.open_render_device) (prdev);
358
    reinit_printer_into_renderer(prdev);
359
 
360
    /* The cmd list logic assumes reader's & writer's tile caches are same size */
361
    if (code >= 0 &&
362
	  ((gx_device_clist *) pwdev)->writer.page_tile_cache_size !=
363
	    ((gx_device_clist *) prdev)->writer.page_tile_cache_size) {
364
	gdev_prn_async_render_close_device(prdev);
365
	code = gs_note_error(gs_error_VMerror);
366
    }
367
    params->open_code = code;
368
    gx_semaphore_signal(params->open_semaphore);
369
    if (code < 0)
370
	return code;
371
 
372
    /* fake open, since not called by gs_opendevice */
373
    prdev->is_open = true;
374
 
375
    /* Successful open */
376
    while ((entry = gx_page_queue_start_dequeue(prdev->page_queue))
377
	   && entry->action != GX_PAGE_QUEUE_ACTION_TERMINATE) {
378
	/* Force printer open again if it mysteriously closed. */
379
	/* This shouldn't ever happen, but... */
380
	if (!prdev->is_open) {
381
	    if (prdev->printer_procs.open_render_device ==
382
		  gx_default_open_render_device)
383
		code = gdev_prn_async_render_open(prdev);
384
	    else
385
		code = (*prdev->printer_procs.open_render_device) (prdev);
386
	    reinit_printer_into_renderer(prdev);
387
 
388
	    if (code >= 0) {
389
		prdev->is_open = true;
390
		gdev_prn_output_page((gx_device *) prdev, 0, true);
391
	    }
392
	}
393
	if (prdev->is_open) {
394
	    /* Force retrieved entry onto render device */
395
	    ((gx_device_clist *) prdev)->common.page_info = entry->page_info;
396
 
397
	    /* Set up device geometry */
398
	    if (clist_setup_params((gx_device *) prdev) >= 0)
399
		/* Go this again, since setup_params may have trashed it */
400
		((gx_device_clist *) prdev)->common.page_info = entry->page_info;
401
 
402
	    /* Call appropriate renderer routine to deal w/buffer */
403
	    /* Ignore status, since we don't know how to deal w/errors! */
404
	    switch (entry->action) {
405
 
406
		case GX_PAGE_QUEUE_ACTION_FULL_PAGE:
407
		    (*dev_proc(prdev, output_page))((gx_device *) prdev,
408
						    entry->num_copies, true);
409
		    break;
410
 
411
		case GX_PAGE_QUEUE_ACTION_PARTIAL_PAGE:
412
		case GX_PAGE_QUEUE_ACTION_COPY_PAGE:
413
		    (*dev_proc(prdev, output_page))((gx_device *) prdev,
414
						    entry->num_copies, false);
415
		    break;
416
	    }
417
	    /*
418
	     * gx_page_queue_finish_dequeue will close and free the band
419
	     * list files, so we don't need to call clist_close_output_file.
420
	     */
421
	}
422
	/* Finalize dequeue & free retrieved queue entry */
423
	gx_page_queue_finish_dequeue(entry);
424
    }
425
 
426
    /* Close device, but don't use default if user hasn't overriden. */
427
    /* Ignore status, since returning bad status means open failed */
428
    if (prdev->printer_procs.close_render_device ==
429
	  gx_default_close_render_device)
430
	gdev_prn_async_render_close_device(prdev);
431
    else
432
	(*prdev->printer_procs.close_render_device)(prdev);
433
 
434
    /* undo fake open, since not called by gs_closedevice */
435
    prdev->is_open = false;
436
 
437
    /* Now that device is closed, acknowledge gx_page_queue_terminate */
438
    gx_page_queue_finish_dequeue(entry);
439
 
440
    return 0;
441
}
442
 
443
/* ------ Get/put parameters ------ */
444
 
445
/* Put parameters. */
446
private int
447
gdev_prn_async_write_put_params(gx_device * pdev, gs_param_list * plist)
448
{
449
    gx_device_clist_writer *const pclwdev =
450
	&((gx_device_clist *) pdev)->writer;
451
    gx_device_printer *const pwdev = (gx_device_printer *) pdev;
452
    gdev_prn_space_params save_sp = pwdev->space_params;
453
    int save_height = pwdev->height;
454
    int save_width = pwdev->width;
455
    int code, ecode;
456
 
457
    if (!pwdev->is_open)
458
	return (*pwdev->orig_procs.put_params) (pdev, plist);
459
 
460
    /*
461
     * First, cascade to real device's put_params.
462
     * If that put_params made any changes that require re-opening
463
     * the device, just flush the page; the parameter block at the
464
     * head of the next page will reflect the changes just made.
465
     * If the put_params requires no re-open, just slip it into the
466
     * stream in the command buffer. This way, the
467
     * writer device should parallel the renderer status at the same point
468
     * in their respective executions.
469
     *
470
     * NB. that all this works only because we take the position that
471
     * put_params can make no change that actually affects hardware's state
472
     * before the final output_page on the RASTERIZER.
473
     */
474
    /* Call original procedure, but "closed" to prevent closing device */
475
    pwdev->is_open = false;	/* prevent put_params from closing device */
476
    code = (*pwdev->orig_procs.put_params) (pdev, plist);
477
    pwdev->is_open = true;
478
    pwdev->OpenOutputFile = 0;	/* This is wrong, because it causes the same thing in the renderer */
479
 
480
    /* Flush device or emit to command list, depending if device changed geometry */
481
    if (memcmp(&pwdev->space_params, &save_sp, sizeof(save_sp)) != 0 ||
482
	pwdev->width != save_width || pwdev->height != save_height
483
	) {
484
	int pageq_remaining;
485
	int new_width = pwdev->width;
486
	int new_height = pwdev->height;
487
	gdev_prn_space_params new_sp = pwdev->space_params;
488
 
489
	/* Need to start a new page, reallocate clist memory */
490
	pwdev->width = save_width;
491
	pwdev->height = save_height;
492
	pwdev->space_params = save_sp;
493
 
494
	/* First, get rid of any pending partial pages */
495
	code = flush_page(pwdev, false);
496
 
497
	/* Free and reallocate the printer memory. */
498
	pageq_remaining = 1;	/* assume there are pages left in queue */
499
	do {
500
	    ecode =
501
		gdev_prn_reallocate_memory(pdev,
502
					   &new_sp, new_width, new_height);
503
	    if (ecode >= 0)
504
		break;		/* managed to recover enough memory */
505
	    if (!pdev->is_open) {
506
		/* Disaster! Device was forced closed, which async drivers */
507
		/* aren't suppsed to do. */
508
		gdev_prn_async_write_close_device(pdev);
509
		return ecode;	/* caller 'spozed to know could be closed now */
510
	    }
511
	    pclwdev->error_is_retryable = (ecode == gs_error_VMerror);
512
	}
513
	while (pageq_remaining >= 1 &&
514
	       (pageq_remaining = ecode =
515
		clist_VMerror_recover(pclwdev, ecode)) >= 0);
516
	if (ecode < 0) {
517
	    gdev_prn_free_memory(pdev);
518
	    pclwdev->is_open = false;
519
	    code = ecode;
520
	}
521
    } else if (code >= 0) {
522
	do
523
	    if ((ecode = cmd_put_params(pclwdev, plist)) >= 0)
524
		break;
525
	while ((ecode = clist_VMerror_recover(pclwdev, ecode)) >= 0);
526
	if (ecode < 0 && pclwdev->error_is_retryable &&
527
	    pclwdev->driver_call_nesting == 0
528
	    )
529
	    ecode = clist_VMerror_recover_flush(pclwdev, ecode);
530
	if (ecode < 0)
531
	    code = ecode;
532
    }
533
    /* Reset fields that got trashed by gdev_prn_put_params and/or gdev_prn_open */
534
    reinit_printer_into_printera(pwdev);
535
 
536
    return code;
537
}
538
 
539
/* Get hardware-detected params. Drain page queue, then call renderer version */
540
private int
541
gdev_prn_async_write_get_hardware_params(gx_device * pdev, gs_param_list * plist)
542
{
543
    gx_device_printer *const pwdev = (gx_device_printer *) pdev;
544
    gx_device_printer *const prdev = pwdev->async_renderer;
545
 
546
    if (!pwdev->is_open || !prdev)
547
	/* if not open, just use device's get hw params */
548
	return (dev_proc(pwdev, get_hardware_params))(pdev, plist);
549
    else {
550
	/* wait for empty pipeline */
551
	gx_page_queue_wait_until_empty(pwdev->page_queue);
552
 
553
	/* get reader's h/w params, now that writer & reader are sync'ed */
554
	return (dev_proc(prdev, get_hardware_params))
555
	    ((gx_device *) prdev, plist);
556
    }
557
}
558
 
559
/* Put parameters on RENDERER. */
560
private int		/* returns -ve err code only if FATAL error (can't keep rendering) */
561
gdev_prn_async_render_put_params(gx_device * pdev, gs_param_list * plist)
562
{
563
    gx_device_printer *const prdev = (gx_device_printer *) pdev;
564
    bool save_is_open = prdev->is_open;
565
 
566
    /* put_parms from clist are guaranteed to never re-init device */
567
    /* They're also pretty much guaranteed to always succeed */
568
    (*prdev->orig_procs.put_params) (pdev, plist);
569
 
570
    /* If device closed itself, try to open & clear it */
571
    if (!prdev->is_open && save_is_open) {
572
	int code;
573
 
574
	if (prdev->printer_procs.open_render_device ==
575
	      gx_default_open_render_device)
576
	    code = gdev_prn_async_render_open(prdev);
577
	else
578
	    code = (*prdev->printer_procs.open_render_device) (prdev);
579
	reinit_printer_into_renderer(prdev);
580
	if (code >= 0)
581
	    /****** CLEAR PAGE SOMEHOW ******/;
582
	else
583
	    return code;	/* this'll cause clist to stop processing this band! */
584
    }
585
    return 0;			/* return this unless FATAL status */
586
}
587
 
588
 
589
/* ------ Others ------ */
590
 
591
/* Output page causes file to get added to page queue for later rasterizing */
592
private int
593
gdev_prn_async_write_output_page(gx_device * pdev, int num_copies, int flush)
594
{
595
    gx_device_printer *const pwdev = (gx_device_printer *) pdev;
596
    gx_device_clist_writer *const pcwdev =
597
	&((gx_device_clist *) pdev)->writer;
598
    int flush_code;
599
    int add_code;
600
    int open_code;
601
    int one_last_time = 1;
602
 
603
    /* do NOT close files before sending to page queue */
604
    flush_code = clist_end_page(pcwdev);
605
    add_code = gx_page_queue_add_page(pwdev->page_queue,
606
				(flush ? GX_PAGE_QUEUE_ACTION_FULL_PAGE :
607
				 GX_PAGE_QUEUE_ACTION_COPY_PAGE),
608
				      &pcwdev->page_info, num_copies);
609
    if (flush && (flush_code >= 0) && (add_code >= 0)) {
610
	/* This page is finished */
611
	gx_finish_output_page(pdev, num_copies, flush);
612
    }
613
 
614
    /* Open new band files to take the place of ones added to page queue */
615
    while ((open_code = (*gs_clist_device_procs.open_device)
616
	    ((gx_device *) pdev)) == gs_error_VMerror) {
617
	/* Open failed, try after a page gets rendered */
618
	if (!gx_page_queue_wait_one_page(pwdev->page_queue)
619
	    && one_last_time-- <= 0)
620
	    break;
621
    }
622
 
623
    return
624
  	(flush_code < 0 ? flush_code : open_code < 0 ? open_code :
625
	 add_code < 0 ? add_code : 0);
626
}
627
 
628
/* Free bandlist memory waits until the rasterizer runs enough to free some mem */
629
private int			/* -ve err,  0 if no pages remain to rasterize, 1 if more pages to go */
630
gdev_prn_async_write_free_up_bandlist_memory(gx_device * pdev, bool flush_current)
631
{
632
    gx_device_printer *const pwdev = (gx_device_printer *) pdev;
633
 
634
    if (flush_current) {
635
	int code = flush_page(pwdev, true);
636
 
637
	if (code < 0)
638
	    return code;
639
    }
640
    return gx_page_queue_wait_one_page(pwdev->page_queue);
641
}
642
 
643
/* -------- Utility Routines --------- */
644
 
645
/* Flush out any partial pages accumulated in device */
646
/* LEAVE DEVICE in a state where it must be re-opened/re-init'd */
647
private int			/* ret 0 ok no flush, -ve error code */
648
flush_page(
649
	   gx_device_printer * pwdev,	/* async writer device to flush */
650
	   bool partial	/* true if only partial page */
651
)
652
{
653
    gx_device_clist *const pcldev = (gx_device_clist *) pwdev;
654
    gx_device_clist_writer *const pcwdev = &pcldev->writer;
655
    int flush_code = 0;
656
    int add_code = 0;
657
 
658
    /* do NOT close files before sending to page queue */
659
    flush_code = clist_end_page(pcwdev);
660
    add_code = gx_page_queue_add_page(pwdev->page_queue,
661
				(partial ? GX_PAGE_QUEUE_ACTION_PARTIAL_PAGE :
662
				 GX_PAGE_QUEUE_ACTION_FULL_PAGE),
663
				      &pcwdev->page_info, 0);
664
 
665
    /* Device no longer has BANDFILES, so it must be re-init'd by caller */
666
    pcwdev->page_info.bfile = pcwdev->page_info.cfile = 0;
667
 
668
    /* return the worst of the status. */
669
    if (flush_code < 0)
670
	return flush_code;
671
    return add_code;
672
}
673
 
674
/* Flush any pending partial pages, re-open device */
675
private int
676
reopen_clist_after_flush(
677
			 gx_device_printer * pwdev	/* async writer device to flush */
678
)
679
{
680
    int open_code;
681
    int one_last_time = 1;
682
 
683
    /* Open new band files to take the place of ones added to page queue */
684
    while ((open_code = (*gs_clist_device_procs.open_device)
685
	    ((gx_device *) pwdev)) == gs_error_VMerror) {
686
	/* Open failed, try after a page gets rendered */
687
	if (!gx_page_queue_wait_one_page(pwdev->page_queue)
688
	    && one_last_time-- <= 0)
689
	    break;
690
    }
691
    return open_code;
692
}
693
 
694
/*
695
 * The bandlist does allocations on the writer's thread & deallocations on
696
 * the reader's thread, so it needs to have mutual exclusion from itself, as
697
 * well as from other memory allocators since the reader can run at the same
698
 * time as the interpreter.  The bandlist allocator therefore consists of
699
 * a monitor-locking wrapper around either a direct heap allocator or (for
700
 * testing) a fixed-limit allocator.
701
 */
702
 
703
/* Create a bandlist allocator. */
704
private int
705
alloc_bandlist_memory(gs_memory_t ** final_allocator,
706
		      gs_memory_t * base_allocator)
707
{
708
    gs_memory_t *data_allocator = 0;
709
    gs_memory_locked_t *locked_allocator = 0;
710
    int code = 0;
711
 
712
#if defined(DEBUG) && defined(DebugBandlistMemorySize)
713
    code = alloc_render_memory(&data_allocator, base_allocator,
714
			       DebugBandlistMemorySize);
715
    if (code < 0)
716
	return code;
717
#else
718
    data_allocator = (gs_memory_t *)gs_malloc_memory_init();
719
    if (!data_allocator)
720
	return_error(gs_error_VMerror);
721
#endif
722
    locked_allocator = (gs_memory_locked_t *)
723
	gs_alloc_bytes_immovable(data_allocator, sizeof(gs_memory_locked_t),
724
				 "alloc_bandlist_memory(locked allocator)");
725
    if (!locked_allocator)
726
	goto alloc_err;
727
    code = gs_memory_locked_init(locked_allocator, data_allocator);
728
    if (code < 0)
729
	goto alloc_err;
730
    *final_allocator = (gs_memory_t *)locked_allocator;
731
    return 0;
732
alloc_err:
733
    if (locked_allocator)
734
	free_bandlist_memory((gs_memory_t *)locked_allocator);
735
    else if (data_allocator)
736
	gs_memory_free_all(data_allocator, FREE_ALL_EVERYTHING,
737
			   "alloc_bandlist_memory(data allocator)");
738
    return (code < 0 ? code : gs_note_error(gs_error_VMerror));
739
}
740
 
741
/* Free a bandlist allocator. */
742
private void
743
free_bandlist_memory(gs_memory_t *bandlist_allocator)
744
{
745
    gs_memory_locked_t *const lmem = (gs_memory_locked_t *)bandlist_allocator;
746
    gs_memory_t *data_mem = gs_memory_locked_target(lmem);
747
 
748
    gs_memory_free_all(bandlist_allocator,
749
		       FREE_ALL_STRUCTURES | FREE_ALL_ALLOCATOR,
750
		       "free_bandlist_memory(locked allocator)");
751
    if (data_mem)
752
	gs_memory_free_all(data_mem, FREE_ALL_EVERYTHING,
753
			   "free_bandlist_memory(data allocator)");
754
}
755
 
756
/* Create an allocator with a fixed memory limit. */
757
private int
758
alloc_render_memory(gs_memory_t **final_allocator,
759
		    gs_memory_t *base_allocator, long space)
760
{
761
    gs_ref_memory_t *rmem =
762
	ialloc_alloc_state((gs_memory_t *)base_allocator, space);
763
    vm_spaces spaces;
764
    int i, code;
765
 
766
    if (rmem == 0)
767
	return_error(gs_error_VMerror);
768
    code = ialloc_add_chunk(rmem, space, "alloc_render_memory");
769
    if (code < 0) {
770
	gs_memory_free_all((gs_memory_t *)rmem, FREE_ALL_EVERYTHING,
771
			   "alloc_render_memory");
772
	return code;
773
    }
774
    *final_allocator = (gs_memory_t *)rmem;
775
 
776
    /* Call the reclaim procedure to delete the string marking tables	*/
777
    /* Only need this once since no other chunks will ever exist	*/
778
 
779
    for ( i = 0; i < countof(spaces_indexed); ++i )
780
	spaces_indexed[i] = 0;
781
    space_local = space_global = (gs_ref_memory_t *)rmem;
782
    spaces.vm_reclaim = gs_nogc_reclaim;	/* no real GC on this chunk */
783
    GS_RECLAIM(&spaces, false);
784
 
785
    return 0;
786
}
787
 
788
/* Free an allocator with a fixed memory limit. */
789
private void
790
free_render_memory(gs_memory_t *render_allocator)
791
{
792
    if (render_allocator)
793
	gs_memory_free_all(render_allocator, FREE_ALL_EVERYTHING,
794
			   "free_render_memory");
795
}