xref: /plan9/sys/src/cmd/gs/src/gxpageq.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1998 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: gxpageq.c,v 1.5 2002/06/16 05:48:56 lpd Exp $ */
18 /* Page queue implementation */
19 
20 /* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
21 
22 #include "gx.h"
23 #include "gxdevice.h"
24 #include "gxclist.h"
25 #include "gxpageq.h"
26 #include "gserrors.h"
27 #include "gsstruct.h"
28 
29 /* Define the structure implementation for a page queue. */
30 struct gx_page_queue_s {
31     gs_memory_t *memory;	/* allocator used to allocate entries */
32     gx_monitor_t *monitor;	/* used to serialize access to this structure */
33     int entry_count;		/* # elements in page_queue */
34     bool dequeue_in_progress;	/* true between start/ & end_dequeue */
35     gx_semaphore_t *render_req_sema;	/* sema signalled when page queued */
36     bool enable_render_done_signal;	/* enable signals to render_done_sema */
37     gx_semaphore_t *render_done_sema;	/* semaphore signaled when (partial) page rendered */
38     gx_page_queue_entry_t *last_in;	/* if <> 0, Last-in queue entry */
39     gx_page_queue_entry_t *first_in;	/* if <> 0, First-in queue entry */
40     gx_page_queue_entry_t *reserve_entry;	/* spare allocation */
41 };
42 
43 /*
44  * Null initializer for entry page_info (used by gx_page_queue_add_page() ).
45  */
46 private const gx_band_page_info_t null_page_info = { PAGE_INFO_NULL_VALUES };
47 
48 #define private_st_gx_page_queue()\
49   gs_private_st_ptrs4(st_gx_page_queue, gx_page_queue_t, "gx_page_queue",\
50     gx_page_queue_enum_ptrs, gx_page_queue_reloc_ptrs,\
51     monitor, first_in, last_in, reserve_entry);
52 
53 /* ------------------- Global Data ----------------------- */
54 
55 /* Structure descriptor for GC */
56 private_st_gx_page_queue_entry();
57 private_st_gx_page_queue();
58 
59 /* ------------ Forward Decl's --------------------------- */
60 private gx_page_queue_entry_t *	/* removed entry, 0 if none avail */
61     gx_page_queue_remove_first(
62 			       gx_page_queue_t * queue	/* page queue to retrieve from */
63 			       );
64 
65 
66 /* --------------------Procedures------------------------- */
67 
68 /* Allocate a page queue. */
69 gx_page_queue_t *
gx_page_queue_alloc(gs_memory_t * mem)70 gx_page_queue_alloc(gs_memory_t *mem)
71 {
72     return gs_alloc_struct(mem, gx_page_queue_t, &st_gx_page_queue,
73 			   "gx_page_queue_alloc");
74 }
75 
76 /* ------- page_queue_entry alloc/free --------- */
77 
78 /* Allocate & init a gx_page_queue_entry */
79 gx_page_queue_entry_t *		/* rets ptr to allocated object, 0 if VM error */
gx_page_queue_entry_alloc(gx_page_queue_t * queue)80 gx_page_queue_entry_alloc(
81 			  gx_page_queue_t * queue	/* queue that entry is being alloc'd for */
82 )
83 {
84     gx_page_queue_entry_t *entry
85     = gs_alloc_struct(queue->memory, gx_page_queue_entry_t,
86 		      &st_gx_page_queue_entry, "gx_page_queue_entry_alloc");
87 
88     if (entry != 0) {
89 	entry->next = 0;
90 	entry->queue = queue;
91     }
92     return entry;
93 }
94 
95 /* Free a gx_page_queue_entry allocated w/gx_page_queue_entry_alloc */
96 void
gx_page_queue_entry_free(gx_page_queue_entry_t * entry)97 gx_page_queue_entry_free(
98 			    gx_page_queue_entry_t * entry	/* entry to free up */
99 )
100 {
101     gs_free_object(entry->queue->memory, entry, "gx_page_queue_entry_free");
102 }
103 
104 /* Free the clist resources held by a gx_page_queue_entry_t */
105 void
gx_page_queue_entry_free_page_info(gx_page_queue_entry_t * entry)106 gx_page_queue_entry_free_page_info(
107 			    gx_page_queue_entry_t * entry	/* entry to free up */
108 )
109 {
110     clist_close_page_info( &entry->page_info );
111 }
112 
113 /* -------- page_queue init/dnit ---------- */
114 
115 /* Initialize a gx_page_queue object */
116 int				/* -ve error code, or 0 */
gx_page_queue_init(gx_page_queue_t * queue,gs_memory_t * memory)117 gx_page_queue_init(
118 		   gx_page_queue_t * queue,	/* page queue to init */
119 		   gs_memory_t * memory	/* allocator for dynamic memory */
120 )
121 {
122     queue->memory = memory;
123     queue->monitor = gx_monitor_alloc(memory);	/* alloc monitor to serialize */
124     queue->entry_count = 0;
125     queue->dequeue_in_progress = false;
126     queue->render_req_sema = gx_semaphore_alloc(memory);
127     queue->enable_render_done_signal = false;
128     queue->render_done_sema = gx_semaphore_alloc(memory);
129     queue->first_in = queue->last_in = 0;
130     queue->reserve_entry = gx_page_queue_entry_alloc(queue);
131 
132     if (queue->monitor && queue->render_req_sema && queue->render_done_sema
133 	&& queue->reserve_entry)
134 	return 0;
135     else {
136 	gx_page_queue_dnit(queue);
137 	return gs_error_VMerror;
138     }
139 }
140 
141 /* Dnitialize a gx_page_queue object */
142 void
gx_page_queue_dnit(gx_page_queue_t * queue)143 gx_page_queue_dnit(
144 		      gx_page_queue_t * queue	/* page queue to dnit */
145 )
146 {
147     /* Deallocate any left-over queue entries */
148     gx_page_queue_entry_t *entry;
149 
150     while ((entry = gx_page_queue_remove_first(queue)) != 0) {
151 	gx_page_queue_entry_free_page_info(entry);
152 	gx_page_queue_entry_free(entry);
153     }
154 
155     /* Free dynamic objects */
156     if (queue->monitor) {
157 	gx_monitor_free(queue->monitor);
158 	queue->monitor = 0;
159     }
160     if (queue->render_req_sema) {
161 	gx_semaphore_free(queue->render_req_sema);
162 	queue->render_req_sema = 0;
163     }
164     if (queue->render_done_sema) {
165 	gx_semaphore_free(queue->render_done_sema);
166 	queue->render_done_sema = 0;
167     }
168     if (queue->reserve_entry) {
169 	gx_page_queue_entry_free(queue->reserve_entry);
170 	queue->reserve_entry = 0;
171     }
172 }
173 
174 /* -------- low-level queue add/remove ---------- */
175 
176 /* Retrieve & remove firstin queue entry */
177 private gx_page_queue_entry_t *	/* removed entry, 0 if none avail */
gx_page_queue_remove_first(gx_page_queue_t * queue)178 gx_page_queue_remove_first(
179 			      gx_page_queue_t * queue	/* page queue to retrieve from */
180 )
181 {
182     gx_page_queue_entry_t *entry = 0;	/* assume failure */
183 
184     /* Enter monitor */
185     gx_monitor_enter(queue->monitor);
186 
187     /* Get the goods */
188     if (queue->entry_count) {
189 	entry = queue->first_in;
190 	queue->first_in = entry->next;
191 	if (queue->last_in == entry)
192 	    queue->last_in = 0;
193 	--queue->entry_count;
194     }
195     /* exit monitor */
196     gx_monitor_leave(queue->monitor);
197 
198     return entry;
199 }
200 
201 /* Add entry to queue at end */
202 private void
gx_page_queue_add_last(gx_page_queue_entry_t * entry)203 gx_page_queue_add_last(
204 			  gx_page_queue_entry_t * entry	/* entry to add */
205 )
206 {
207     gx_page_queue_t *queue = entry->queue;
208 
209     /* Enter monitor */
210     gx_monitor_enter(queue->monitor);
211 
212     /* Add the goods */
213     entry->next = 0;
214     if (queue->last_in != 0)
215 	queue->last_in->next = entry;
216     queue->last_in = entry;
217     if (queue->first_in == 0)
218 	queue->first_in = entry;
219     ++queue->entry_count;
220 
221     /* exit monitor */
222     gx_monitor_leave(queue->monitor);
223 }
224 
225 /* --------- low-level synchronization ---------- */
226 
227 /* Wait for a single page to finish rendering (if any pending) */
228 int				/* rets 0 if no pages were waiting for rendering, 1 if actually waited */
gx_page_queue_wait_one_page(gx_page_queue_t * queue)229 gx_page_queue_wait_one_page(
230 			       gx_page_queue_t * queue	/* queue to wait on */
231 )
232 {
233     int code;
234 
235     gx_monitor_enter(queue->monitor);
236     if (!queue->entry_count && !queue->dequeue_in_progress) {
237 	code = 0;
238 	gx_monitor_leave(queue->monitor);
239     } else {
240 	/* request acknowledgement on render done */
241 	queue->enable_render_done_signal = true;
242 
243 	/* exit monitor & wait for acknowlegement */
244 	gx_monitor_leave(queue->monitor);
245 	gx_semaphore_wait(queue->render_done_sema);
246 	code = 1;
247     }
248     return code;
249 }
250 
251 /* Wait for page queue to become empty */
252 void
gx_page_queue_wait_until_empty(gx_page_queue_t * queue)253 gx_page_queue_wait_until_empty(
254 				  gx_page_queue_t * queue	/* page queue to wait on */
255 )
256 {
257     while (gx_page_queue_wait_one_page(queue));
258 }
259 
260 /* -----------  Synchronized page_queue get/put routines ------ */
261 
262 /* Add an entry to page queue for rendering w/sync to renderer */
263 void
gx_page_queue_enqueue(gx_page_queue_entry_t * entry)264 gx_page_queue_enqueue(
265 			 gx_page_queue_entry_t * entry	/* entry to add */
266 )
267 {
268     gx_page_queue_t *queue = entry->queue;
269 
270     /* Add the goods to queue, & signal it */
271     gx_page_queue_add_last(entry);
272     gx_semaphore_signal(queue->render_req_sema);
273 }
274 
275 /* Add page to a page queue */
276 /* Even if an error is returned, entry will have been added to queue! */
277 int				/* rets 0 ok, gs_error_VMerror if error */
gx_page_queue_add_page(gx_page_queue_t * queue,gx_page_queue_action_t action,const gx_band_page_info_t * page_info,int page_count)278 gx_page_queue_add_page(
279 			  gx_page_queue_t * queue,	/* page queue to add to */
280 			  gx_page_queue_action_t action,	/* action code to queue */
281 			  const gx_band_page_info_t * page_info,  /* bandinfo incl. bandlist (or 0) */
282 			  int page_count	/* see comments in gdevprna.c */
283 )
284 {
285     int code = 0;
286 
287     /* Allocate a new page queue entry */
288     gx_page_queue_entry_t *entry
289     = gx_page_queue_entry_alloc(queue);
290 
291     if (!entry) {
292 	/* Use reserve page queue entry */
293 	gx_monitor_enter(queue->monitor);	/* not strictly necessary */
294 	entry = queue->reserve_entry;
295 	queue->reserve_entry = 0;
296 	gx_monitor_leave(queue->monitor);
297     }
298     /* Fill in page queue entry with info from device */
299     entry->action = action;
300     if (page_info != 0)
301 	entry->page_info = *page_info;
302     else
303 	entry->page_info = null_page_info;
304     entry->num_copies = page_count;
305 
306     /* Stick onto page queue & signal */
307     gx_page_queue_enqueue(entry);
308 
309     /* If a new reserve entry is needed, wait till enough mem is avail */
310     while (!queue->reserve_entry) {
311 	queue->reserve_entry = gx_page_queue_entry_alloc(queue);
312 	if (!queue->reserve_entry && !gx_page_queue_wait_one_page(queue)) {
313 	    /* Should never happen: all pages rendered & still can't get memory: give up! */
314 	    code = gs_note_error(gs_error_Fatal);
315 	    break;
316 	}
317     }
318     return code;
319 }
320 
321 /* Wait for & get next page queue entry */
322 gx_page_queue_entry_t *		/* removed entry */
gx_page_queue_start_dequeue(gx_page_queue_t * queue)323 gx_page_queue_start_dequeue(
324 			       gx_page_queue_t * queue	/* page queue to retrieve from */
325 )
326 {
327     gx_semaphore_wait(queue->render_req_sema);
328     queue->dequeue_in_progress = true;
329     return gx_page_queue_remove_first(queue);
330 }
331 
332 /* After rendering page gotten w/gx_page_queue_dequeue, call this to ack */
333 void
gx_page_queue_finish_dequeue(gx_page_queue_entry_t * entry)334 gx_page_queue_finish_dequeue(
335 				gx_page_queue_entry_t * entry	/* entry that was retrieved to delete */
336 )
337 {
338     gx_page_queue_t *queue = entry->queue;
339 
340     gx_monitor_enter(queue->monitor);
341     if (queue->enable_render_done_signal) {
342 	queue->enable_render_done_signal = false;
343 	gx_semaphore_signal(queue->render_done_sema);
344     }
345     queue->dequeue_in_progress = false;
346 
347     /*
348      * Delete the previously-allocated entry, do inside monitor in case
349      * this is the reserve entry & is the only memory in the universe;
350      * in that case gx_page_queue_add_page won't be looking for this
351      * until the monitor is exited.
352      * In this implementation of the page queue, clist and queue entries
353      * are managed together, so free the clist just before freeing the entry.
354      */
355     gx_page_queue_entry_free_page_info(entry);
356     gx_page_queue_entry_free(entry);
357 
358     gx_monitor_leave(queue->monitor);
359 }
360