fix for attribute dictionaries whose contents mutate while in the cache

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@37344 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2013-11-01 06:48:20 +00:00
parent cedb544c00
commit 9956ef64e1
2 changed files with 33 additions and 3 deletions

View file

@ -1,6 +1,13 @@
2013-11-01 Richard Frith-Macdonald <rfm@gnu.org>
* Source/GSAttributedString.m: Use exact equality test when removing
attribute dictionary from cache. Prevents possibly removing the
wrong dictionary in cases where dictionary contents are mutated
while in the cache, such that two dictionaries become equal.
2013-10-30 Richard Frith-Macdonald <rfm@gnu.org>
* Source/GSDictionary.m: Add priovate class GSCachedDictionary which
* Source/GSDictionary.m: Add private class GSCachedDictionary which
will raise an exception if deallocated without being uncached.
* Source/GSAttributedString.m: Use GSCachedDictionary for attributes
to aid in detection of things improperly releasing them.

View file

@ -95,11 +95,31 @@
static BOOL adding;
/* When caching attributes we make a shallow copy of the dictionary cached,
* so that it is immutable and safe to cache.
* However, we have a potential problem if the objects within the attributes
* dictionary are themselves mutable, and something mutates them while they
* are in the cache. In this case we could items added while different and
* then mutated to have the same contents, so we would not know which of
* the equal dictionaries to remove.
* The solution is to require dictionaries to be identical for removal.
*/
static inline BOOL
cacheEqual(id A, id B)
{
if (YES == adding)
return [A isEqualToDictionary: B];
else
return A == B;
}
#define GSI_MAP_RETAIN_KEY(M, X)
#define GSI_MAP_RELEASE_KEY(M, X)
#define GSI_MAP_RETAIN_VAL(M, X)
#define GSI_MAP_RELEASE_VAL(M, X)
#define GSI_MAP_EQUAL(M, X,Y) [(X).obj isEqualToDictionary: (Y).obj]
#define GSI_MAP_EQUAL(M, X,Y) cacheEqual((X).obj, (Y).obj)
#define GSI_MAP_KTYPES GSUNION_OBJ
#define GSI_MAP_VTYPES GSUNION_NSINT
#define GSI_MAP_NOCLEAN 1
@ -140,6 +160,7 @@ cacheAttributes(NSDictionary *attrs)
GSIMapNode node;
ALOCK();
adding = YES;
node = GSIMapNodeForKey(&attrMap, (GSIMapKey)((id)attrs));
if (node == 0)
{
@ -173,6 +194,7 @@ unCacheAttributes(NSDictionary *attrs)
id<GSCachedDictionary> removed = nil;
ALOCK();
adding = NO;
bucket = GSIMapBucketForKey(&attrMap, (GSIMapKey)((id)attrs));
if (bucket != 0)
{
@ -854,6 +876,7 @@ SANITY();
while (next < arraySize)
{
GSAttrInfo *n = OBJECTAT(next);
if (n->loc <= NSMaxRange(range))
{
REMOVEAT(arrayIndex);
@ -892,7 +915,7 @@ SANITY();
&& effectiveRange.length == range.length)
{
arrayIndex--;
if (arrayIndex!=0 || arraySize > 1)
if (arrayIndex != 0 || arraySize > 1)
{
REMOVEAT(arrayIndex);
arraySize--;