diff options
author | Michael Vrhel <michael.vrhel@artifex.com> | 2010-05-18 23:51:46 +0000 |
---|---|---|
committer | Michael Vrhel <michael.vrhel@artifex.com> | 2010-05-18 23:51:46 +0000 |
commit | d910aee3084c1cae14f2e957acf4a38d0b21d2b6 (patch) | |
tree | 0993a1c4a801d01fb0264efe95c5daf968fc0fdd | |
parent | dd3ca323b1c3003ac98310b56f8502ab1094ee9e (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.h | 18 | ||||
-rw-r--r-- | gs/base/gsicc_profilecache.c | 133 | ||||
-rw-r--r-- | gs/base/gsicccache.c | 487 | ||||
-rw-r--r-- | gs/base/gsicccache.h | 2 | ||||
-rw-r--r-- | gs/base/gxclist.c | 5 | ||||
-rw-r--r-- | gs/base/gxclist.h | 9 | ||||
-rw-r--r-- | gs/base/gxclread.c | 10 | ||||
-rw-r--r-- | gs/base/gxclthrd.c | 10 | ||||
-rw-r--r-- | gs/base/lib.mak | 4 |
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)\ |