summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Vrhel <michael.vrhel@artifex.com>2010-05-18 23:51:46 +0000
committerMichael Vrhel <michael.vrhel@artifex.com>2010-05-18 23:51:46 +0000
commitd910aee3084c1cae14f2e957acf4a38d0b21d2b6 (patch)
tree0993a1c4a801d01fb0264efe95c5daf968fc0fdd
parentdd3ca323b1c3003ac98310b56f8502ab1094ee9e (diff)
Initial commit of code to perform multi-threaded clist rendering with the new ICC color management. Thanks to Ray for writing the bulk of this. This still needs additional testing but since I have fixed a few issues with Ray's initial patch and it is running in the cases I have tested so far, it is worthwhile to commit now. The individual threads each maintain a pointer to the icc_table and the link_cache which are member variables of the primary clist reader device. During the creation of a link in the link cache in gsicc_get_link_profile a lock is placed on the cache. If the number of links is at (or above) the maximum a wait semaphore is signaled to the thread. When a link is released in gsicc_release_link, gx_semaphore_signal is applied for all threads that are waiting for a cache slot. Note that gsicc_release_link is also wrapped with a cache lock (i.e. gx_monitor_enter and gx_monitor_leave). The threads that are waiting will then race in gsicc_findcachelink to lock up the cache and grab the available slot.
git-svn-id: http://svn.ghostscript.com/ghostscript/branches/icc_work@11279 a1074d23-0009-0410-80fe-cf8c14f379e6
-rw-r--r--gs/base/gscms.h18
-rw-r--r--gs/base/gsicc_profilecache.c133
-rw-r--r--gs/base/gsicccache.c487
-rw-r--r--gs/base/gsicccache.h2
-rw-r--r--gs/base/gxclist.c5
-rw-r--r--gs/base/gxclist.h9
-rw-r--r--gs/base/gxclread.c10
-rw-r--r--gs/base/gxclthrd.c10
-rw-r--r--gs/base/lib.mak4
9 files changed, 395 insertions, 283 deletions
diff --git a/gs/base/gscms.h b/gs/base/gscms.h
index a11e8866d..9b415a15f 100644
--- a/gs/base/gscms.h
+++ b/gs/base/gscms.h
@@ -21,6 +21,7 @@
#include "gstypes.h"
#include "gsutil.h" /* Need for the object types */
#include "gsdevice.h" /* Need to carry pointer to clist reader */
+#include "gxsync.h" /* for semaphore and monitors */
#include "stdint_.h"
#define ICC_MAX_CHANNELS 15
@@ -181,15 +182,14 @@ typedef struct gsicc_profile_entry_s gsicc_profile_entry_t;
struct gsicc_profile_entry_s {
gs_color_space *color_space; /* The color space with the profile */
gsicc_profile_entry_t *next; /* next CS */
- gsicc_profile_entry_t *prev; /* previous CS */
+ int ref_count;
int64_t key; /* Key based off dictionary location */
};
/* ProfileList. The size of the list is limited by max_memory_size.
Profiles are added if there is sufficient memory. */
typedef struct gsicc_profile_cache_s {
- gsicc_profile_entry_t *first_entry;
- gsicc_profile_entry_t *last_entry;
+ gsicc_profile_entry_t *head;
int num_entries;
rc_header rc;
gs_memory_t *memory;
@@ -223,11 +223,14 @@ struct gsicc_link_s {
void *link_handle;
void *contextptr;
gsicc_hashlink_t hashcode;
+ struct gsicc_link_cache_s *icc_link_cache;
int ref_count;
- gsicc_link_t *nextlink;
- gsicc_link_t *prevlink;
+ gsicc_link_t *next;
+ gx_semaphore_t *wait; /* semaphore used by waiting threads */
+ int num_waiting;
bool includes_softproof;
bool is_identity; /* Used for noting that this is an identity profile */
+ bool valid; /* true once link is completely built and usable */
};
@@ -237,10 +240,13 @@ struct gsicc_link_s {
*/
typedef struct gsicc_link_cache_s {
- gsicc_link_t *icc_link;
+ gsicc_link_t *head;
int num_links;
rc_header rc;
gs_memory_t *memory;
+ gx_monitor_t *lock; /* handle for the monitor */
+ gx_semaphore_t *wait; /* somebody needs a link cache slot */
+ int num_waiting; /* number of threads waiting */
} gsicc_link_cache_t;
/* A linked list structure to keep DeviceN ICC profiles
diff --git a/gs/base/gsicc_profilecache.c b/gs/base/gsicc_profilecache.c
index 4809d5e36..5da77629c 100644
--- a/gs/base/gsicc_profilecache.c
+++ b/gs/base/gsicc_profilecache.c
@@ -33,12 +33,12 @@ static void rc_gsicc_profile_cache_free(gs_memory_t * mem, void *ptr_in,
client_name_t cname);
static void gsicc_remove_cs_entry(gsicc_profile_cache_t *profile_cache);
-gs_private_st_ptrs3(st_profile_entry, gsicc_profile_entry_t,
+gs_private_st_ptrs2(st_profile_entry, gsicc_profile_entry_t,
"gsicc_profile_entry", profile_entry_enum_ptrs,
- profile_entry_reloc_ptrs, color_space, next, prev);
-gs_private_st_ptrs2(st_profile_cache, gsicc_profile_cache_t,
+ profile_entry_reloc_ptrs, color_space, next);
+gs_private_st_ptrs1(st_profile_cache, gsicc_profile_cache_t,
"gsicc_profile_cache", profile_list_enum_ptrs,
- profile_list_reloc_ptrs, first_entry, last_entry);
+ profile_list_reloc_ptrs, head);
/**
* gsicc_cache_new: Allocate a new ICC cache manager
@@ -56,8 +56,7 @@ gsicc_profilecache_new(gs_memory_t *memory)
if ( result == NULL )
return(NULL);
rc_init_free(result, memory->stable_memory, 1, rc_gsicc_profile_cache_free);
- result->first_entry = NULL;
- result->last_entry = NULL;
+ result->head = NULL;
result->num_entries = 0;
result->memory = memory;
return(result);
@@ -67,16 +66,20 @@ static void
rc_gsicc_profile_cache_free(gs_memory_t * mem, void *ptr_in, client_name_t cname)
{
gsicc_profile_cache_t *profile_cache = (gsicc_profile_cache_t * ) ptr_in;
- gsicc_profile_entry_t *curr_entry;
+ gsicc_profile_entry_t *curr = profile_cache->head;
- curr_entry = profile_cache->first_entry;
- while (curr_entry != NULL ){
- rc_decrement(curr_entry->color_space, "rc_gsicc_profile_cache_free");
- gs_free_object(mem->stable_memory, curr_entry,
+ while (curr != NULL ){
+ rc_decrement(curr->color_space, "rc_gsicc_profile_cache_free");
+ gs_free_object(mem->stable_memory, curr,
"rc_gsicc_profile_cache_free");
profile_cache->num_entries--;
- curr_entry = curr_entry->next;
+ curr = curr->next;
}
+#ifdef DEBUG
+ if (profile_cache->num_entries != 0)
+ eprintf1("gsicc_profile_cache_free, num_entries is %d (should be 0).\n",
+ profile_cache->num_entries);
+#endif
gs_free_object(mem->stable_memory, profile_cache,
"rc_gsicc_profile_cache_free");
}
@@ -92,25 +95,14 @@ gsicc_add_cs(gs_state * pgs, gs_color_space * colorspace, ulong dictkey)
to be maintained across the gsave and grestore process */
result = gs_alloc_struct(memory->stable_memory, gsicc_profile_entry_t,
&st_profile_entry, "gsicc_add_cs");
- /* If needed, remove an entry */
+ /* If needed, remove an entry (the last one) */
if (profile_cache->num_entries > ICC_CACHE_MAXPROFILE) {
gsicc_remove_cs_entry(profile_cache);
}
- if (profile_cache->first_entry != NULL){
- /* Add to the top of the list.
- That way we find the MRU enty right away.
- Last entry stays the same. */
- profile_cache->first_entry->prev = result;
- result->prev = NULL;
- result->next = profile_cache->first_entry;
- profile_cache->first_entry = result; /* MRU */
- } else {
- /* First one to add */
- result->next = NULL;
- result->prev = NULL;
- profile_cache->first_entry = result;
- profile_cache->last_entry = result;
- }
+ /* Add to the top of the list. That way we find the MRU enty right away.
+ Last entry stays the same. */
+ result->next = profile_cache->head;
+ profile_cache->head = result; /* MRU */
result->color_space = colorspace;
rc_increment(colorspace);
result->key = dictkey;
@@ -120,58 +112,63 @@ gsicc_add_cs(gs_state * pgs, gs_color_space * colorspace, ulong dictkey)
gs_color_space*
gsicc_find_cs(ulong key_test, gs_state * pgs)
{
- gsicc_profile_entry_t *curr_pos1,*temp1,*temp2,*last;
gsicc_profile_cache_t *profile_cache = pgs->icc_profile_cache;
+ gsicc_profile_entry_t *prev = NULL, *curr = profile_cache->head;
/* Look through the cache for the key. If found, move to MRU */
- curr_pos1 = profile_cache->first_entry;
- while (curr_pos1 != NULL ){
- if (curr_pos1->key == key_test){
- /* First test for MRU. Also catch case of 1 entry */
- if (curr_pos1 == profile_cache->first_entry) {
- return(curr_pos1->color_space);
- }
- /* We need to move found one to the top of the list. */
- last = profile_cache->last_entry;
- temp1 = curr_pos1->prev;
- temp2 = curr_pos1->next;
- if (temp1 != NULL) {
- temp1->next = temp2;
- }
- if (temp2 != NULL) {
- temp2->prev = temp1;
- }
- /* Update last entry if we grabbed the bottom one */
- if (curr_pos1 == profile_cache->last_entry) {
- profile_cache->last_entry = profile_cache->last_entry->prev;
- }
- curr_pos1->prev = NULL;
- curr_pos1->next = profile_cache->first_entry;
- profile_cache->first_entry->prev = curr_pos1;
- profile_cache->first_entry = curr_pos1;
- return(curr_pos1->color_space);
+ while (curr != NULL ){
+ if (curr->key == key_test){
+ /* If not already at head of list, move this one there */
+ if (curr != profile_cache->head) {
+ /* We need to move found one to the top of the list. */
+ prev->next = curr->next;
+ curr->next = profile_cache->head;
+ profile_cache->head = curr;
+ }
+ return(curr->color_space);
}
- curr_pos1 = curr_pos1->next;
+ curr = curr->next;
}
return(NULL);
}
/* Remove the LRU entry, which ideally is at the bottom */
static void
-gsicc_remove_cs_entry(gsicc_profile_cache_t *profile_cache){
-
- gsicc_profile_entry_t *last_entry = profile_cache->last_entry;
+gsicc_remove_cs_entry(gsicc_profile_cache_t *profile_cache)
+{
gs_memory_t *memory = profile_cache->memory;
+ gsicc_profile_entry_t *prev = NULL, *curr = profile_cache->head;
- if (profile_cache->last_entry->prev != NULL) {
- profile_cache->last_entry->prev->next = NULL;
- profile_cache->last_entry = profile_cache->last_entry->prev;
- } else {
- /* No entries */
- profile_cache->first_entry = NULL;
- profile_cache->last_entry = NULL;
+#ifdef DEBUG
+ if (curr == NULL) {
+ eprintf(" attempt to remove from an empty profile cache.\n");
+ gs_abort();
+ }
+#endif
+ while (curr->next != NULL) {
+ prev = curr;
+ curr = curr->next;
+ }
+ if (curr->ref_count != 0) {
+#ifdef DEBUG
+ eprintf1("gsicc_remove_cs_entry cannot remove any, num_entries=%d\n",
+ profile_cache->num_entries);
+#endif
+ return; /* better than failing */
}
- rc_decrement(last_entry->color_space, "gsicc_remove_cs_entry");
- gs_free_object(memory->stable_memory, last_entry, "gsicc_remove_cs_entry");
profile_cache->num_entries--;
+ if (prev == NULL) {
+ /* No more entries */
+ profile_cache->head = NULL;
+#ifdef DEBUG
+ if (profile_cache->num_entries != 0) {
+ eprintf1("profile cache list empty, but list has num_entries=%d.\n",
+ profile_cache->num_entries);
+ }
+#endif
+ } else {
+ prev->next = NULL; /* new tail */
+ }
+ rc_decrement(curr->color_space, "gsicc_remove_cs_entry");
+ gs_free_object(memory->stable_memory, curr, "gsicc_remove_cs_entry");
} \ No newline at end of file
diff --git a/gs/base/gsicccache.c b/gs/base/gsicccache.c
index d750bc95f..ba6896b8a 100644
--- a/gs/base/gsicccache.c
+++ b/gs/base/gsicccache.c
@@ -28,6 +28,7 @@
#include "gserrors.h"
#include "gsmalloc.h" /* Needed for named color structure allocation */
#include "string_.h" /* Needed for named color structure allocation */
+#include "gxsync.h"
/*
* Note that the the external memory used to maintain
* links in the CMS is generally not visible to GS.
@@ -44,47 +45,42 @@
/* Static prototypes */
-static gsicc_link_t * gsicc_add_link(gsicc_link_cache_t *link_cache,
- void *link_handle, void *ContextPtr,
- gsicc_hashlink_t hashcode,
- gs_memory_t *memory);
+static gsicc_link_t * gsicc_alloc_link(gs_memory_t *memory, gsicc_hashlink_t hashcode);
+
+static void gsicc_set_icc_link_data(gsicc_link_t *icc_link, void *link_handle, void *contextptr,
+ gsicc_hashlink_t hashcode);
+
static void gsicc_link_free(gsicc_link_t *icc_link, gs_memory_t *memory);
-static void gsicc_get_cspace_hash(gsicc_manager_t *icc_manager,
- cmm_profile_t *profile, int64_t *hash);
-static void gsicc_compute_linkhash(gsicc_manager_t *icc_manager,
- cmm_profile_t *input_profile,
- cmm_profile_t *output_profile,
- gsicc_rendering_param_t *rendering_params,
- gsicc_hashlink_t *hash);
-static gsicc_link_t* gsicc_findcachelink(gsicc_hashlink_t hashcode,
- gsicc_link_cache_t *icc_cache,
- bool includes_proof);
+static void gsicc_get_cspace_hash(gsicc_manager_t *icc_manager, cmm_profile_t *profile, int64_t *hash);
+
+static void gsicc_compute_linkhash(gsicc_manager_t *icc_manager, cmm_profile_t *input_profile,
+ cmm_profile_t *output_profile,
+ gsicc_rendering_param_t *rendering_params, gsicc_hashlink_t *hash);
-static gsicc_link_t* gsicc_find_zeroref_cache(gsicc_link_cache_t *icc_cache);
+static gsicc_link_t* gsicc_findcachelink(gsicc_hashlink_t hashcode,gsicc_link_cache_t *icc_link_cache,
+ bool includes_proof);
-static void gsicc_remove_link(gsicc_link_t *link,gsicc_link_cache_t *icc_cache,
- gs_memory_t *memory);
+static gsicc_link_t* gsicc_find_zeroref_cache(gsicc_link_cache_t *icc_link_cache);
-static void gsicc_get_buff_hash(unsigned char *data,unsigned int num_bytes,
- int64_t *hash);
+static void gsicc_remove_link(gsicc_link_t *link, gs_memory_t *memory);
-static void rc_gsicc_cache_free(gs_memory_t * mem, void *ptr_in,
- client_name_t cname);
+static void gsicc_get_buff_hash(unsigned char *data, int64_t *hash, unsigned int num_bytes);
+
+static void rc_gsicc_link_cache_free(gs_memory_t * mem, void *ptr_in, client_name_t cname);
/* Structure pointer information */
-gs_private_st_ptrs3(st_icc_link, gsicc_link_t, "gsiccmanage_link",
+gs_private_st_ptrs4(st_icc_link, gsicc_link_t, "gsiccmanage_link",
icc_link_enum_ptrs, icc_link_reloc_ptrs,
- contextptr, nextlink, prevlink);
+ contextptr, icc_link_cache, next, wait);
-gs_private_st_ptrs1(st_icc_linkcache, gsicc_link_cache_t, "gsiccmanage_linkcache",
+gs_private_st_ptrs3(st_icc_linkcache, gsicc_link_cache_t, "gsiccmanage_linkcache",
icc_linkcache_enum_ptrs, icc_linkcache_reloc_ptrs,
- icc_link);
-
+ head, lock, wait);
/**
* gsicc_cache_new: Allocate a new ICC cache manager
@@ -96,95 +92,121 @@ gsicc_cache_new(gs_memory_t *memory)
{
gsicc_link_cache_t *result;
- /* We want this to be maintained in stable_memory. It should not be
- affected by the save and restores */
- result = gs_alloc_struct(memory->stable_memory, gsicc_link_cache_t,
- &st_icc_linkcache, "gsicc_cache_new");
+ /* We want this to be maintained in stable_memory. It should be be effected by the
+ save and restores */
+ result = gs_alloc_struct(memory->stable_memory, gsicc_link_cache_t, &st_icc_linkcache,
+ "gsicc_cache_new");
if ( result == NULL )
return(NULL);
- rc_init_free(result, memory->stable_memory, 1, rc_gsicc_cache_free);
- result->icc_link = NULL;
+ result->lock = gx_monitor_alloc(memory->stable_memory);
+ result->wait = gx_semaphore_alloc(memory->stable_memory);
+ if (result->lock == NULL || result->wait == NULL) {
+ gs_free_object(memory->stable_memory, result, "gsicc_cache_new");
+ return(NULL);
+ }
+ result->num_waiting = 0;
+ rc_init_free(result, memory->stable_memory, 1, rc_gsicc_link_cache_free);
+ result->head = NULL;
result->num_links = 0;
- result->memory = memory;
+ result->memory = memory->stable_memory;
return(result);
}
static void
-rc_gsicc_cache_free(gs_memory_t * mem, void *ptr_in, client_name_t cname)
+rc_gsicc_link_cache_free(gs_memory_t * mem, void *ptr_in, client_name_t cname)
{
/* Ending the entire cache. The ref counts on all the links should be 0 */
gsicc_link_cache_t *link_cache = (gsicc_link_cache_t * ) ptr_in;
- int k;
- gsicc_link_t *link;
- link = gsicc_find_zeroref_cache(link_cache);
- for( k = 0; k < link_cache->num_links; k++) {
- if ( link_cache->icc_link != NULL) {
- gsicc_remove_link(link_cache->icc_link,link_cache, mem);
- }
+ while (link_cache->head != NULL) {
+ gsicc_remove_link(link_cache->head, mem);
+ link_cache->num_links--;
}
- link_cache->num_links = 0;
- gs_free_object(mem->stable_memory, link_cache, "rc_gsicc_cache_free");
+#ifdef DEBUG
+ if (link_cache->num_links != 0) {
+ eprintf1("num_links is %d, should be 0.\n", link_cache->num_links);
+ }
+#endif
+ gs_free_object(mem->stable_memory, link_cache->lock, "rc_gsicc_link_cache_free(lock)");
+ gs_free_object(mem->stable_memory, link_cache->wait, "rc_gsicc_link_cache_free(wait)");
+ gs_free_object(mem->stable_memory, link_cache, "rc_gsicc_link_cache_free");
}
static gsicc_link_t *
-gsicc_add_link(gsicc_link_cache_t *link_cache, void *link_handle, void *contextptr,
- gsicc_hashlink_t hashcode, gs_memory_t *memory)
+gsicc_alloc_link(gs_memory_t *memory, gsicc_hashlink_t hashcode)
{
- gsicc_link_t *result, *nextlink;
+ gsicc_link_t *result;
+ gx_semaphore_t *wait;
/* The link has to be added in stable memory. We want them
to be maintained across the gsave and grestore process */
result = gs_alloc_struct(memory->stable_memory, gsicc_link_t, &st_icc_link,
- "gsiccmanage_link_new");
- result->contextptr = contextptr;
- result->link_handle = link_handle;
- result->hashcode.link_hashcode = hashcode.link_hashcode;
- result->hashcode.des_hash = hashcode.des_hash;
- result->hashcode.src_hash = hashcode.src_hash;
- result->hashcode.rend_hash = hashcode.rend_hash;
- result->ref_count = 1;
- result->includes_softproof = 0; /* Need to enable this at some point */
+ "gsicc_alloc_link");
+ wait = gx_semaphore_alloc(memory->stable_memory);
+ if (wait == NULL) {
+ gs_free_object(memory, result, "gsicc_alloc_link(wait)");
+ result = NULL;
+ }
+ if (result != NULL) {
+ /* set up placeholder values */
+ result->next = NULL;
+ result->contextptr = NULL;
+ result->link_handle = NULL;
+ result->hashcode.link_hashcode = hashcode.link_hashcode;
+ result->hashcode.des_hash = 0;
+ result->hashcode.src_hash = 0;
+ result->hashcode.rend_hash = 0;
+ result->ref_count = 1; /* prevent it from being freed */
+ result->includes_softproof = 0;
+ result->is_identity = false;
+ result->valid = false; /* not yet complete */
+ result->num_waiting = 0;
+ result->wait = wait;
+ }
+ return(result);
+}
+
+static void
+gsicc_set_link_data(gsicc_link_t *icc_link, void *link_handle, void *contextptr,
+ gsicc_hashlink_t hashcode, gx_monitor_t *lock)
+{
+ 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 = 0; /* Need to enable this at some point */
if ( hashcode.src_hash == hashcode.des_hash ) {
- result->is_identity = true;
+ icc_link->is_identity = true;
} else {
- result->is_identity = false;
+ icc_link->is_identity = false;
}
- if (link_cache->icc_link != NULL) {
- /* Add where ever we are right
- now. Later we may want to
- do this differently. */
- nextlink = link_cache->icc_link->nextlink;
- link_cache->icc_link->nextlink = result;
- result->prevlink = link_cache->icc_link;
- result->nextlink = nextlink;
- if (nextlink != NULL) {
- nextlink->prevlink = result;
- }
- } else {
- result->nextlink = NULL;
- result->prevlink = NULL;
- link_cache->icc_link = result;
+ 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--;
}
- link_cache->num_links++;
- return(result);
+ gx_monitor_leave(lock); /* done with updating, let everyone run */
}
static void
gsicc_link_free(gsicc_link_t *icc_link, gs_memory_t *memory)
{
gscms_release_link(icc_link);
- gs_free_object(memory->stable_memory, icc_link, "gsiccmanage_link_free");
+ gs_free_object(memory->stable_memory, icc_link->wait, "gsicc_link_free(wait)");
+ gs_free_object(memory->stable_memory, icc_link, "gsicc_link_free");
}
static void
-gsicc_get_gscs_hash(gsicc_manager_t *icc_manager, gs_color_space *colorspace,
- int64_t *hash)
+gsicc_get_gscs_hash(gsicc_manager_t *icc_manager, gs_color_space *colorspace, int64_t *hash)
{
- /* There may be some work to do here with respect to pattern
- and indexed spaces */
+ /* There may be some work to do here with respect to pattern and indexed spaces */
const gs_color_space_type *pcst = colorspace->type;
switch(pcst->index) {
@@ -209,13 +231,13 @@ gsicc_mash_hash(gsicc_hashlink_t *hash)
}
void
-gsicc_get_icc_buff_hash(unsigned char *buffer, int64_t *hash, int profile_size)
+gsicc_get_icc_buff_hash(unsigned char *buffer, int64_t *hash, unsigned int buff_size)
{
- gsicc_get_buff_hash(buffer,profile_size,hash);
+ gsicc_get_buff_hash(buffer, hash, buff_size);
}
static void
-gsicc_get_buff_hash(unsigned char *data,unsigned int num_bytes,int64_t *hash)
+gsicc_get_buff_hash(unsigned char *data, int64_t *hash, unsigned int num_bytes)
{
gs_md5_state_t md5;
byte digest[16];
@@ -257,16 +279,15 @@ gsicc_compute_linkhash(gsicc_manager_t *icc_manager, cmm_profile_t *input_profil
gsicc_get_cspace_hash(icc_manager, output_profile, &(hash->des_hash));
/* now for the rendering paramaters */
- gsicc_get_buff_hash((unsigned char *) rendering_params,
- sizeof(gsicc_rendering_param_t),&(hash->rend_hash));
+ gsicc_get_buff_hash((unsigned char *) rendering_params, &(hash->rend_hash),
+ sizeof(gsicc_rendering_param_t));
/* for now, mash all of these into a link hash */
gsicc_mash_hash(hash);
}
static void
-gsicc_get_cspace_hash(gsicc_manager_t *icc_manager,
- cmm_profile_t *cmm_icc_profile_data, int64_t *hash)
+gsicc_get_cspace_hash(gsicc_manager_t *icc_manager, cmm_profile_t *cmm_icc_profile_data, int64_t *hash)
{
if (cmm_icc_profile_data == NULL ) {
*hash = icc_manager->device_profile->hashcode;
@@ -277,8 +298,8 @@ gsicc_get_cspace_hash(gsicc_manager_t *icc_manager,
return;
} else {
/* We need to compute for this color space */
- gsicc_get_icc_buff_hash(cmm_icc_profile_data->buffer, hash,
- cmm_icc_profile_data->buffer_size);
+ gsicc_get_icc_buff_hash(cmm_icc_profile_data->buffer, hash,
+ cmm_icc_profile_data->buffer_size);
cmm_icc_profile_data->hashcode = *hash;
cmm_icc_profile_data->hash_is_valid = true;
return;
@@ -286,89 +307,109 @@ gsicc_get_cspace_hash(gsicc_manager_t *icc_manager,
}
static gsicc_link_t*
-gsicc_findcachelink(gsicc_hashlink_t hash,gsicc_link_cache_t *icc_cache,
- bool includes_proof)
+gsicc_findcachelink(gsicc_hashlink_t hash, gsicc_link_cache_t *icc_link_cache, bool includes_proof)
{
- gsicc_link_t *curr_pos1,*curr_pos2;
+ gsicc_link_t *curr, *prev;
int64_t hashcode = hash.link_hashcode;
/* Look through the cache for the hashcode */
- curr_pos1 = icc_cache->icc_link;
- curr_pos2 = curr_pos1;
-
- while (curr_pos1 != NULL ) {
- if (curr_pos1->hashcode.link_hashcode == hashcode &&
- includes_proof == curr_pos1->includes_softproof) {
- return(curr_pos1);
- }
- curr_pos1 = curr_pos1->prevlink;
- }
- while (curr_pos2 != NULL ) {
- if (curr_pos2->hashcode.link_hashcode == hashcode &&
- includes_proof == curr_pos2->includes_softproof) {
- return(curr_pos2);
+ 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 */
}
- curr_pos2 = curr_pos2->nextlink;
+ prev = curr;
+ curr = curr->next;
}
+ gx_monitor_leave(icc_link_cache->lock);
return(NULL);
}
/* Find entry with zero ref count and remove it */
-/* may need to lock cache during this time to avoid */
-/* issue in multi-threaded case */
-
+/* lock the cache during this time for multi-threaded case */
+/* This may release links waiting for an icc_link_cache slot */
static gsicc_link_t*
-gsicc_find_zeroref_cache(gsicc_link_cache_t *icc_cache)
+gsicc_find_zeroref_cache(gsicc_link_cache_t *icc_link_cache)
{
- gsicc_link_t *curr_pos1,*curr_pos2;
-
- /* Look through the cache for zero ref count */
- curr_pos1 = icc_cache->icc_link;
- curr_pos2 = curr_pos1;
-
- while (curr_pos1 != NULL ) {
- if (curr_pos1->ref_count == 0) {
- return(curr_pos1);
- }
- curr_pos1 = curr_pos1->prevlink;
- }
-
- while (curr_pos2 != NULL ) {
- if (curr_pos2->ref_count == 0) {
- return(curr_pos2);
- }
- curr_pos2 = curr_pos2->nextlink;
- }
- return(NULL);
+ gsicc_link_t *curr = NULL;
+
+ /* Look through the cache for first zero ref count */
+ /* when ref counts go to zero, the icc_link is moved to the */
+ /* end of the list, so the first we find is the 'oldest' */
+ while (curr == NULL) {
+ gx_monitor_enter(icc_link_cache->lock);
+ curr = icc_link_cache->head;
+ while (curr != NULL ) {
+ if (curr->ref_count == 0) {
+ curr->ref_count++; /* we will use this one */
+ gx_monitor_leave(icc_link_cache->lock);
+ break;
+ }
+ curr = curr->next;
+ }
+ /* We need to wait for a link to get released and go to ref_count 0 */
+ icc_link_cache->num_waiting++;
+ /* we have bumped num_waiting, so we can be assured of the */
+ /* semaphore being signalled. Then whoever gets there first wins */
+ gx_monitor_leave(icc_link_cache->lock);
+ gx_semaphore_wait(icc_link_cache->wait);
+ }
+ return(curr);
}
/* Remove link from cache. Notify CMS and free */
static void
-gsicc_remove_link(gsicc_link_t *link, gsicc_link_cache_t *icc_cache,
- gs_memory_t *memory)
+gsicc_remove_link(gsicc_link_t *link, gs_memory_t *memory)
{
- gsicc_link_t *prevlink,*nextlink;
-
- prevlink = link->prevlink;
- nextlink = link->nextlink;
-
- if (prevlink != NULL) {
- prevlink->nextlink = nextlink;
- } else {
- /* It is the first link */
- icc_cache->icc_link = nextlink;
- }
- if (nextlink != NULL) {
- nextlink->prevlink = prevlink;
+ 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;
}
- gsicc_link_free(link, memory);
+ /* if curr != link we didn't find it: assert ? */
+ gx_monitor_leave(icc_link_cache->lock);
+ gsicc_link_free(link, memory); /* outside link */
}
gsicc_link_t*
gsicc_get_link(const gs_imager_state *pis, const gs_color_space *input_colorspace,
gs_color_space *output_colorspace,
- gsicc_rendering_param_t *rendering_params,
- gs_memory_t *memory, bool include_softproof)
+ gsicc_rendering_param_t *rendering_params, gs_memory_t *memory, bool include_softproof)
{
cmm_profile_t *gs_input_profile;
cmm_profile_t *gs_output_profile;
@@ -399,49 +440,72 @@ gsicc_get_link(const gs_imager_state *pis, const gs_color_space *input_colorspa
gsicc_link_t*
gsicc_get_link_profile(gs_imager_state *pis, 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_rendering_param_t *rendering_params, gs_memory_t *memory, bool include_softproof)
{
gsicc_hashlink_t hash;
gsicc_link_t *link;
gcmmhlink_t link_handle = NULL;
void **contextptr = NULL;
gsicc_manager_t *icc_manager = pis->icc_manager;
- gsicc_link_cache_t *icc_cache = pis->icc_link_cache;
+ 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, gs_input_profile, gs_output_profile,
- rendering_params, &hash);
+ /* If the output color space is NULL we will use the device profile for the output color space */
+ gsicc_compute_linkhash(icc_manager, gs_input_profile, gs_output_profile, rendering_params, &hash);
/* Check the cache for a hit. Need to check if softproofing was used */
- link = gsicc_findcachelink(hash,icc_cache,include_softproof);
+ link = gsicc_findcachelink(hash, icc_link_cache, include_softproof);
- /* Got a hit, update the reference count, return link */
+ /* Got a hit, return link (ref_count for the link was already bumped */
if (link != NULL) {
- link->ref_count++;
- return(link); /* TO FIX: We are really not going to want to have the
- members of this object visible outside gsiccmange */
+ return(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 have space */
- if (icc_cache->num_links > ICC_CACHE_MAXLINKS) {
- /* If not, see if there is anything we can remove from cache.
- Need to spend some time on this.... */
- link = gsicc_find_zeroref_cache(icc_cache);
- if ( link == NULL) {
- return(NULL);
- } else {
- gsicc_remove_link(link,icc_cache, memory);
- }
+ /* 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); /* Multi-threaded Breakpoint ? */
+ 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 */
+
+ /*FIXME: we might want to repeat the findcachelink to see */
+ /* if some other thread has already started on this link */
+ /* If so, we'll need to change findcachelink so that it */
+ /* doesn't request the lock. */
+ }
+ /* 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--;
}
-
- /* Now get the link */
+ /* 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) {
@@ -455,12 +519,14 @@ gsicc_get_link_profile(gs_imager_state *pis, cmm_profile_t *gs_input_profile,
/* ICC profile should be in clist. This is
the first call to it. */
cms_input_profile =
- gsicc_get_profile_handle_clist(gs_input_profile, memory);
+ gsicc_get_profile_handle_clist(gs_input_profile, cache_mem);
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);
}
}
@@ -469,9 +535,9 @@ gsicc_get_link_profile(gs_imager_state *pis, cmm_profile_t *gs_input_profile,
cms_output_profile = gs_output_profile->profile_handle;
if (cms_output_profile == NULL) {
if (gs_output_profile->buffer != NULL) {
- cms_output_profile =
- gsicc_get_profile_handle_buffer(gs_output_profile->buffer,
- gs_output_profile->buffer_size);
+ 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. */
@@ -479,12 +545,14 @@ gsicc_get_link_profile(gs_imager_state *pis, cmm_profile_t *gs_input_profile,
/* ICC profile should be in clist. This is
the first call to it. */
cms_output_profile =
- gsicc_get_profile_handle_clist(gs_output_profile, memory);
+ gsicc_get_profile_handle_clist(gs_output_profile, cache_mem);
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);
}
}
@@ -492,8 +560,10 @@ gsicc_get_link_profile(gs_imager_state *pis, cmm_profile_t *gs_input_profile,
link_handle = gscms_get_link(cms_input_profile, cms_output_profile,
rendering_params);
if (link_handle != NULL) {
- link = gsicc_add_link(icc_cache, link_handle,contextptr, hash, memory);
+ 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);
@@ -625,7 +695,7 @@ gsicc_transform_named_color(float tint_value, byte *color_name, uint name_size,
/* Note that we do this in non-GC memory since the
profile pointer is not GC'd */
namedcolor_table =
- (gsicc_namedcolortable_t*) gs_malloc(pis->memory, 1,
+ (gsicc_namedcolortable_t*) gs_malloc(pis->memory->stable_memory, 1,
sizeof(gsicc_namedcolortable_t),
"gsicc_transform_named_color");
if (namedcolor_table == NULL) return(-1);
@@ -648,7 +718,7 @@ gsicc_transform_named_color(float tint_value, byte *color_name, uint name_size,
return (-1);
}
namedcolor_data =
- (gsicc_namedcolor_t*) gs_malloc(pis->memory, num_entries,
+ (gsicc_namedcolor_t*) gs_malloc(pis->memory->stable_memory, num_entries,
sizeof(gsicc_namedcolor_t),
"gsicc_transform_named_color");
if (namedcolor_data == NULL) {
@@ -679,7 +749,7 @@ gsicc_transform_named_color(float tint_value, byte *color_name, uint name_size,
namedcolor_data[k].name_size = curr_name_size;
/* +1 for the null */
namedcolor_data[k].colorant_name =
- (char*) gs_malloc(pis->memory,1,name_size+1,
+ (char*) gs_malloc(pis->memory->stable_memory,1,name_size+1,
"gsicc_transform_named_color");
strncpy(namedcolor_data[k].colorant_name,temp_ptr,
namedcolor_data[k].name_size+1);
@@ -764,22 +834,63 @@ gsicc_transform_named_color(float tint_value, byte *color_name, uint name_size,
}
/* Used by gs to notify the ICC manager that we are done with this link for now */
-
+/* This may release elements waiting on a icc_link_cache slot */
void
gsicc_release_link(gsicc_link_t *icclink)
{
- /* Find link in cache */
+ gsicc_link_cache_t *icc_link_cache = icclink->icc_link_cache;
+
+ gx_monitor_enter(icc_link_cache->lock);
/* Decrement the reference count */
- icclink->ref_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);
+ }
+ gx_monitor_leave(icc_link_cache->lock);
}
/* Used to initialize the buffer description prior to color conversion */
void
-gsicc_init_buffer(gsicc_bufferdesc_t *buffer_desc, unsigned char num_chan,
- unsigned char bytes_per_chan, bool has_alpha,
- bool alpha_first, bool is_planar, int plane_stride,
- int row_stride, int num_rows, int pixels_per_row)
+gsicc_init_buffer(gsicc_bufferdesc_t *buffer_desc, unsigned char num_chan, unsigned char bytes_per_chan,
+ bool has_alpha, bool alpha_first, bool is_planar, int plane_stride, int row_stride,
+ int num_rows, int pixels_per_row)
{
buffer_desc->num_chan = num_chan;
buffer_desc->bytes_per_chan = bytes_per_chan;
diff --git a/gs/base/gsicccache.h b/gs/base/gsicccache.h
index 7acd47584..74a5a990d 100644
--- a/gs/base/gsicccache.h
+++ b/gs/base/gsicccache.h
@@ -35,7 +35,7 @@ gsicc_link_t* gsicc_get_link_profile(gs_imager_state *pis,
gsicc_rendering_param_t *rendering_params,
gs_memory_t *memory, bool include_softproof);
void gsicc_release_link(gsicc_link_t *icclink);
-void gsicc_get_icc_buff_hash(unsigned char *buffer, int64_t *hash, int buff_size);
+void gsicc_get_icc_buff_hash(unsigned char *buffer, int64_t *hash, unsigned int buff_size);
int gsicc_transform_named_color(float tint_value, byte *color_name, uint name_size,
gx_color_value device_values[],
const gs_imager_state *pis,
diff --git a/gs/base/gxclist.c b/gs/base/gxclist.c
index 23dd58387..34a1f9893 100644
--- a/gs/base/gxclist.c
+++ b/gs/base/gxclist.c
@@ -723,7 +723,10 @@ clist_finish_page(gx_device *dev, bool flush)
/* Also free the icc_table at this time and the icc_cache */
if (!CLIST_IS_WRITER((gx_device_clist *)dev)) {
/* Free the icc table associated with this device.
- May Need to do some work here for multi-threaded case. MJV to do */
+ The threads that may have pointed to this were destroyed in
+ the above call to clist_teardown_render_threads. Since they
+ all maintained a copy of the cache and the table there should not
+ be any issues. */
gx_device_clist_reader * const crdev = &((gx_device_clist *)dev)->reader;
clist_icc_freetable(crdev->icc_table, crdev->memory);
rc_decrement(crdev->icc_cache_cl,"clist_finish_page");
diff --git a/gs/base/gxclist.h b/gs/base/gxclist.h
index 140897937..fdfb9cf0f 100644
--- a/gs/base/gxclist.h
+++ b/gs/base/gxclist.h
@@ -389,14 +389,7 @@ typedef struct gx_device_clist_reader_s {
byte *main_thread_data; /* saved data pointer of main thread */
int curr_render_thread; /* index into array */
int thread_lookahead_direction; /* +1 or -1 */
- /* clist_icctable_t *icc_table; */ /* Table that keeps track of ICC profiles. It
- relates the hashcode to the cfile file location.
- I did not put this into gx_device_clist_common_members
- since I dont see where those pointers are ever defined
- for GC. */
- /* gsicc_link_cache_t *icc_cache_cl; */ /* A link cache so that each band
- does not recreate the links.
- A big savings */
+
} gx_device_clist_reader;
union gx_device_clist_s {
diff --git a/gs/base/gxclread.c b/gs/base/gxclread.c
index 32d961768..487cbf129 100644
--- a/gs/base/gxclread.c
+++ b/gs/base/gxclread.c
@@ -350,6 +350,10 @@ clist_close_writer_and_init_reader(gx_device_clist *cldev)
if (code < 0)
return code;
code = clist_render_init(cldev);
+ /* Check for and get ICC profile table */
+ code = clist_read_icctable(crdev);
+ /* Allocate the icc cache for the clist reader */
+ crdev->icc_cache_cl = gsicc_cache_new(crdev->memory);
}
return code;
}
@@ -499,12 +503,6 @@ clist_render_init(gx_device_clist *dev)
crdev->render_threads = NULL;
code = gx_clist_reader_read_band_complexity(dev);
- if (code < 0)
- return(code);
- /* Check for and get ICC profile table */
- code = clist_read_icctable(crdev);
- /* Allocate the icc cache for the clist reader */
- crdev->icc_cache_cl = gsicc_cache_new(crdev->memory);
return code;
}
diff --git a/gs/base/gxclthrd.c b/gs/base/gxclthrd.c
index d4281b844..bbe7fd04d 100644
--- a/gs/base/gxclthrd.c
+++ b/gs/base/gxclthrd.c
@@ -155,7 +155,12 @@ clist_setup_render_threads(gx_device *dev, int y)
break;
clist_render_init(ncldev); /* Initialize clist device for reading */
ncdev->page_bfile_end_pos = cdev->page_bfile_end_pos;
-
+ /* Use the same link cache in each thread and the same profile table.
+ The threads are maintained until clist_finish_page. At which
+ point, the threads are torn down and the master clist reader device
+ is destroyed along with the icc_table and the icc_cache_cl */
+ ncdev->icc_cache_cl = cdev->icc_cache_cl;
+ ncdev->icc_table = cdev->icc_table;
/* create the buf device for this thread, and allocate the semaphores */
if ((code = gdev_create_buf_device(cdev->buf_procs.create_buf_device,
&(thread->bdev), cdev->target,
@@ -346,8 +351,7 @@ clist_render_thread(void *data)
crdev->ymin = band_begin_line;
crdev->ymax = band_end_line;
crdev->offset_map = NULL;
- crdev->icc_table = NULL;
- crdev->icc_cache_cl = NULL;
+ //crdev->icc_cache_cl = NULL;
if (code < 0)
thread->status = code; /* shouldn't happen */
else
diff --git a/gs/base/lib.mak b/gs/base/lib.mak
index 72dadec79..c79394721 100644
--- a/gs/base/lib.mak
+++ b/gs/base/lib.mak
@@ -2497,7 +2497,7 @@ $(GLOBJ)gsicc.$(OBJ) : $(GLSRC)gsicc.c $(GXERR) $(math__h) $(memory__h)\
$(GLCC) $(GLO_)gsicc.$(OBJ) $(C_) $(GLSRC)gsicc.c
gscms_h=$(std_h) $(stdpre_h) $(gstypes_h) $(gsutil_h)\
- $(gsdevice_h) $(stdint_h)
+ $(gsdevice_h) $(stdint_h) $(gxsync_h)
gsicc_littlecms_h=$(GLSRC)gsicc_littlecms.h $(gxcvalue_h) $(gscms_h)\
$(std_h) $(gsmemory_h)
gsiccmanage_h=$(GLSRC)gsiccmanage.h $(gsicc_littlecms_h)
@@ -2514,7 +2514,7 @@ $(GLOBJ)gsiccmanage.$(OBJ) : $(GLSRC)gsiccmanage.c $(GX) $(stdpre_h)\
$(GLOBJ)gsicccache.$(OBJ) : $(GLSRC)gsicccache.c $(GX) $(stdpre_h)\
$(gstypes_h) $(gsmemory_h) $(gsstruct_h) $(scommon_h) $(smd5_h)\
$(gxistate_h) $(gscms_h) $(gsiccmanage_h) $(gsicccache_h)\
- $(gserrors_h) $(gsmalloc_h) $(string__h)
+ $(gserrors_h) $(gsmalloc_h) $(string__h) $(gsicc_profilecache_h)
$(GLCC) $(GLO_)gsicccache.$(OBJ) $(C_) $(GLSRC)gsicccache.c
$(GLOBJ)gsicc_profilecache.$(OBJ) : $(GLSRC)gsicc_profilecache.c $(GX) $(std_h)\