~ubuntu-branches/ubuntu/jaunty/ghostscript/jaunty-updates

« back to all changes in this revision

Viewing changes to src/zcontext.c

  • Committer: Bazaar Package Importer
  • Author(s): Till Kamppeter
  • Date: 2009-01-20 16:40:45 UTC
  • mfrom: (1.1.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20090120164045-lnfhi0n30o5lwhwa
Tags: 8.64.dfsg.1~svn9377-0ubuntu1
* New upstream release (SVN rev 9377)
   o Fixes many bugs concerning PDF rendering, to make the PDF printing
     workflow correctly working.
   o Fixes long-standing bugs in many drivers, like input paper tray and
     duplex options not working for the built-in PCL 4, 5, 5c, 5e, and
     6/XL drivers, PDF input not working for bjc600, bjc800, and cups
     output devices, several options not working and uninitialized
     memory with cups output device.
   o Merged nearly all patches of the Ubuntu and Debian packages upstream.
   o Fixes LP: #317810, LP: #314439, LP: #314018.
* debian/patches/03_libpaper_support.dpatch,
  debian/patches/11_gs-cjk_font_glyph_handling_fix.dpatch,
  debian/patches/12_gs-cjk_vertical_writing_metrics_fix.dpatch,
  debian/patches/13_gs-cjk_cjkps_examples.dpatch,
  debian/patches/20_bbox_segv_fix.dpatch,
  debian/patches/21_brother_7x0_gdi_fix.dpatch,
  debian/patches/22_epsn_margin_workaround.dpatch,
  debian/patches/24_gs_man_fix.dpatch,
  debian/patches/25_toolbin_insecure_tmp_usage_fix.dpatch,
  debian/patches/26_assorted_script_fixes.dpatch,
  debian/patches/29_gs_css_fix.dpatch,
  debian/patches/30_ps2pdf_man_improvement.dpatch,
  debian/patches/31_fix-gc-sigbus.dpatch,
  debian/patches/34_ftbfs-on-hurd-fix.dpatch,
  debian/patches/35_disable_libcairo.dpatch,
  debian/patches/38_pxl-duplex.dpatch,
  debian/patches/39_pxl-resolution.dpatch,
  debian/patches/42_gs-init-ps-delaybind-fix.dpatch,
  debian/patches/45_bjc600-bjc800-pdf-input.dpatch,
  debian/patches/48_cups-output-device-pdf-duplex-uninitialized-memory-fix.dpatch,
  debian/patches/50_lips4-floating-point-exception.dpatch,
  debian/patches/52_cups-device-logging.dpatch,
  debian/patches/55_pcl-input-slot-fix.dpatch,
  debian/patches/57_pxl-input-slot-fix.dpatch,
  debian/patches/60_pxl-cups-driver-pdf.dpatch,
  debian/patches/62_onebitcmyk-pdf.dpatch,
  debian/patches/65_too-big-temp-files-1.dpatch,
  debian/patches/67_too-big-temp-files-2.dpatch,
  debian/patches/70_take-into-account-data-in-stream-buffer-before-refill.dpatch:
  Removed, applied upstream.
* debian/patches/01_docdir_fix_for_debian.dpatch,
  debian/patches/02_gs_man_fix_debian.dpatch,
  debian/patches/01_docdir-fix-for-debian.dpatch,
  debian/patches/02_docdir-fix-for-debian.dpatch: Renamed patches to
  make merging with Debian easier.
* debian/patches/32_improve-handling-of-media-size-changes-from-gv.dpatch, 
  debian/patches/33_bad-params-to-xinitimage-on-large-bitmaps.dpatch:
  regenerated for new source directory structure.
* debian/rules: Corrected paths to remove cidfmap (it is in Resource/Init/
  in GS 8.64) and to install headers (source paths are psi/ and base/ now).
* debian/rules: Remove all fontmaps, as DeFoMa replaces them.
* debian/local/pdftoraster/pdftoraster.c,
  debian/local/pdftoraster/pdftoraster.convs, debian/rules: Removed
  added pdftoraster filter and use the one which comes with Ghostscript.
* debian/ghostscript.links: s/8.63/8.64/

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (C) 2001-2006 Artifex Software, Inc.
2
 
   All Rights Reserved.
3
 
  
4
 
   This software is provided AS-IS with no warranty, either express or
5
 
   implied.
6
 
 
7
 
   This software is distributed under license and may not be copied, modified
8
 
   or distributed except as expressly authorized under the terms of that
9
 
   license.  Refer to licensing information at http://www.artifex.com/
10
 
   or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11
 
   San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12
 
*/
13
 
 
14
 
/* $Id: zcontext.c 8250 2007-09-25 13:31:24Z giles $ */
15
 
/* Display PostScript context operators */
16
 
#include "memory_.h"
17
 
#include "ghost.h"
18
 
#include "gp.h"                 /* for usertime */
19
 
#include "oper.h"
20
 
#include "gsexit.h"
21
 
#include "gsgc.h"
22
 
#include "gsstruct.h"
23
 
#include "gsutil.h"
24
 
#include "gxalloc.h"
25
 
#include "gxstate.h"            /* for copying gstate stack */
26
 
#include "stream.h"             /* for files.h */
27
 
#include "files.h"
28
 
#include "idict.h"
29
 
#include "igstate.h"
30
 
#include "icontext.h"
31
 
#include "interp.h"
32
 
#include "isave.h"
33
 
#include "istruct.h"
34
 
#include "dstack.h"
35
 
#include "estack.h"
36
 
#include "ostack.h"
37
 
#include "store.h"
38
 
 
39
 
/*
40
 
 * Define the rescheduling interval.  A value of max_int effectively
41
 
 * disables scheduling.  The only reason not to make this const is to
42
 
 * allow it to be changed during testing.
43
 
 */
44
 
static int reschedule_interval = 100;
45
 
 
46
 
/* Scheduling hooks in interp.c */
47
 
extern int (*gs_interp_reschedule_proc)(i_ctx_t **);
48
 
extern int (*gs_interp_time_slice_proc)(i_ctx_t **);
49
 
extern int gs_interp_time_slice_ticks;
50
 
 
51
 
/* Context structure */
52
 
typedef enum {
53
 
    cs_active,
54
 
    cs_done
55
 
} ctx_status_t;
56
 
typedef long ctx_index_t;       /* >= 0 */
57
 
typedef struct gs_context_s gs_context_t;
58
 
typedef struct gs_scheduler_s gs_scheduler_t;
59
 
 
60
 
/*
61
 
 * If several contexts share local VM, then if any one of them has done an
62
 
 * unmatched save, the others are not allowed to run.  We handle this by
63
 
 * maintaining the following invariant:
64
 
 *      When control reaches the point in the scheduler that decides
65
 
 *      what context to run next, then for each group of contexts
66
 
 *      sharing local VM, if the save level for that VM is non-zero,
67
 
 *      saved_local_vm is only set in the context that has unmatched
68
 
 *      saves.
69
 
 * We maintain this invariant as follows: when control enters the
70
 
 * scheduler, if a context was running, we set its saved_local_vm flag
71
 
 * to (save_level > 0).  When selecting a context to run, we ignore
72
 
 * contexts where saved_local_vm is false and the local VM save_level > 0.
73
 
 */
74
 
struct gs_context_s {
75
 
    gs_context_state_t state;   /* (must be first for subclassing) */
76
 
    /* Private state */
77
 
    gs_scheduler_t *scheduler;
78
 
    ctx_status_t status;
79
 
    ctx_index_t index;          /* > 0 */
80
 
    bool detach;                /* true if a detach has been */
81
 
                                /* executed for this context */
82
 
    bool saved_local_vm;        /* (see above) */
83
 
    bool visible;               /* during GC, true if visible; */
84
 
                                /* otherwise, always true */
85
 
    ctx_index_t next_index;     /* next context with same status */
86
 
                                /* (active, waiting on same lock, */
87
 
                                /* waiting on same condition, */
88
 
                                /* waiting to be destroyed) */
89
 
    ctx_index_t joiner_index;   /* context waiting on a join */
90
 
                                /* for this one */
91
 
    gs_context_t *table_next;   /* hash table chain -- this must be a real */
92
 
                                /* pointer, for looking up indices */
93
 
};
94
 
static inline bool
95
 
context_is_visible(const gs_context_t *pctx)
96
 
{
97
 
    return (pctx && pctx->visible);
98
 
}
99
 
static inline gs_context_t *
100
 
visible_context(gs_context_t *pctx)
101
 
{
102
 
    return (pctx && pctx->visible ? pctx : (gs_context_t *)0);
103
 
}
104
 
 
105
 
/* GC descriptor */
106
 
static 
107
 
CLEAR_MARKS_PROC(context_clear_marks)
108
 
{
109
 
    gs_context_t *const pctx = vptr;
110
 
 
111
 
    (*st_context_state.clear_marks)
112
 
        (cmem, &pctx->state, sizeof(pctx->state), &st_context_state);
113
 
}
114
 
static 
115
 
ENUM_PTRS_WITH(context_enum_ptrs, gs_context_t *pctx)
116
 
ENUM_PREFIX(st_context_state, 2);
117
 
case 0: return ENUM_OBJ(pctx->scheduler);
118
 
case 1: {
119
 
    /* Return the next *visible* context. */
120
 
    const gs_context_t *next = pctx->table_next;
121
 
 
122
 
    while (next && !next->visible)
123
 
        next = next->table_next;
124
 
    return ENUM_OBJ(next);
125
 
}
126
 
ENUM_PTRS_END
127
 
static RELOC_PTRS_WITH(context_reloc_ptrs, gs_context_t *pctx)
128
 
    RELOC_PREFIX(st_context_state);
129
 
    RELOC_VAR(pctx->scheduler);
130
 
    /* Don't relocate table_next -- the scheduler object handles that. */
131
 
RELOC_PTRS_END
132
 
gs_private_st_complex_only(st_context, gs_context_t, "gs_context_t",
133
 
             context_clear_marks, context_enum_ptrs, context_reloc_ptrs, 0);
134
 
 
135
 
/*
136
 
 * Context list structure.  Note that this uses context indices, not
137
 
 * pointers, to avoid having to worry about pointers between local VMs.
138
 
 */
139
 
typedef struct ctx_list_s {
140
 
    ctx_index_t head_index;
141
 
    ctx_index_t tail_index;
142
 
} ctx_list_t;
143
 
 
144
 
/* Condition structure */
145
 
typedef struct gs_condition_s {
146
 
    ctx_list_t waiting; /* contexts waiting on this condition */
147
 
} gs_condition_t;
148
 
gs_private_st_simple(st_condition, gs_condition_t, "conditiontype");
149
 
 
150
 
/* Lock structure */
151
 
typedef struct gs_lock_s {
152
 
    ctx_list_t waiting;         /* contexts waiting for this lock, */
153
 
                                /* must be first for subclassing */
154
 
    ctx_index_t holder_index;   /* context holding the lock, if any */
155
 
    gs_scheduler_t *scheduler;
156
 
} gs_lock_t;
157
 
gs_private_st_ptrs1(st_lock, gs_lock_t, "locktype",
158
 
                    lock_enum_ptrs, lock_reloc_ptrs, scheduler);
159
 
 
160
 
/* Global state */
161
 
/*typedef struct gs_scheduler_s gs_scheduler_t; *//* (above) */
162
 
struct gs_scheduler_s {
163
 
    gs_context_t *current;
164
 
    long usertime_initial;      /* usertime when current started running */
165
 
    ctx_list_t active;
166
 
    vm_reclaim_proc((*save_vm_reclaim));
167
 
    ctx_index_t dead_index;
168
 
#define CTX_TABLE_SIZE 19
169
 
    gs_context_t *table[CTX_TABLE_SIZE];
170
 
};
171
 
 
172
 
/* Convert a context index to a context pointer. */
173
 
static gs_context_t *
174
 
index_context(const gs_scheduler_t *psched, long index)
175
 
{
176
 
    gs_context_t *pctx;
177
 
 
178
 
    if (index == 0)
179
 
        return 0;
180
 
    pctx = psched->table[index % CTX_TABLE_SIZE];
181
 
    while (pctx != 0 && pctx->index != index)
182
 
        pctx = pctx->table_next;
183
 
    return pctx;
184
 
}
185
 
 
186
 
/* Structure definition */
187
 
gs_private_st_composite(st_scheduler, gs_scheduler_t, "gs_scheduler",
188
 
                        scheduler_enum_ptrs, scheduler_reloc_ptrs);
189
 
/*
190
 
 * The only cross-local-VM pointers in the context machinery are the
191
 
 * table_next pointers in contexts, and the current and table[] pointers
192
 
 * in the scheduler.  We need to handle all of these specially.
193
 
 */
194
 
static ENUM_PTRS_WITH(scheduler_enum_ptrs, gs_scheduler_t *psched)
195
 
{
196
 
    index -= 1;
197
 
    if (index < CTX_TABLE_SIZE) {
198
 
        gs_context_t *pctx = psched->table[index];
199
 
 
200
 
        while (pctx && !pctx->visible)
201
 
            pctx = pctx->table_next;
202
 
        return ENUM_OBJ(pctx);
203
 
    }
204
 
    return 0;
205
 
}
206
 
case 0: return ENUM_OBJ(visible_context(psched->current));
207
 
ENUM_PTRS_END
208
 
static RELOC_PTRS_WITH(scheduler_reloc_ptrs, gs_scheduler_t *psched)
209
 
{
210
 
    if (psched->current->visible)
211
 
        RELOC_VAR(psched->current);
212
 
    {
213
 
        int i;
214
 
 
215
 
        for (i = 0; i < CTX_TABLE_SIZE; ++i) {
216
 
            gs_context_t **ppctx = &psched->table[i];
217
 
            gs_context_t **pnext;
218
 
 
219
 
            for (; *ppctx; ppctx = pnext) {
220
 
                pnext = &(*ppctx)->table_next;
221
 
                if ((*ppctx)->visible)
222
 
                    RELOC_VAR(*ppctx);
223
 
            }
224
 
        }
225
 
    }
226
 
}
227
 
RELOC_PTRS_END
228
 
 
229
 
/*
230
 
 * The context scheduler requires special handling during garbage
231
 
 * collection, since it is the only structure that can legitimately
232
 
 * reference objects in multiple local VMs.  To deal with this, we wrap the
233
 
 * interpreter's garbage collector with code that prevents it from seeing
234
 
 * contexts in other than the current local VM.  ****** WORKS FOR LOCAL GC,
235
 
 * NOT FOR GLOBAL ******
236
 
 */
237
 
static void
238
 
context_reclaim(vm_spaces * pspaces, bool global)
239
 
{
240
 
    /*
241
 
     * Search through the registered roots to find the current context.
242
 
     * (This is a hack so we can find the scheduler.)
243
 
     */
244
 
    int i;
245
 
    gs_context_t *pctx = 0;     /* = 0 is bogus to pacify compilers */
246
 
    gs_scheduler_t *psched = 0;
247
 
    gs_ref_memory_t *lmem = 0;  /* = 0 is bogus to pacify compilers */
248
 
    chunk_locator_t loc;
249
 
 
250
 
    for (i = countof(pspaces->memories.indexed) - 1; psched == 0 && i > 0; --i) {
251
 
        gs_ref_memory_t *mem = pspaces->memories.indexed[i];
252
 
        const gs_gc_root_t *root = mem->roots;
253
 
 
254
 
        for (; root; root = root->next) {
255
 
            if (gs_object_type((gs_memory_t *)mem, *root->p) == &st_context) {
256
 
                pctx = *root->p;
257
 
                psched = pctx->scheduler;
258
 
                lmem = mem;
259
 
                break;
260
 
            }
261
 
        }
262
 
    }
263
 
 
264
 
    /* Hide all contexts in other (local) VMs. */
265
 
    /*
266
 
     * See context_create below for why we look for the context
267
 
     * in stable memory.
268
 
     */
269
 
    loc.memory = (gs_ref_memory_t *)gs_memory_stable((gs_memory_t *)lmem);
270
 
    loc.cp = 0;
271
 
    for (i = 0; i < CTX_TABLE_SIZE; ++i)
272
 
        for (pctx = psched->table[i]; pctx; pctx = pctx->table_next)
273
 
            pctx->visible = chunk_locate_ptr(pctx, &loc);
274
 
 
275
 
#ifdef DEBUG
276
 
    if (!psched->current->visible) {
277
 
        lprintf("Current context is invisible!\n");
278
 
        gs_abort((gs_memory_t *)lmem);
279
 
    }
280
 
#endif
281
 
 
282
 
    /* Do the actual garbage collection. */
283
 
    psched->save_vm_reclaim(pspaces, global);
284
 
 
285
 
    /* Make all contexts visible again. */
286
 
    for (i = 0; i < CTX_TABLE_SIZE; ++i)
287
 
        for (pctx = psched->table[i]; pctx; pctx = pctx->table_next)
288
 
            pctx->visible = true;
289
 
}
290
 
 
291
 
 
292
 
/* Forward references */
293
 
static int context_create(gs_scheduler_t *, gs_context_t **,
294
 
                           const gs_dual_memory_t *,
295
 
                           const gs_context_state_t *, bool);
296
 
static long context_usertime(void);
297
 
static int context_param(const gs_scheduler_t *, os_ptr, gs_context_t **);
298
 
static void context_destroy(gs_context_t *);
299
 
static void stack_copy(ref_stack_t *, const ref_stack_t *, uint, uint);
300
 
static int lock_acquire(os_ptr, gs_context_t *);
301
 
static int lock_release(ref *);
302
 
 
303
 
/* Internal procedures */
304
 
static void
305
 
context_load(gs_scheduler_t *psched, gs_context_t *pctx)
306
 
{
307
 
    if_debug1('"', "[\"]loading %ld\n", pctx->index);
308
 
    if ( pctx->state.keep_usertime )
309
 
      psched->usertime_initial = context_usertime();
310
 
    context_state_load(&pctx->state);
311
 
}
312
 
static void
313
 
context_store(gs_scheduler_t *psched, gs_context_t *pctx)
314
 
{
315
 
    if_debug1('"', "[\"]storing %ld\n", pctx->index);
316
 
    context_state_store(&pctx->state);
317
 
    if ( pctx->state.keep_usertime )
318
 
      pctx->state.usertime_total +=
319
 
        context_usertime() - psched->usertime_initial;
320
 
}
321
 
 
322
 
/* List manipulation */
323
 
static void
324
 
add_last(const gs_scheduler_t *psched, ctx_list_t *pl, gs_context_t *pc)
325
 
{
326
 
    pc->next_index = 0;
327
 
    if (pl->head_index == 0)
328
 
        pl->head_index = pc->index;
329
 
    else
330
 
        index_context(psched, pl->tail_index)->next_index = pc->index;
331
 
    pl->tail_index = pc->index;
332
 
}
333
 
 
334
 
/* ------ Initialization ------ */
335
 
 
336
 
static int ctx_initialize(i_ctx_t **);
337
 
static int ctx_reschedule(i_ctx_t **);
338
 
static int ctx_time_slice(i_ctx_t **);
339
 
static int
340
 
zcontext_init(i_ctx_t *i_ctx_p)
341
 
{
342
 
    /* Complete initialization after the interpreter is entered. */
343
 
    gs_interp_reschedule_proc = ctx_initialize;
344
 
    gs_interp_time_slice_proc = ctx_initialize;
345
 
    gs_interp_time_slice_ticks = 0;
346
 
    return 0;
347
 
}
348
 
/*
349
 
 * The interpreter calls this procedure at the first reschedule point.
350
 
 * It completes context initialization.
351
 
 */
352
 
static int
353
 
ctx_initialize(i_ctx_t **pi_ctx_p)
354
 
{
355
 
    i_ctx_t *i_ctx_p = *pi_ctx_p; /* for gs_imemory */
356
 
    gs_ref_memory_t *imem = iimemory_system;
357
 
    gs_scheduler_t *psched =
358
 
        gs_alloc_struct_immovable((gs_memory_t *) imem, gs_scheduler_t,
359
 
                                  &st_scheduler, "gs_scheduler");
360
 
 
361
 
    psched->current = 0;
362
 
    psched->active.head_index = psched->active.tail_index = 0;
363
 
    psched->save_vm_reclaim = i_ctx_p->memory.spaces.vm_reclaim;
364
 
    i_ctx_p->memory.spaces.vm_reclaim = context_reclaim;
365
 
    psched->dead_index = 0;
366
 
    memset(psched->table, 0, sizeof(psched->table));
367
 
    /* Create an initial context. */
368
 
    if (context_create(psched, &psched->current, &gs_imemory, *pi_ctx_p, true) < 0) {
369
 
        lprintf("Can't create initial context!");
370
 
        gs_abort(imemory);
371
 
    }
372
 
    psched->current->scheduler = psched;
373
 
    /* Hook into the interpreter. */
374
 
    *pi_ctx_p = &psched->current->state;
375
 
    gs_interp_reschedule_proc = ctx_reschedule;
376
 
    gs_interp_time_slice_proc = ctx_time_slice;
377
 
    gs_interp_time_slice_ticks = reschedule_interval;
378
 
    return 0;
379
 
}
380
 
 
381
 
/* ------ Interpreter interface to scheduler ------ */
382
 
 
383
 
/* When an operator decides it is time to run a new context, */
384
 
/* it returns o_reschedule.  The interpreter saves all its state in */
385
 
/* memory, calls ctx_reschedule, and then loads the state from memory. */
386
 
static int
387
 
ctx_reschedule(i_ctx_t **pi_ctx_p)
388
 
{
389
 
    gs_context_t *current = (gs_context_t *)*pi_ctx_p;
390
 
    gs_scheduler_t *psched = current->scheduler;
391
 
 
392
 
#ifdef DEBUG
393
 
    if (*pi_ctx_p != &current->state) {
394
 
        lprintf2("current->state = 0x%lx, != i_ctx_p = 0x%lx!\n",
395
 
                 (ulong)&current->state, (ulong)*pi_ctx_p);
396
 
    }
397
 
#endif
398
 
    /* If there are any dead contexts waiting to be released, */
399
 
    /* take care of that now. */
400
 
    while (psched->dead_index != 0) {
401
 
        gs_context_t *dead = index_context(psched, psched->dead_index);
402
 
        long next_index = dead->next_index;
403
 
 
404
 
        if (current == dead) {
405
 
            if_debug1('"', "[\"]storing dead %ld\n", current->index);
406
 
            context_state_store(&current->state);
407
 
            current = 0;
408
 
        }
409
 
        context_destroy(dead);
410
 
        psched->dead_index = next_index;
411
 
    }
412
 
    /* Update saved_local_vm.  See above for the invariant. */
413
 
    if (current != 0)
414
 
        current->saved_local_vm =
415
 
            current->state.memory.space_local->saved != 0;
416
 
    /* Run the first ready context, taking the 'save' lock into account. */
417
 
    {
418
 
        gs_context_t *prev = 0;
419
 
        gs_context_t *ready;
420
 
 
421
 
        for (ready = index_context(psched, psched->active.head_index);;
422
 
             prev = ready, ready = index_context(psched, ready->next_index)
423
 
            ) {
424
 
            if (ready == 0) {
425
 
                if (current != 0)
426
 
                    context_store(psched, current);
427
 
                lprintf("No context to run!");
428
 
                return_error(e_Fatal);
429
 
            }
430
 
            /* See above for an explanation of the following test. */
431
 
            if (ready->state.memory.space_local->saved != 0 &&
432
 
                !ready->saved_local_vm
433
 
                )
434
 
                continue;
435
 
            /* Found a context to run. */
436
 
            {
437
 
                ctx_index_t next_index = ready->next_index;
438
 
 
439
 
                if (prev)
440
 
                    prev->next_index = next_index;
441
 
                else
442
 
                    psched->active.head_index = next_index;
443
 
                if (!next_index)
444
 
                    psched->active.tail_index = (prev ? prev->index : 0);
445
 
            }
446
 
            break;
447
 
        }
448
 
        if (ready == current)
449
 
            return 0;           /* no switch */
450
 
        /*
451
 
         * Save the state of the current context in psched->current,
452
 
         * if any context is current.
453
 
         */
454
 
        if (current != 0)
455
 
            context_store(psched, current);
456
 
        psched->current = ready;
457
 
        /* Load the state of the new current context. */
458
 
        context_load(psched, ready);
459
 
        /* Switch the interpreter's context state pointer. */
460
 
        *pi_ctx_p = &ready->state;
461
 
    }
462
 
    return 0;
463
 
}
464
 
 
465
 
/* If the interpreter wants to time-slice, it saves its state, */
466
 
/* calls ctx_time_slice, and reloads its state. */
467
 
static int
468
 
ctx_time_slice(i_ctx_t **pi_ctx_p)
469
 
{
470
 
    gs_scheduler_t *psched = ((gs_context_t *)*pi_ctx_p)->scheduler;
471
 
 
472
 
    if (psched->active.head_index == 0)
473
 
        return 0;
474
 
    if_debug0('"', "[\"]time-slice\n");
475
 
    add_last(psched, &psched->active, psched->current);
476
 
    return ctx_reschedule(pi_ctx_p);
477
 
}
478
 
 
479
 
/* ------ Context operators ------ */
480
 
 
481
 
/* - currentcontext <context> */
482
 
static int
483
 
zcurrentcontext(i_ctx_t *i_ctx_p)
484
 
{
485
 
    os_ptr op = osp;
486
 
    const gs_context_t *current = (const gs_context_t *)i_ctx_p;
487
 
 
488
 
    push(1);
489
 
    make_int(op, current->index);
490
 
    return 0;
491
 
}
492
 
 
493
 
/* <context> detach - */
494
 
static int
495
 
zdetach(i_ctx_t *i_ctx_p)
496
 
{
497
 
    os_ptr op = osp;
498
 
    const gs_scheduler_t *psched = ((gs_context_t *)i_ctx_p)->scheduler;
499
 
    gs_context_t *pctx;
500
 
    int code;
501
 
 
502
 
    if ((code = context_param(psched, op, &pctx)) < 0)
503
 
        return code;
504
 
    if_debug2('\'', "[']detach %ld, status = %d\n",
505
 
              pctx->index, pctx->status);
506
 
    if (pctx->joiner_index != 0 || pctx->detach)
507
 
        return_error(e_invalidcontext);
508
 
    switch (pctx->status) {
509
 
        case cs_active:
510
 
            pctx->detach = true;
511
 
            break;
512
 
        case cs_done:
513
 
            context_destroy(pctx);
514
 
    }
515
 
    pop(1);
516
 
    return 0;
517
 
}
518
 
 
519
 
static int
520
 
    do_fork(i_ctx_t *i_ctx_p, os_ptr op, const ref * pstdin,
521
 
            const ref * pstdout, uint mcount, bool local),
522
 
    values_older_than(const ref_stack_t * pstack, uint first, uint last,
523
 
                      int max_space);
524
 
static int
525
 
    fork_done(i_ctx_t *),
526
 
    fork_done_with_error(i_ctx_t *),
527
 
    finish_join(i_ctx_t *),
528
 
    reschedule_now(i_ctx_t *);
529
 
 
530
 
/* <mark> <obj1> ... <objN> <proc> .fork <context> */
531
 
/* <mark> <obj1> ... <objN> <proc> <stdin|null> <stdout|null> */
532
 
/*   .localfork <context> */
533
 
static int
534
 
zfork(i_ctx_t *i_ctx_p)
535
 
{
536
 
    os_ptr op = osp;
537
 
    uint mcount = ref_stack_counttomark(&o_stack);
538
 
    ref rnull;
539
 
 
540
 
    if (mcount == 0)
541
 
        return_error(e_unmatchedmark);
542
 
    make_null(&rnull);
543
 
    return do_fork(i_ctx_p, op, &rnull, &rnull, mcount, false);
544
 
}
545
 
static int
546
 
zlocalfork(i_ctx_t *i_ctx_p)
547
 
{
548
 
    os_ptr op = osp;
549
 
    uint mcount = ref_stack_counttomark(&o_stack);
550
 
    int code;
551
 
 
552
 
    if (mcount == 0)
553
 
        return_error(e_unmatchedmark);
554
 
    code = values_older_than(&o_stack, 1, mcount - 1, avm_local);
555
 
    if (code < 0)
556
 
        return code;
557
 
    code = do_fork(i_ctx_p, op - 2, op - 1, op, mcount - 2, true);
558
 
    if (code < 0)
559
 
        return code;
560
 
    op = osp;
561
 
    op[-2] = *op;
562
 
    pop(2);
563
 
    return code;
564
 
}
565
 
 
566
 
/* Internal procedure to actually do the fork operation. */
567
 
static int
568
 
do_fork(i_ctx_t *i_ctx_p, os_ptr op, const ref * pstdin, const ref * pstdout,
569
 
        uint mcount, bool local)
570
 
{
571
 
    gs_context_t *pcur = (gs_context_t *)i_ctx_p;
572
 
    gs_scheduler_t *psched = pcur->scheduler;
573
 
    stream *s;
574
 
    gs_dual_memory_t dmem;
575
 
    gs_context_t *pctx;
576
 
    ref old_userdict, new_userdict;
577
 
    int code;
578
 
 
579
 
    check_proc(*op);
580
 
    if (iimemory_local->save_level)
581
 
        return_error(e_invalidcontext);
582
 
    if (r_has_type(pstdout, t_null)) {
583
 
        code = zget_stdout(i_ctx_p, &s);
584
 
        if (code < 0)
585
 
            return code;
586
 
        pstdout = &ref_stdio[1];
587
 
    } else
588
 
        check_read_file(s, pstdout);
589
 
    if (r_has_type(pstdin, t_null)) {
590
 
        code = zget_stdin(i_ctx_p, &s);
591
 
        if (code < 0)
592
 
            return code;
593
 
        pstdin = &ref_stdio[0];
594
 
    } else
595
 
        check_read_file(s, pstdin);
596
 
    dmem = gs_imemory;
597
 
    if (local) {
598
 
        /* Share global VM, private local VM. */
599
 
        ref *puserdict;
600
 
        uint userdict_size;
601
 
        gs_memory_t *parent = iimemory_local->non_gc_memory;
602
 
        gs_ref_memory_t *lmem;
603
 
        gs_ref_memory_t *lmem_stable;
604
 
 
605
 
        if (dict_find_string(systemdict, "userdict", &puserdict) <= 0 ||
606
 
            !r_has_type(puserdict, t_dictionary)
607
 
            )
608
 
            return_error(e_Fatal);
609
 
        old_userdict = *puserdict;
610
 
        userdict_size = dict_maxlength(&old_userdict);
611
 
        lmem = ialloc_alloc_state(parent, iimemory_local->chunk_size);
612
 
        lmem_stable = ialloc_alloc_state(parent, iimemory_local->chunk_size);
613
 
        if (lmem == 0 || lmem_stable == 0) {
614
 
            gs_free_object(parent, lmem_stable, "do_fork");
615
 
            gs_free_object(parent, lmem, "do_fork");
616
 
            return_error(e_VMerror);
617
 
        }
618
 
        lmem->space = avm_local;
619
 
        lmem_stable->space = avm_local;
620
 
        lmem->stable_memory = (gs_memory_t *)lmem_stable;
621
 
        dmem.space_local = lmem;
622
 
        code = context_create(psched, &pctx, &dmem, &pcur->state, false);
623
 
        if (code < 0) {
624
 
            /****** FREE lmem ******/
625
 
            return code;
626
 
        }
627
 
        /*
628
 
         * Create a new userdict.  PostScript code will take care of
629
 
         * the rest of the initialization of the new context.
630
 
         */
631
 
        code = dict_alloc(lmem, userdict_size, &new_userdict);
632
 
        if (code < 0) {
633
 
            context_destroy(pctx);
634
 
            /****** FREE lmem ******/
635
 
            return code;
636
 
        }
637
 
    } else {
638
 
        /* Share global and local VM. */
639
 
        code = context_create(psched, &pctx, &dmem, &pcur->state, false);
640
 
        if (code < 0) {
641
 
            /****** FREE lmem ******/
642
 
            return code;
643
 
        }
644
 
        /*
645
 
         * Copy the gstate stack.  The current method is not elegant;
646
 
         * in fact, I'm not entirely sure it works.
647
 
         */
648
 
        {
649
 
            int n;
650
 
            const gs_state *old;
651
 
            gs_state *new;
652
 
 
653
 
            for (n = 0, old = igs; old != 0; old = gs_state_saved(old))
654
 
                ++n;
655
 
            for (old = pctx->state.pgs; old != 0; old = gs_state_saved(old))
656
 
                --n;
657
 
            for (; n > 0 && code >= 0; --n)
658
 
                code = gs_gsave(pctx->state.pgs);
659
 
            if (code < 0) {
660
 
/****** FREE lmem & GSTATES ******/
661
 
                return code;
662
 
            }
663
 
            for (old = igs, new = pctx->state.pgs;
664
 
                 old != 0 /* (== new != 0) */  && code >= 0;
665
 
                 old = gs_state_saved(old), new = gs_state_saved(new)
666
 
                )
667
 
                code = gs_setgstate(new, old);
668
 
            if (code < 0) {
669
 
/****** FREE lmem & GSTATES ******/
670
 
                return code;
671
 
            }
672
 
        }
673
 
    }
674
 
    pctx->state.language_level = i_ctx_p->language_level;
675
 
    pctx->state.dict_stack.min_size = idict_stack.min_size;
676
 
    pctx->state.dict_stack.userdict_index = idict_stack.userdict_index;
677
 
    pctx->state.stdio[0] = *pstdin;
678
 
    pctx->state.stdio[1] = *pstdout;
679
 
    pctx->state.stdio[2] = pcur->state.stdio[2];
680
 
    /* Initialize the interpreter stacks. */
681
 
    {
682
 
        ref_stack_t *dstack = (ref_stack_t *)&pctx->state.dict_stack;
683
 
        uint count = ref_stack_count(&d_stack);
684
 
        uint copy = (local ? min_dstack_size : count);
685
 
 
686
 
        ref_stack_push(dstack, copy);
687
 
        stack_copy(dstack, &d_stack, copy, count - copy);
688
 
        if (local) {
689
 
            /* Substitute the new userdict for the old one. */
690
 
            long i;
691
 
 
692
 
            for (i = 0; i < copy; ++i) {
693
 
                ref *pdref = ref_stack_index(dstack, i);
694
 
 
695
 
                if (obj_eq(imemory, pdref, &old_userdict))
696
 
                    *pdref = new_userdict;
697
 
            }
698
 
        }
699
 
    }
700
 
    {
701
 
        ref_stack_t *estack = (ref_stack_t *)&pctx->state.exec_stack;
702
 
 
703
 
        ref_stack_push(estack, 3);
704
 
        /* fork_done must be executed in both normal and error cases. */
705
 
        make_mark_estack(estack->p - 2, es_other, fork_done_with_error);
706
 
        make_oper(estack->p - 1, 0, fork_done);
707
 
        *estack->p = *op;
708
 
    }
709
 
    {
710
 
        ref_stack_t *ostack = (ref_stack_t *)&pctx->state.op_stack;
711
 
        uint count = mcount - 2;
712
 
 
713
 
        ref_stack_push(ostack, count);
714
 
        stack_copy(ostack, &o_stack, count, osp - op + 1);
715
 
    }
716
 
    pctx->state.binary_object_format = pcur->state.binary_object_format;
717
 
    add_last(psched, &psched->active, pctx);
718
 
    pop(mcount - 1);
719
 
    op = osp;
720
 
    make_int(op, pctx->index);
721
 
    return 0;
722
 
}
723
 
 
724
 
/*
725
 
 * Check that all values being passed by fork or join are old enough
726
 
 * to be valid in the environment to which they are being transferred.
727
 
 */
728
 
static int
729
 
values_older_than(const ref_stack_t * pstack, uint first, uint last,
730
 
                  int next_space)
731
 
{
732
 
    uint i;
733
 
 
734
 
    for (i = first; i <= last; ++i)
735
 
        if (r_space(ref_stack_index(pstack, (long)i)) >= next_space)
736
 
            return_error(e_invalidaccess);
737
 
    return 0;
738
 
}
739
 
 
740
 
/* This gets executed when a context terminates normally. */
741
 
/****** MUST DO ALL RESTORES ******/
742
 
/****** WHAT IF invalidrestore? ******/
743
 
static int
744
 
fork_done(i_ctx_t *i_ctx_p)
745
 
{
746
 
    os_ptr op = osp;
747
 
    gs_context_t *pcur = (gs_context_t *)i_ctx_p;
748
 
    gs_scheduler_t *psched = pcur->scheduler;
749
 
 
750
 
    if_debug2('\'', "[']done %ld%s\n", pcur->index,
751
 
              (pcur->detach ? ", detached" : ""));
752
 
    /*
753
 
     * Clear the context's dictionary, execution and graphics stacks
754
 
     * now, to retain as little as possible in case of a garbage
755
 
     * collection or restore.  We know that fork_done is the
756
 
     * next-to-bottom entry on the execution stack.
757
 
     */
758
 
    ref_stack_pop_to(&d_stack, min_dstack_size);
759
 
    pop_estack(&pcur->state, ref_stack_count(&e_stack) - 1);
760
 
    gs_grestoreall(igs);
761
 
    /*
762
 
     * If there are any unmatched saves, we need to execute restores
763
 
     * until there aren't.  An invalidrestore is possible and will
764
 
     * result in an error termination.
765
 
     */
766
 
    if (iimemory_local->save_level) {
767
 
        ref *prestore;
768
 
 
769
 
        if (dict_find_string(systemdict, "restore", &prestore) <= 0) {
770
 
            lprintf("restore not found in systemdict!");
771
 
            return_error(e_Fatal);
772
 
        }
773
 
        if (pcur->detach) {
774
 
            ref_stack_clear(&o_stack);  /* help avoid invalidrestore */
775
 
            op = osp;
776
 
        }
777
 
        push(1);
778
 
        make_tv(op, t_save, saveid, alloc_save_current_id(&gs_imemory));
779
 
        push_op_estack(fork_done);
780
 
        ++esp;
781
 
        ref_assign(esp, prestore);
782
 
        return o_push_estack;
783
 
    }
784
 
    if (pcur->detach) {
785
 
        /*
786
 
         * We would like to free the context's memory, but we can't do
787
 
         * it yet, because the interpreter still has references to it.
788
 
         * Instead, queue the context to be freed the next time we
789
 
         * reschedule.  We can, however, clear its operand stack now.
790
 
         */
791
 
        ref_stack_clear(&o_stack);
792
 
        context_store(psched, pcur);
793
 
        pcur->next_index = psched->dead_index;
794
 
        psched->dead_index = pcur->index;
795
 
        psched->current = 0;
796
 
    } else {
797
 
        gs_context_t *pctx = index_context(psched, pcur->joiner_index);
798
 
 
799
 
        pcur->status = cs_done;
800
 
        /* Schedule the context waiting to join this one, if any. */
801
 
        if (pctx != 0)
802
 
            add_last(psched, &psched->active, pctx);
803
 
    }
804
 
    return o_reschedule;
805
 
}
806
 
/*
807
 
 * This gets executed when the stack is being unwound for an error
808
 
 * termination.
809
 
 */
810
 
static int
811
 
fork_done_with_error(i_ctx_t *i_ctx_p)
812
 
{
813
 
/****** WHAT TO DO? ******/
814
 
    return fork_done(i_ctx_p);
815
 
}
816
 
 
817
 
/* <context> join <mark> <obj1> ... <objN> */
818
 
static int
819
 
zjoin(i_ctx_t *i_ctx_p)
820
 
{
821
 
    os_ptr op = osp;
822
 
    gs_context_t *current = (gs_context_t *)i_ctx_p;
823
 
    gs_scheduler_t *psched = current->scheduler;
824
 
    gs_context_t *pctx;
825
 
    int code;
826
 
 
827
 
    if ((code = context_param(psched, op, &pctx)) < 0)
828
 
        return code;
829
 
    if_debug2('\'', "[']join %ld, status = %d\n",
830
 
              pctx->index, pctx->status);
831
 
    /*
832
 
     * It doesn't seem logically necessary, but the Red Book says that
833
 
     * the context being joined must share both global and local VM with
834
 
     * the current context.
835
 
     */
836
 
    if (pctx->joiner_index != 0 || pctx->detach || pctx == current ||
837
 
        pctx->state.memory.space_global !=
838
 
          current->state.memory.space_global ||
839
 
        pctx->state.memory.space_local !=
840
 
          current->state.memory.space_local ||
841
 
        iimemory_local->save_level != 0
842
 
        )
843
 
        return_error(e_invalidcontext);
844
 
    switch (pctx->status) {
845
 
        case cs_active:
846
 
            /*
847
 
             * We need to re-execute the join after the joined
848
 
             * context is done.  Since we can't return both
849
 
             * o_push_estack and o_reschedule, we push a call on
850
 
             * reschedule_now, which accomplishes the latter.
851
 
             */
852
 
            check_estack(2);
853
 
            push_op_estack(finish_join);
854
 
            push_op_estack(reschedule_now);
855
 
            pctx->joiner_index = current->index;
856
 
            return o_push_estack;
857
 
        case cs_done:
858
 
            {
859
 
                const ref_stack_t *ostack =
860
 
                    (ref_stack_t *)&pctx->state.op_stack;
861
 
                uint count = ref_stack_count(ostack);
862
 
 
863
 
                push(count);
864
 
                {
865
 
                    ref *rp = ref_stack_index(&o_stack, count);
866
 
 
867
 
                    make_mark(rp);
868
 
                }
869
 
                stack_copy(&o_stack, ostack, count, 0);
870
 
                context_destroy(pctx);
871
 
            }
872
 
    }
873
 
    return 0;
874
 
}
875
 
 
876
 
/* Finish a deferred join. */
877
 
static int
878
 
finish_join(i_ctx_t *i_ctx_p)
879
 
{
880
 
    os_ptr op = osp;
881
 
    gs_context_t *current = (gs_context_t *)i_ctx_p;
882
 
    gs_scheduler_t *psched = current->scheduler;
883
 
    gs_context_t *pctx;
884
 
    int code;
885
 
 
886
 
    if ((code = context_param(psched, op, &pctx)) < 0)
887
 
        return code;
888
 
    if_debug2('\'', "[']finish_join %ld, status = %d\n",
889
 
              pctx->index, pctx->status);
890
 
    if (pctx->joiner_index != current->index)
891
 
        return_error(e_invalidcontext);
892
 
    pctx->joiner_index = 0;
893
 
    return zjoin(i_ctx_p);
894
 
}
895
 
 
896
 
/* Reschedule now. */
897
 
static int
898
 
reschedule_now(i_ctx_t *i_ctx_p)
899
 
{
900
 
    return o_reschedule;
901
 
}
902
 
 
903
 
/* - yield - */
904
 
static int
905
 
zyield(i_ctx_t *i_ctx_p)
906
 
{
907
 
    gs_context_t *current = (gs_context_t *)i_ctx_p;
908
 
    gs_scheduler_t *psched = current->scheduler;
909
 
 
910
 
    if (psched->active.head_index == 0)
911
 
        return 0;
912
 
    if_debug0('"', "[\"]yield\n");
913
 
    add_last(psched, &psched->active, current);
914
 
    return o_reschedule;
915
 
}
916
 
 
917
 
/* ------ Condition and lock operators ------ */
918
 
 
919
 
static int
920
 
    monitor_cleanup(i_ctx_t *),
921
 
    monitor_release(i_ctx_t *),
922
 
    await_lock(i_ctx_t *);
923
 
static void
924
 
     activate_waiting(gs_scheduler_t *, ctx_list_t * pcl);
925
 
 
926
 
/* - condition <condition> */
927
 
static int
928
 
zcondition(i_ctx_t *i_ctx_p)
929
 
{
930
 
    os_ptr op = osp;
931
 
    gs_condition_t *pcond =
932
 
        ialloc_struct(gs_condition_t, &st_condition, "zcondition");
933
 
 
934
 
    if (pcond == 0)
935
 
        return_error(e_VMerror);
936
 
    pcond->waiting.head_index = pcond->waiting.tail_index = 0;
937
 
    push(1);
938
 
    make_istruct(op, a_all, pcond);
939
 
    return 0;
940
 
}
941
 
 
942
 
/* - lock <lock> */
943
 
static int
944
 
zlock(i_ctx_t *i_ctx_p)
945
 
{
946
 
    os_ptr op = osp;
947
 
    gs_lock_t *plock = ialloc_struct(gs_lock_t, &st_lock, "zlock");
948
 
 
949
 
    if (plock == 0)
950
 
        return_error(e_VMerror);
951
 
    plock->holder_index = 0;
952
 
    plock->waiting.head_index = plock->waiting.tail_index = 0;
953
 
    push(1);
954
 
    make_istruct(op, a_all, plock);
955
 
    return 0;
956
 
}
957
 
 
958
 
/* <lock> <proc> monitor - */
959
 
static int
960
 
zmonitor(i_ctx_t *i_ctx_p)
961
 
{
962
 
    gs_context_t *current = (gs_context_t *)i_ctx_p;
963
 
    os_ptr op = osp;
964
 
    gs_lock_t *plock;
965
 
    gs_context_t *pctx;
966
 
    int code;
967
 
 
968
 
    check_stype(op[-1], st_lock);
969
 
    check_proc(*op);
970
 
    plock = r_ptr(op - 1, gs_lock_t);
971
 
    pctx = index_context(current->scheduler, plock->holder_index);
972
 
    if_debug1('\'', "[']monitor 0x%lx\n", (ulong) plock);
973
 
    if (pctx != 0) {
974
 
        if (pctx == current ||
975
 
            (iimemory_local->save_level != 0 &&
976
 
             pctx->state.memory.space_local ==
977
 
             current->state.memory.space_local)
978
 
            )
979
 
            return_error(e_invalidcontext);
980
 
    }
981
 
    /*
982
 
     * We push on the e-stack:
983
 
     *      The lock object
984
 
     *      An e-stack mark with monitor_cleanup, to release the lock
985
 
     *        in case of an error
986
 
     *      monitor_release, to release the lock in the normal case
987
 
     *      The procedure to execute
988
 
     */
989
 
    check_estack(4);
990
 
    code = lock_acquire(op - 1, current);
991
 
    if (code != 0) {            /* We didn't acquire the lock.  Re-execute this later. */
992
 
        push_op_estack(zmonitor);
993
 
        return code;            /* o_reschedule */
994
 
    }
995
 
    *++esp = op[-1];
996
 
    push_mark_estack(es_other, monitor_cleanup);
997
 
    push_op_estack(monitor_release);
998
 
    *++esp = *op;
999
 
    pop(2);
1000
 
    return o_push_estack;
1001
 
}
1002
 
/* Release the monitor lock when unwinding for an error or exit. */
1003
 
static int
1004
 
monitor_cleanup(i_ctx_t *i_ctx_p)
1005
 
{
1006
 
    int code = lock_release(esp);
1007
 
 
1008
 
    if (code < 0)
1009
 
        return code;
1010
 
    --esp;
1011
 
    return o_pop_estack;
1012
 
}
1013
 
/* Release the monitor lock when the procedure completes. */
1014
 
static int
1015
 
monitor_release(i_ctx_t *i_ctx_p)
1016
 
{
1017
 
    int code = lock_release(esp - 1);
1018
 
 
1019
 
    if (code < 0)
1020
 
        return code;
1021
 
    esp -= 2;
1022
 
    return o_pop_estack;
1023
 
}
1024
 
 
1025
 
/* <condition> notify - */
1026
 
static int
1027
 
znotify(i_ctx_t *i_ctx_p)
1028
 
{
1029
 
    os_ptr op = osp;
1030
 
    gs_context_t *current = (gs_context_t *)i_ctx_p;
1031
 
    gs_condition_t *pcond;
1032
 
 
1033
 
    check_stype(*op, st_condition);
1034
 
    pcond = r_ptr(op, gs_condition_t);
1035
 
    if_debug1('"', "[\"]notify 0x%lx\n", (ulong) pcond);
1036
 
    pop(1);
1037
 
    op--;
1038
 
    if (pcond->waiting.head_index == 0) /* nothing to do */
1039
 
        return 0;
1040
 
    activate_waiting(current->scheduler, &pcond->waiting);
1041
 
    return zyield(i_ctx_p);
1042
 
}
1043
 
 
1044
 
/* <lock> <condition> wait - */
1045
 
static int
1046
 
zwait(i_ctx_t *i_ctx_p)
1047
 
{
1048
 
    os_ptr op = osp;
1049
 
    gs_context_t *current = (gs_context_t *)i_ctx_p;
1050
 
    gs_scheduler_t *psched = current->scheduler;
1051
 
    gs_lock_t *plock;
1052
 
    gs_context_t *pctx;
1053
 
    gs_condition_t *pcond;
1054
 
 
1055
 
    check_stype(op[-1], st_lock);
1056
 
    plock = r_ptr(op - 1, gs_lock_t);
1057
 
    check_stype(*op, st_condition);
1058
 
    pcond = r_ptr(op, gs_condition_t);
1059
 
    if_debug2('"', "[\"]wait lock 0x%lx, condition 0x%lx\n",
1060
 
              (ulong) plock, (ulong) pcond);
1061
 
    pctx = index_context(psched, plock->holder_index);
1062
 
    if (pctx == 0 || pctx != psched->current ||
1063
 
        (iimemory_local->save_level != 0 &&
1064
 
         (r_space(op - 1) == avm_local || r_space(op) == avm_local))
1065
 
        )
1066
 
        return_error(e_invalidcontext);
1067
 
    check_estack(1);
1068
 
    lock_release(op - 1);
1069
 
    add_last(psched, &pcond->waiting, pctx);
1070
 
    push_op_estack(await_lock);
1071
 
    return o_reschedule;
1072
 
}
1073
 
/* When the condition is signaled, wait for acquiring the lock. */
1074
 
static int
1075
 
await_lock(i_ctx_t *i_ctx_p)
1076
 
{
1077
 
    gs_context_t *current = (gs_context_t *)i_ctx_p;
1078
 
    os_ptr op = osp;
1079
 
    int code = lock_acquire(op - 1, current);
1080
 
 
1081
 
    if (code == 0) {
1082
 
        pop(2);
1083
 
        return 0;
1084
 
    }
1085
 
    /* We didn't acquire the lock.  Re-execute the wait. */
1086
 
    push_op_estack(await_lock);
1087
 
    return code;                /* o_reschedule */
1088
 
}
1089
 
 
1090
 
/* Activate a list of waiting contexts, and reset the list. */
1091
 
static void
1092
 
activate_waiting(gs_scheduler_t *psched, ctx_list_t * pcl)
1093
 
{
1094
 
    gs_context_t *pctx = index_context(psched, pcl->head_index);
1095
 
    gs_context_t *next;
1096
 
 
1097
 
    for (; pctx != 0; pctx = next) {
1098
 
        next = index_context(psched, pctx->next_index);
1099
 
        add_last(psched, &psched->active, pctx);
1100
 
    }
1101
 
    pcl->head_index = pcl->tail_index = 0;
1102
 
}
1103
 
 
1104
 
/* ------ Miscellaneous operators ------ */
1105
 
 
1106
 
/* - usertime <int> */
1107
 
static int
1108
 
zusertime_context(i_ctx_t *i_ctx_p)
1109
 
{
1110
 
    gs_context_t *current = (gs_context_t *)i_ctx_p;
1111
 
    gs_scheduler_t *psched = current->scheduler;
1112
 
    os_ptr op = osp;
1113
 
    long utime = context_usertime();
1114
 
 
1115
 
    push(1);
1116
 
    if (!current->state.keep_usertime) {
1117
 
        /*
1118
 
         * This is the first time this context has executed usertime:
1119
 
         * we must track its execution time from now on.
1120
 
         */
1121
 
        psched->usertime_initial = utime;
1122
 
        current->state.keep_usertime = true;
1123
 
    }
1124
 
    make_int(op, current->state.usertime_total + utime -
1125
 
             psched->usertime_initial);
1126
 
    return 0;
1127
 
}
1128
 
 
1129
 
/* ------ Internal procedures ------ */
1130
 
 
1131
 
/* Create a context. */
1132
 
static int
1133
 
context_create(gs_scheduler_t * psched, gs_context_t ** ppctx,
1134
 
               const gs_dual_memory_t * dmem,
1135
 
               const gs_context_state_t *i_ctx_p, bool copy_state)
1136
 
{
1137
 
    /*
1138
 
     * Contexts are always created at the outermost save level, so they do
1139
 
     * not need to be allocated in stable memory for the sake of
1140
 
     * save/restore.  However, context_reclaim needs to be able to test
1141
 
     * whether a given context belongs to a given local VM, and allocating
1142
 
     * contexts in stable local VM avoids the need to scan multiple save
1143
 
     * levels when making this test.
1144
 
     */
1145
 
    gs_memory_t *mem = gs_memory_stable((gs_memory_t *)dmem->space_local);
1146
 
    gs_context_t *pctx;
1147
 
    int code;
1148
 
    long ctx_index;
1149
 
    gs_context_t **pte;
1150
 
 
1151
 
    pctx = gs_alloc_struct(mem, gs_context_t, &st_context, "context_create");
1152
 
    if (pctx == 0)
1153
 
        return_error(e_VMerror);
1154
 
    if (copy_state) {
1155
 
        pctx->state = *i_ctx_p;
1156
 
    } else {
1157
 
        gs_context_state_t *pctx_st = &pctx->state;
1158
 
 
1159
 
        code = context_state_alloc(&pctx_st, systemdict, dmem);
1160
 
        if (code < 0) {
1161
 
            gs_free_object(mem, pctx, "context_create");
1162
 
            return code;
1163
 
        }
1164
 
    }
1165
 
    ctx_index = gs_next_ids(mem, 1);
1166
 
    pctx->scheduler = psched;
1167
 
    pctx->status = cs_active;
1168
 
    pctx->index = ctx_index;
1169
 
    pctx->detach = false;
1170
 
    pctx->saved_local_vm = false;
1171
 
    pctx->visible = true;
1172
 
    pctx->next_index = 0;
1173
 
    pctx->joiner_index = 0;
1174
 
    pte = &psched->table[ctx_index % CTX_TABLE_SIZE];
1175
 
    pctx->table_next = *pte;
1176
 
    *pte = pctx;
1177
 
    *ppctx = pctx;
1178
 
    if (gs_debug_c('\'') | gs_debug_c('"'))
1179
 
        dlprintf2("[']create %ld at 0x%lx\n", ctx_index, (ulong) pctx);
1180
 
    return 0;
1181
 
}
1182
 
 
1183
 
/* Check a context ID.  Note that we do not check for context validity. */
1184
 
static int
1185
 
context_param(const gs_scheduler_t * psched, os_ptr op, gs_context_t ** ppctx)
1186
 
{
1187
 
    gs_context_t *pctx;
1188
 
 
1189
 
    check_type(*op, t_integer);
1190
 
    pctx = index_context(psched, op->value.intval);
1191
 
    if (pctx == 0)
1192
 
        return_error(e_invalidcontext);
1193
 
    *ppctx = pctx;
1194
 
    return 0;
1195
 
}
1196
 
 
1197
 
/* Read the usertime as a single value. */
1198
 
static long
1199
 
context_usertime(void)
1200
 
{
1201
 
    long secs_ns[2];
1202
 
 
1203
 
    gp_get_usertime(secs_ns);
1204
 
    return secs_ns[0] * 1000 + secs_ns[1] / 1000000;
1205
 
}
1206
 
 
1207
 
/* Destroy a context. */
1208
 
static void
1209
 
context_destroy(gs_context_t * pctx)
1210
 
{
1211
 
    gs_ref_memory_t *mem = pctx->state.memory.space_local;
1212
 
    gs_scheduler_t *psched = pctx->scheduler;
1213
 
    gs_context_t **ppctx = &psched->table[pctx->index % CTX_TABLE_SIZE];
1214
 
 
1215
 
    while (*ppctx != pctx)
1216
 
        ppctx = &(*ppctx)->table_next;
1217
 
    *ppctx = (*ppctx)->table_next;
1218
 
    if (gs_debug_c('\'') | gs_debug_c('"'))
1219
 
        dlprintf3("[']destroy %ld at 0x%lx, status = %d\n",
1220
 
                  pctx->index, (ulong) pctx, pctx->status);
1221
 
    if (!context_state_free(&pctx->state))
1222
 
        gs_free_object((gs_memory_t *) mem, pctx, "context_destroy");
1223
 
}
1224
 
 
1225
 
/* Copy the top elements of one stack to another. */
1226
 
/* Note that this does not push the elements: */
1227
 
/* the destination stack must have enough space preallocated. */
1228
 
static void
1229
 
stack_copy(ref_stack_t * to, const ref_stack_t * from, uint count,
1230
 
           uint from_index)
1231
 
{
1232
 
    long i;
1233
 
 
1234
 
    for (i = (long)count - 1; i >= 0; --i)
1235
 
        *ref_stack_index(to, i) = *ref_stack_index(from, i + from_index);
1236
 
}
1237
 
 
1238
 
/* Acquire a lock.  Return 0 if acquired, o_reschedule if not. */
1239
 
static int
1240
 
lock_acquire(os_ptr op, gs_context_t * pctx)
1241
 
{
1242
 
    gs_lock_t *plock = r_ptr(op, gs_lock_t);
1243
 
 
1244
 
    if (plock->holder_index == 0) {
1245
 
        plock->holder_index = pctx->index;
1246
 
        plock->scheduler = pctx->scheduler;
1247
 
        return 0;
1248
 
    }
1249
 
    add_last(pctx->scheduler, &plock->waiting, pctx);
1250
 
    return o_reschedule;
1251
 
}
1252
 
 
1253
 
/* Release a lock.  Return 0 if OK, e_invalidcontext if not. */
1254
 
static int
1255
 
lock_release(ref * op)
1256
 
{
1257
 
    gs_lock_t *plock = r_ptr(op, gs_lock_t);
1258
 
    gs_scheduler_t *psched = plock->scheduler;
1259
 
    gs_context_t *pctx = index_context(psched, plock->holder_index);
1260
 
 
1261
 
    if (pctx != 0 && pctx == psched->current) {
1262
 
        plock->holder_index = 0;
1263
 
        activate_waiting(psched, &plock->waiting);
1264
 
        return 0;
1265
 
    }
1266
 
    return_error(e_invalidcontext);
1267
 
}
1268
 
 
1269
 
/* ------ Initialization procedure ------ */
1270
 
 
1271
 
/* We need to split the table because of the 16-element limit. */
1272
 
const op_def zcontext1_op_defs[] = {
1273
 
    {"0condition", zcondition},
1274
 
    {"0currentcontext", zcurrentcontext},
1275
 
    {"1detach", zdetach},
1276
 
    {"2.fork", zfork},
1277
 
    {"1join", zjoin},
1278
 
    {"4.localfork", zlocalfork},
1279
 
    {"0lock", zlock},
1280
 
    {"2monitor", zmonitor},
1281
 
    {"1notify", znotify},
1282
 
    {"2wait", zwait},
1283
 
    {"0yield", zyield},
1284
 
                /* Note that the following replace prior definitions */
1285
 
                /* in the indicated files: */
1286
 
    {"0usertime", zusertime_context},   /* zmisc.c */
1287
 
    op_def_end(0)
1288
 
};
1289
 
const op_def zcontext2_op_defs[] = {
1290
 
                /* Internal operators */
1291
 
    {"0%fork_done", fork_done},
1292
 
    {"1%finish_join", finish_join},
1293
 
    {"0%monitor_cleanup", monitor_cleanup},
1294
 
    {"0%monitor_release", monitor_release},
1295
 
    {"2%await_lock", await_lock},
1296
 
    op_def_end(zcontext_init)
1297
 
};