Beispiel #1
0
/* After rendering page gotten w/gx_page_queue_dequeue, call this to ack */
void
gx_page_queue_finish_dequeue(
				gx_page_queue_entry_t * entry	/* entry that was retrieved to delete */
)
{
    gx_page_queue_t *queue = entry->queue;

    gx_monitor_enter(queue->monitor);
    if (queue->enable_render_done_signal) {
	queue->enable_render_done_signal = false;
	gx_semaphore_signal(queue->render_done_sema);
    }
    queue->dequeue_in_progress = false;

    /*
     * Delete the previously-allocated entry, do inside monitor in case
     * this is the reserve entry & is the only memory in the universe;
     * in that case gx_page_queue_add_page won't be looking for this
     * until the monitor is exited.
     * In this implementation of the page queue, clist and queue entries
     * are managed together, so free the clist just before freeing the entry.
     */
    gx_page_queue_entry_free_page_info(entry);
    gx_page_queue_entry_free(entry);

    gx_monitor_leave(queue->monitor);
}
void
gsicc_set_link_data(gsicc_link_t *icc_link, void *link_handle, void *contextptr,
               gsicc_hashlink_t hashcode, gx_monitor_t *lock, 
               bool includes_softproof, bool includes_devlink)
{
    gx_monitor_enter(lock);		/* lock the cache while changing data */
    icc_link->contextptr = contextptr;
    icc_link->link_handle = link_handle;
    icc_link->hashcode.link_hashcode = hashcode.link_hashcode;
    icc_link->hashcode.des_hash = hashcode.des_hash;
    icc_link->hashcode.src_hash = hashcode.src_hash;
    icc_link->hashcode.rend_hash = hashcode.rend_hash;
    icc_link->includes_softproof = includes_softproof;
    icc_link->includes_devlink = includes_devlink;
    if ( (hashcode.src_hash == hashcode.des_hash) &&
          !includes_softproof && !includes_devlink) {
        icc_link->is_identity = true;
    } else {
        icc_link->is_identity = false;
    }
    icc_link->valid = true;

    /* Now release any tasks/threads waiting for these contents */
    while (icc_link->num_waiting > 0) {
        gx_semaphore_signal(icc_link->wait);
        icc_link->num_waiting--;
    }
    gx_monitor_leave(lock);	/* done with updating, let everyone run */
}
Beispiel #3
0
/* Remove link from cache.  Notify CMS and free */
static void
gsicc_remove_link(gsicc_link_t *link, gs_memory_t *memory)
{
    gsicc_link_t *curr, *prev;
    gsicc_link_cache_t *icc_link_cache = link->icc_link_cache;

    /* NOTE: link->ref_count must be 0: assert ? */
    gx_monitor_enter(icc_link_cache->lock);
    curr = icc_link_cache->head;
    prev = NULL;

    while (curr != NULL ) {
        if (curr == link) {
	    /* remove this one from the list */
	    if (prev == NULL)
		icc_link_cache->head = curr->next;
	    else
		prev->next = curr->next;
            break;
        }
	prev = curr;
        curr = curr->next;
    }
    /* if curr != link we didn't find it: assert ? */
    gx_monitor_leave(icc_link_cache->lock);
    gsicc_link_free(link, memory);	/* outside link */
}
Beispiel #4
0
/* Wait for a single page to finish rendering (if any pending) */
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	/* queue to wait on */
)
{
    int code;

    gx_monitor_enter(queue->monitor);
    if (!queue->entry_count && !queue->dequeue_in_progress) {
	code = 0;
	gx_monitor_leave(queue->monitor);
    } else {
	/* request acknowledgement on render done */
	queue->enable_render_done_signal = true;

	/* exit monitor & wait for acknowlegement */
	gx_monitor_leave(queue->monitor);
	gx_semaphore_wait(queue->render_done_sema);
	code = 1;
    }
    return code;
}
Beispiel #5
0
/* This may release elements waiting on a icc_link_cache slot */
void
gsicc_release_link(gsicc_link_t *icclink)
{
    gsicc_link_cache_t *icc_link_cache = icclink->icc_link_cache;

    gx_monitor_enter(icc_link_cache->lock);
    /* Decrement the reference count */
    if (--(icclink->ref_count) == 0) {

	gsicc_link_t *curr, *prev;


	/* Find link in cache, and move it to the end of the list.  */
	/* This way zero ref_count links are found LRU first	*/
	curr = icc_link_cache->head;
	prev = NULL;
	while (curr != icclink) {
	    prev = curr;
	    curr = curr->next;
	};
	if (prev == NULL) {
	    /* this link was the head */
	    icc_link_cache->head = curr->next;
	} else {
	    prev->next = curr->next;		/* de-link this one */
	}
	/* Find the first zero-ref entry on the list */
	curr = icc_link_cache->head;
	prev = NULL;
	while (curr != NULL && curr->ref_count > 0) {
	    prev = curr;
	    curr = curr->next;
	}
	/* Found where to link this one into the tail of the list */
	if (prev == NULL) {
	    icc_link_cache->head = icclink;
	    icclink->next = icc_link_cache->head->next;
	} else {
	    /* link this one in here */
	    prev->next = icclink;
	    icclink->next = curr;
	}

	/* now release any tasks waiting for a cache slot */
        while (icclink->icc_link_cache->num_waiting > 0) {
	    gx_semaphore_signal(icclink->icc_link_cache->wait);
	    icclink->icc_link_cache->num_waiting--;
        }
    }
    gx_monitor_leave(icc_link_cache->lock);
}
Beispiel #6
0
static gsicc_link_t*
gsicc_findcachelink(gsicc_hashlink_t hash, gsicc_link_cache_t *icc_link_cache, bool includes_proof)
{
    gsicc_link_t *curr, *prev;
    int64_t hashcode = hash.link_hashcode;

    /* Look through the cache for the hashcode */
    gx_monitor_enter(icc_link_cache->lock);

    /* List scanning is fast, so we scan the entire list, this includes   */
    /* links that are currently unused, but still in the cache (zero_ref) */
    curr = icc_link_cache->head;
    prev = NULL;

    while (curr != NULL ) {
        if (curr->hashcode.link_hashcode == hashcode && includes_proof == curr->includes_softproof) {
	    /* move this one to the front of the list hoping we will use it again soon */
	    if (prev != NULL) {		/* if prev == NULL, curr is already the head */
		prev->next = curr->next;
		curr->next = icc_link_cache->head;
		icc_link_cache->head = curr;
	    }
	    curr->ref_count++;		/* bump the ref_count since we will be using this one */
	    while (curr->valid == false) {
		curr->num_waiting++;
	    	gx_monitor_leave(icc_link_cache->lock);
		gx_semaphore_wait(curr->wait);
    		gx_monitor_enter(icc_link_cache->lock);	/* re-enter breifly */
	    }
	    gx_monitor_leave(icc_link_cache->lock);
            return(curr);	/* success */
        }
	prev = curr;
        curr = curr->next;
    }
    gx_monitor_leave(icc_link_cache->lock);
    return(NULL);
}
Beispiel #7
0
/* Even if an error is returned, entry will have been added to queue! */
int				/* rets 0 ok, gs_error_VMerror if error */
gx_page_queue_add_page(gx_device_clist_writer *const pcwdev,
			  gx_page_queue_t * queue,	/* page queue to add to */
			  gx_page_queue_action_t action,	/* action code to queue */
			  const gx_band_page_info_t * page_info,  /* bandinfo incl. bandlist (or 0) */
			  int page_count	/* see comments in gdevprna.c */
)
{
    int code = 0;

    /* Allocate a new page queue entry */
    gx_page_queue_entry_t *entry
    = gx_page_queue_entry_alloc(queue);

    if (!entry) {
	/* Use reserve page queue entry */
	gx_monitor_enter(queue->monitor);	/* not strictly necessary */
	entry = queue->reserve_entry;
	queue->reserve_entry = 0;
	gx_monitor_leave(queue->monitor);
    }
    /* Fill in page queue entry with info from device */
    entry->action = action;
    if (page_info != 0)
	entry->page_info = *page_info;
    else {
	entry->page_info = null_page_info;
	entry->page_info.io_procs = pcwdev->page_info.io_procs;
    }
    entry->num_copies = page_count;

    /* Stick onto page queue & signal */
    gx_page_queue_enqueue(entry);

    /* If a new reserve entry is needed, wait till enough mem is avail */
    while (!queue->reserve_entry) {
	queue->reserve_entry = gx_page_queue_entry_alloc(queue);
	if (!queue->reserve_entry && !gx_page_queue_wait_one_page(queue)) {
	    /* Should never happen: all pages rendered & still can't get memory: give up! */
	    code = gs_note_error(gs_error_Fatal);
	    break;
	}
    }
    return code;
}
Beispiel #8
0
/* Add entry to queue at end */
static void
gx_page_queue_add_last(
			  gx_page_queue_entry_t * entry	/* entry to add */
)
{
    gx_page_queue_t *queue = entry->queue;

    /* Enter monitor */
    gx_monitor_enter(queue->monitor);

    /* Add the goods */
    entry->next = 0;
    if (queue->last_in != 0)
	queue->last_in->next = entry;
    queue->last_in = entry;
    if (queue->first_in == 0)
	queue->first_in = entry;
    ++queue->entry_count;

    /* exit monitor */
    gx_monitor_leave(queue->monitor);
}
Beispiel #9
0
/* Retrieve & remove firstin queue entry */
static gx_page_queue_entry_t *	/* removed entry, 0 if none avail */
gx_page_queue_remove_first(
			      gx_page_queue_t * queue	/* page queue to retrieve from */
)
{
    gx_page_queue_entry_t *entry = 0;	/* assume failure */

    /* Enter monitor */
    gx_monitor_enter(queue->monitor);

    /* Get the goods */
    if (queue->entry_count) {
	entry = queue->first_in;
	queue->first_in = entry->next;
	if (queue->last_in == entry)
	    queue->last_in = 0;
	--queue->entry_count;
    }
    /* exit monitor */
    gx_monitor_leave(queue->monitor);

    return entry;
}
Beispiel #10
0
static void *
gs_heap_resize_object(gs_memory_t * mem, void *obj, uint new_num_elements,
		      client_name_t cname)
{
    gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem;
    gs_malloc_block_t *ptr = (gs_malloc_block_t *) obj - 1;
    gs_memory_type_ptr_t pstype = ptr->type;
    uint old_size = gs_object_size(mem, obj) + sizeof(gs_malloc_block_t);
    uint new_size =
	gs_struct_type_size(pstype) * new_num_elements +
	sizeof(gs_malloc_block_t);
    gs_malloc_block_t *new_ptr;

    if (new_size == old_size)
        return obj;
    if (mmem->monitor)
	gx_monitor_enter(mmem->monitor);	/* Exclusive access */
    new_ptr = (gs_malloc_block_t *) gs_realloc(ptr, old_size, new_size);
    if (new_ptr == 0)
	return 0;
    if (new_ptr->prev)
	new_ptr->prev->next = new_ptr;
    else
	mmem->allocated = new_ptr;
    if (new_ptr->next)
	new_ptr->next->prev = new_ptr;
    new_ptr->size = new_size - sizeof(gs_malloc_block_t);
    mmem->used -= old_size;
    mmem->used += new_size;
    if (mmem->monitor)
	gx_monitor_leave(mmem->monitor);	/* Done with exclusive access */
    if (new_size > old_size)
	gs_alloc_fill((byte *) new_ptr + old_size,
		      gs_alloc_fill_alloc, new_size - old_size);
    return new_ptr + 1;
}
/* Get the replacement color management link.  It basically needs to store
   the number of components for the source so that we know what we are 
   coming from (e.g. RGB, CMYK, Gray) */
