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