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