gsicc_link_t*
gsicc_rcm_get_link(const gs_imager_state *pis, gx_device *dev, 
                   gsicc_colorbuffer_t data_cs)
{
    gsicc_link_t *result;
    gsicc_hashlink_t hash;
    rcm_link_t *rcm_link;
    gs_memory_t *mem = dev->memory->non_gc_memory;
    const gx_cm_color_map_procs * cm_procs;
    bool pageneutralcolor = false;
    cmm_dev_profile_t *dev_profile;
    int code;

    /* Need to check if we need to monitor for color */
    if (dev != NULL ) {
        code = dev_proc(dev, get_profile)(dev,  &dev_profile);
        if (code < 0)
            return NULL;
        if (dev_profile != NULL) {
            pageneutralcolor = dev_profile->pageneutralcolor;
        }
     }

    /* If the cm_procs are forwarding due to the overprint device or other
       odd thing, drill down now and get the proper ones */
    if (fwd_uses_fwd_cmap_procs(dev)) {
        cm_procs = fwd_get_target_cmap_procs(dev);
    } else {
        cm_procs = get_color_mapping_procs_subclass(dev);
    }

    hash.rend_hash = gsCMM_REPLACE;
    hash.des_hash = dev->color_info.num_components;
    hash.src_hash = data_cs;
    hash.link_hashcode = data_cs + hash.des_hash * 256 + hash.rend_hash * 4096;

    /* Check the cache for a hit. */
    result = gsicc_findcachelink(hash, pis->icc_link_cache, false, false);
    if (result != NULL) {
        return result;
    }
    /* If not, then lets create a new one.  This may actually return a link if 
       another thread has already created it while we were trying to do so */ 
    if (gsicc_alloc_link_entry(pis->icc_link_cache, &result, hash, false, false)) 
        return result;

    if (result == NULL)
        return result;

    /* Now compute the link contents */
    /* Lock the cache as we alter the procs */
    gx_monitor_enter(pis->icc_link_cache->lock);	

    result->procs.map_buffer = gsicc_rcm_transform_color_buffer;
    result->procs.map_color = gsicc_rcm_transform_color;
    result->procs.free_link = gsicc_rcm_freelink;
    result->hashcode = hash;
    result->is_identity = false;
    rcm_link = (rcm_link_t *) gs_alloc_bytes(mem, sizeof(rcm_link_t),
                                               "gsicc_rcm_get_link");
    if (rcm_link == NULL)
        return NULL;
    result->link_handle = (void*) rcm_link;
    rcm_link->memory = mem;
    rcm_link->num_out = min(dev->color_info.num_components, 
                             GS_CLIENT_COLOR_MAX_COMPONENTS);
    rcm_link->data_cs_in = data_cs;
    rcm_link->cm_procs.map_cmyk = cm_procs->map_cmyk;
    rcm_link->cm_procs.map_rgb = cm_procs->map_rgb;
    rcm_link->cm_procs.map_gray = cm_procs->map_gray;

    switch (data_cs) {
        case gsGRAY:
            rcm_link->num_in = 1;
            break;
        case gsRGB:
        case gsCIELAB:
            rcm_link->num_in = 3;
            break;
        case gsCMYK:
            rcm_link->num_in = 4;
            break;
        default:
            result->procs.free_link(result);
            return NULL;
    }
    /* Likely set if we have something like a table or procs */    
    rcm_link->context = NULL;  

    result->num_input = rcm_link->num_in;
    result->num_output = rcm_link->num_out;
    result->link_handle = rcm_link;
    result->hashcode.link_hashcode = hash.link_hashcode;
    result->hashcode.des_hash = hash.des_hash;
    result->hashcode.src_hash = hash.src_hash;
    result->hashcode.rend_hash = hash.rend_hash;
    result->includes_softproof = false;
    result->includes_devlink = false;
    if (hash.src_hash == hash.des_hash) {
        result->is_identity = true;
    } else {
        result->is_identity = false;
    }
    result->valid = true;

    /* Set up for monitoring non gray color spaces */
    if (pageneutralcolor && data_cs != gsGRAY)
        gsicc_mcm_set_link(result);

    /* Now release any tasks/threads waiting for these contents */
    while (result->num_waiting > 0) {
        gx_semaphore_signal(result->wait);
        result->num_waiting--;
    }
    gx_monitor_leave(pis->icc_link_cache->lock);	/* done with updating, let everyone run */

    return result;
}
Beispiel #12
0
static void
gs_heap_free_object(gs_memory_t * mem, void *ptr, client_name_t cname)
{
    gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem;
    gs_malloc_block_t *bp;
    gs_memory_type_ptr_t pstype;
    struct_proc_finalize((*finalize));

    if_debug3('a', "[a-]gs_free(%s) 0x%lx(%u)\n",
	      client_name_string(cname), (ulong) ptr,
	      (ptr == 0 ? 0 : ((gs_malloc_block_t *) ptr)[-1].size));
    if (ptr == 0)
	return;
    pstype = ((gs_malloc_block_t *) ptr)[-1].type;
    finalize = pstype->finalize;
    if (finalize != 0) {
	if_debug3('u', "[u]finalizing %s 0x%lx (%s)\n",
		  struct_type_name_string(pstype),
		  (ulong) ptr, client_name_string(cname));
	(*finalize) (ptr);
    }
    if (mmem->monitor)
	gx_monitor_enter(mmem->monitor);	/* Exclusive access */
    bp = mmem->allocated; /* If 'finalize' releases a memory,
			     this function could be called recursively and
			     change mmem->allocated. */
    if (ptr == bp + 1) {
	mmem->allocated = bp->next;
	mmem->used -= bp->size + sizeof(gs_malloc_block_t);

	if (mmem->allocated)
	    mmem->allocated->prev = 0;
	if (mmem->monitor)
	    gx_monitor_leave(mmem->monitor);	/* Done with exclusive access */
	gs_alloc_fill(bp, gs_alloc_fill_free,
		      bp->size + sizeof(gs_malloc_block_t));
	free(bp);
    } else {
	gs_malloc_block_t *np;

	/*
	 * bp == 0 at this point is an error, but we'd rather have an
	 * error message than an invalid access.
	 */
	if (bp) {
	    for (; (np = bp->next) != 0; bp = np) {
		if (ptr == np + 1) {
		    bp->next = np->next;
		    if (np->next)
			np->next->prev = bp;
		    mmem->used -= np->size + sizeof(gs_malloc_block_t);
		    if (mmem->monitor)
			gx_monitor_leave(mmem->monitor);	/* Done with exclusive access */
		    gs_alloc_fill(np, gs_alloc_fill_free,
				  np->size + sizeof(gs_malloc_block_t));
		    free(np);
		    return;
		}
	    }
	}
	if (mmem->monitor)
	    gx_monitor_leave(mmem->monitor);	/* Done with exclusive access */
	lprintf2("%s: free 0x%lx not found!\n",
		 client_name_string(cname), (ulong) ptr);
	free((char *)((gs_malloc_block_t *) ptr - 1));
    }
}
Beispiel #13
0
/* Allocate various kinds of blocks. */
static byte *
gs_heap_alloc_bytes(gs_memory_t * mem, uint size, client_name_t cname)
{
    gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem;
    byte *ptr = 0;

#ifdef DEBUG
    const char *msg;
    static const char *const ok_msg = "OK";

#  define set_msg(str) (msg = (str))
#else
#  define set_msg(str) DO_NOTHING
#endif

    	/* Exclusive acces so our decisions and changes are 'atomic' */
    if (mmem->monitor)
	gx_monitor_enter(mmem->monitor);
    if (size > mmem->limit - sizeof(gs_malloc_block_t)) {
	/* Definitely too large to allocate; also avoids overflow. */
	set_msg("exceeded limit");
    } else {
	uint added = size + sizeof(gs_malloc_block_t);

	if (mmem->limit - added < mmem->used)
	    set_msg("exceeded limit");
	else if ((ptr = (byte *) malloc(added)) == 0)
	    set_msg("failed");
	else {
	    gs_malloc_block_t *bp = (gs_malloc_block_t *) ptr;

	    /*
	     * We would like to check that malloc aligns blocks at least as
	     * strictly as the compiler (as defined by ARCH_ALIGN_MEMORY_MOD).
	     * However, Microsoft VC 6 does not satisfy this requirement.
	     * See gsmemory.h for more explanation.
	     */
	    set_msg(ok_msg);
	    if (mmem->allocated)
		mmem->allocated->prev = bp;
	    bp->next = mmem->allocated;
	    bp->prev = 0;
	    bp->size = size;
	    bp->type = &st_bytes;
	    bp->cname = cname;
	    mmem->allocated = bp;
	    ptr = (byte *) (bp + 1);
	    mmem->used += size + sizeof(gs_malloc_block_t);
	    if (mmem->used > mmem->max_used)
		mmem->max_used = mmem->used;
	}
    }
    if (mmem->monitor)
	gx_monitor_leave(mmem->monitor);	/* Done with exclusive access */
    /* We don't want to 'fill' under mutex to keep the window smaller */
    if (ptr)
	gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
#ifdef DEBUG
    if (gs_debug_c('a') || msg != ok_msg)
	dlprintf4("[a+]gs_malloc(%s)(%u) = 0x%lx: %s\n",
		  client_name_string(cname), size, (ulong) ptr, msg);
#endif
    return ptr;
#undef set_msg
}
Beispiel #14
0
gsicc_link_t*
gsicc_get_link_profile(gs_imager_state *pis, gx_device *dev,
                       cmm_profile_t *gs_input_profile,
                       cmm_profile_t *gs_output_profile,
                       gsicc_rendering_param_t *rendering_params,
                       gs_memory_t *memory, bool include_softproof)
{
    gsicc_hashlink_t hash;
    gsicc_link_t *link, *found_link;
    gcmmhlink_t link_handle = NULL;
    void **contextptr = NULL;
    gsicc_manager_t *icc_manager = pis->icc_manager;
    gsicc_link_cache_t *icc_link_cache = pis->icc_link_cache;
    gs_memory_t *cache_mem = pis->icc_link_cache->memory;

    gcmmhprofile_t *cms_input_profile;
    gcmmhprofile_t *cms_output_profile;

    /* First compute the hash code for the incoming case */
    /* If the output color space is NULL we will use the device profile for the output color space */
    gsicc_compute_linkhash(icc_manager, dev, gs_input_profile, gs_output_profile,
                            rendering_params, &hash);

    /* Check the cache for a hit.  Need to check if softproofing was used */
    found_link = gsicc_findcachelink(hash, icc_link_cache, include_softproof);

    /* Got a hit, return link (ref_count for the link was already bumped */
    if (found_link != NULL)
        return(found_link);  /* TO FIX: We are really not going to want to have the members
                          of this object visible outside gsiccmange */

    /* If not, then lets create a new one if there is room or return NULL */
    /* Caller will need to try later */

    /* First see if we can add a link */
    /* TODO: this should be based on memory usage, not just num_links */
    gx_monitor_enter(icc_link_cache->lock);
    while (icc_link_cache->num_links >= ICC_CACHE_MAXLINKS) {
        /* If not, see if there is anything we can remove from cache. */
	while ((link = gsicc_find_zeroref_cache(icc_link_cache)) == NULL) {
	    icc_link_cache->num_waiting++;
	    /* safe to unlock since above will make sure semaphore is signalled */
	    gx_monitor_leave(icc_link_cache->lock);
	    /* we get signalled (released from wait) when a link goes to zero ref */
	    gx_semaphore_wait(icc_link_cache->wait);

	    /* repeat the findcachelink to see if some other thread has	*/
	    /*already started building the link	we need			*/
	    found_link = gsicc_findcachelink(hash, icc_link_cache, include_softproof);

	    /* Got a hit, return link (ref_count for the link was already bumped */
	    if (found_link != NULL)
		return(found_link);  /* TO FIX: We are really not going to want to have the members
				  of this object visible outside gsiccmange */

	    gx_monitor_enter(icc_link_cache->lock);	    /* restore the lock */
	    /* we will re-test the num_links above while locked to insure */
	    /* that some other thread didn't grab the slot and max us out */
	}
	/* Remove the zero ref_count link profile we found.		*/
	/* Even if we remove this link, we may still be maxed out so	*/
	/* the outermost 'while' will check to make sure some other	*/
	/* thread did not grab the one we remove.			*/
	gsicc_remove_link(link, cache_mem);
        icc_link_cache->num_links--;
    }
    /* insert an empty link that we will reserve so we */
    /* can unlock while building the link contents     */
    link = gsicc_alloc_link(cache_mem->stable_memory, hash);
    link->icc_link_cache = icc_link_cache;
    link->next = icc_link_cache->head;
    icc_link_cache->head = link;
    icc_link_cache->num_links++;
    gx_monitor_leave(icc_link_cache->lock);	/* now that we own this link we can release */
					/* the lock since it is not valid */

    /* Now compute the link contents */
    cms_input_profile = gs_input_profile->profile_handle;
    if (cms_input_profile == NULL) {
        if (gs_input_profile->buffer != NULL) {
            cms_input_profile =
                gsicc_get_profile_handle_buffer(gs_input_profile->buffer,
                                                gs_input_profile->buffer_size);
            gs_input_profile->profile_handle = cms_input_profile;
        } else {
            /* See if we have a clist device pointer. */
            if ( gs_input_profile->dev != NULL ) {
                /* ICC profile should be in clist. This is
                   the first call to it.  Note that the profiles are not
                   really shared amongst threads like the links are.  Hence
                   the memory is for the local thread's chunk */
                cms_input_profile =
                    gsicc_get_profile_handle_clist(gs_input_profile,
                                                   gs_input_profile->memory);
                gs_input_profile->profile_handle = cms_input_profile;
            } else {
                /* Cant create the link.  No profile present,
                   nor any defaults to use for this.  Really
                   need to throw an error for this case. */
		gsicc_remove_link(link, cache_mem);
                icc_link_cache->num_links--;
                return(NULL);
            }
        }
    }

    cms_output_profile = gs_output_profile->profile_handle;
    if (cms_output_profile == NULL) {
        if (gs_output_profile->buffer != NULL) {
            cms_input_profile =
                gsicc_get_profile_handle_buffer(gs_input_profile->buffer,
                                                gs_input_profile->buffer_size);
            gs_output_profile->profile_handle = cms_output_profile;
        } else {
              /* See if we have a clist device pointer. */
            if ( gs_output_profile->dev != NULL ) {
                /* ICC profile should be in clist. This is
                   the first call to it. */
                cms_output_profile =
                    gsicc_get_profile_handle_clist(gs_output_profile,
                                                   gs_output_profile->memory);
                gs_output_profile->profile_handle = cms_output_profile;
            } else {
                /* Cant create the link.  No profile present,
                   nor any defaults to use for this.  Really
                   need to throw an error for this case. */
		gsicc_remove_link(link, cache_mem);
                icc_link_cache->num_links--;
                return(NULL);
            }
        }
    }
    link_handle = gscms_get_link(cms_input_profile, cms_output_profile,
                                    rendering_params);
    if (link_handle != NULL) {
	gsicc_set_link_data(link, link_handle, contextptr, hash, icc_link_cache->lock);
    } else {
	gsicc_remove_link(link, cache_mem);
        icc_link_cache->num_links--;
        return(NULL);
    }
    return(link);
}