mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 00:41:02 +00:00
Merge pull request #72 from triplef/fix-enumeration-queue
Perform non-concurrent block enumeration in place.
This commit is contained in:
commit
78dc958f7b
7 changed files with 144 additions and 117 deletions
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
|||
2019-10-17 Frederik Seiffert <frederik@algoriddim.com>
|
||||
|
||||
* Source/GSDispatch.h:
|
||||
* Source/NSArray.m:
|
||||
* Source/NSDictionary.m:
|
||||
* Source/NSIndexSet.m:
|
||||
* Source/NSOrderedSet.m:
|
||||
* Source/NSSet.m:
|
||||
Perform non-concurrent block enumeration calls in place instead
|
||||
of using serial queue to match Apple platforms.
|
||||
|
||||
2019-10-25 Frederik Seiffert <frederik@algoriddim.com>
|
||||
|
||||
* Headers/Foundation/NSMapTable.h:
|
||||
|
|
|
@ -70,13 +70,21 @@
|
|||
* Release an dispatch object.
|
||||
*/
|
||||
#define GS_DISPATCH_RELEASE(x) dispatch_release(x)
|
||||
|
||||
/**
|
||||
* Allows an arbitrary block to be submitted to the queue. Since dispatch blocks
|
||||
* return nothing and take no arguments, the caller can use the before and after
|
||||
* arguments to set up and tear down the block as required.
|
||||
* Allows an arbitrary block to be submitted to the queue (if available) or run
|
||||
* in place. Since dispatch blocks return nothing and take no arguments, the
|
||||
* caller can use the before and after arguments to guard calling the block as
|
||||
* required.
|
||||
*/
|
||||
#define GS_DISPATCH_SUBMIT_BLOCK(group, queue, before, after, block, args, ...) \
|
||||
dispatch_group_async(group, queue, ^(void){before; block(args, ## __VA_ARGS__); after;})
|
||||
if (queue != NULL) {\
|
||||
dispatch_group_async(group, queue, ^(void){before block(args, ## __VA_ARGS__); after});\
|
||||
} else {\
|
||||
before\
|
||||
block(args, ## __VA_ARGS__);\
|
||||
after\
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits a block without special provisions.
|
||||
|
@ -85,32 +93,30 @@
|
|||
|
||||
|
||||
/**
|
||||
* Convenience macro to create serial or concurrent dispatch queues for the
|
||||
* various -enumerateUsingBlock: methods.
|
||||
* Convenience macro to create concurrent dispatch queues for the various
|
||||
* -enumerateUsingBlock: methods. Non-concurrent will be run in place.
|
||||
*/
|
||||
#define GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(queue, opts)\
|
||||
dispatch_queue_t queue;\
|
||||
dispatch_group_t queue ## Group;\
|
||||
dispatch_queue_t queue = NULL;\
|
||||
dispatch_group_t queue ## Group = NULL;\
|
||||
if (opts & NSEnumerationConcurrent)\
|
||||
{\
|
||||
queue = GS_DISPATCH_GET_DEFAULT_CONCURRENT_QUEUE();\
|
||||
}\
|
||||
else\
|
||||
{\
|
||||
queue = GS_DISPATCH_QUEUE_CREATE(DISPATCH_QUEUE_SERIAL);\
|
||||
}\
|
||||
queue ## Group = GS_DISPATCH_GROUP_CREATE();
|
||||
queue ## Group = GS_DISPATCH_GROUP_CREATE();\
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience macro to destroy serial or concurrent dispatch queues for the
|
||||
* various -enumerateUsingBlock: methods.
|
||||
* Convenience macro to destroy concurrent dispatch queues for the various
|
||||
* -enumerateUsingBlock: methods.
|
||||
*/
|
||||
#define GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(queue, opts)\
|
||||
GS_DISPATCH_GROUP_FINISH(queue ## Group);\
|
||||
GS_DISPATCH_RELEASE(enumQueueGroup);\
|
||||
if (NO == (opts & NSEnumerationConcurrent))\
|
||||
{\
|
||||
GS_DISPATCH_RELEASE(enumQueue);\
|
||||
if (queue != NULL) { \
|
||||
GS_DISPATCH_GROUP_FINISH(queue ## Group);\
|
||||
GS_DISPATCH_RELEASE(queue ## Group);\
|
||||
if (NO == (opts & NSEnumerationConcurrent))\
|
||||
{\
|
||||
GS_DISPATCH_RELEASE(queue);\
|
||||
}\
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1790,7 +1790,7 @@ compare(id elem1, id elem2, void* context)
|
|||
{
|
||||
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
|
||||
FOR_IN (id, obj, enumerator)
|
||||
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup, enumQueue, if (YES == shouldStop) {return;}, return, aBlock, obj, count, &shouldStop);
|
||||
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup, enumQueue, if (shouldStop == NO) {, }, aBlock, obj, count, &shouldStop);
|
||||
if (isReverse)
|
||||
{
|
||||
count--;
|
||||
|
@ -1841,30 +1841,32 @@ compare(id elem1, id elem2, void* context)
|
|||
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
|
||||
FOR_IN (id, obj, enumerator)
|
||||
# if __has_feature(blocks) && (GS_USE_LIBDISPATCH == 1)
|
||||
|
||||
dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
|
||||
if (shouldStop)
|
||||
if (enumQueue != NULL)
|
||||
{
|
||||
return;
|
||||
dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
|
||||
if (shouldStop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (predicate(obj, count, &shouldStop))
|
||||
{
|
||||
[setLock lock];
|
||||
[set addIndex: count];
|
||||
[setLock unlock];
|
||||
}
|
||||
});
|
||||
}
|
||||
if (predicate(obj, count, &shouldStop))
|
||||
{
|
||||
[setLock lock];
|
||||
[set addIndex: count];
|
||||
[setLock unlock];
|
||||
}
|
||||
});
|
||||
# else
|
||||
else // call block directly
|
||||
# endif
|
||||
if (CALL_BLOCK(predicate, obj, count, &shouldStop))
|
||||
{
|
||||
/* TODO: It would be more efficient to collect an NSRange and only
|
||||
* pass it to the index set when CALL_BLOCK returned NO. */
|
||||
[set addIndex: count];
|
||||
/* TODO: It would be more efficient to collect an NSRange and only
|
||||
* pass it to the index set when CALL_BLOCK returned NO. */
|
||||
[set addIndex: count];
|
||||
}
|
||||
# endif
|
||||
if (shouldStop)
|
||||
{
|
||||
break;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
END_FOR_IN(enumerator)
|
||||
|
@ -1936,33 +1938,35 @@ compare(id elem1, id elem2, void* context)
|
|||
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
|
||||
FOR_IN (id, obj, enumerator)
|
||||
# if __has_feature(blocks) && (GS_USE_LIBDISPATCH == 1)
|
||||
dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
|
||||
if (shouldStop)
|
||||
if (enumQueue != NULL)
|
||||
{
|
||||
return;
|
||||
dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
|
||||
if (shouldStop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (predicate(obj, count, &shouldStop))
|
||||
{
|
||||
// FIXME: atomic operation on the shouldStop variable would be nicer,
|
||||
// but we don't expose the GSAtomic* primitives anywhere.
|
||||
[indexLock lock];
|
||||
index = count;
|
||||
// Cancel all other predicate evaluations:
|
||||
shouldStop = YES;
|
||||
[indexLock unlock];
|
||||
}
|
||||
});
|
||||
}
|
||||
if (predicate(obj, count, &shouldStop))
|
||||
{
|
||||
// FIXME: atomic operation on the shouldStop variable would be nicer,
|
||||
// but we don't expose the GSAtomic* primitives anywhere.
|
||||
[indexLock lock];
|
||||
index = count;
|
||||
// Cancel all other predicate evaluations:
|
||||
shouldStop = YES;
|
||||
[indexLock unlock];
|
||||
}
|
||||
});
|
||||
# else
|
||||
else // call block directly
|
||||
# endif
|
||||
if (CALL_BLOCK(predicate, obj, count, &shouldStop))
|
||||
{
|
||||
|
||||
index = count;
|
||||
shouldStop = YES;
|
||||
index = count;
|
||||
shouldStop = YES;
|
||||
}
|
||||
# endif
|
||||
if (shouldStop)
|
||||
{
|
||||
break;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
END_FOR_IN(enumerator)
|
||||
|
|
|
@ -178,7 +178,7 @@ static SEL appSel;
|
|||
FOR_IN(id, key, enumerator)
|
||||
obj = (*objectForKey)(self, objectForKeySelector, key);
|
||||
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup, enumQueue,
|
||||
if (shouldStop){return;};, return;, aBlock, key, obj, &shouldStop);
|
||||
if (shouldStop == NO) {, }, aBlock, key, obj, &shouldStop);
|
||||
if (YES == shouldStop)
|
||||
{
|
||||
break;
|
||||
|
@ -1054,28 +1054,30 @@ compareIt(id o1, id o2, void* context)
|
|||
FOR_IN(id, key, enumerator)
|
||||
obj = (*objectForKey)(self, objectForKeySelector, key);
|
||||
#if (__has_feature(blocks) && (GS_USE_LIBDISPATCH == 1))
|
||||
dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
|
||||
if (shouldStop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (aPredicate(key, obj, &shouldStop))
|
||||
{
|
||||
[setLock lock];
|
||||
addObject(buildSet, addObjectSelector, key);
|
||||
[setLock unlock];
|
||||
}
|
||||
});
|
||||
#else
|
||||
if (enumQueue != NULL)
|
||||
{
|
||||
dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
|
||||
if (shouldStop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (aPredicate(key, obj, &shouldStop))
|
||||
{
|
||||
[setLock lock];
|
||||
addObject(buildSet, addObjectSelector, key);
|
||||
[setLock unlock];
|
||||
}
|
||||
});
|
||||
}
|
||||
else // call block directly
|
||||
#endif
|
||||
if (CALL_BLOCK(aPredicate, key, obj, &shouldStop))
|
||||
{
|
||||
addObject(buildSet, addObjectSelector, key);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (YES == shouldStop)
|
||||
if (shouldStop)
|
||||
{
|
||||
break;
|
||||
break;
|
||||
}
|
||||
END_FOR_IN(enumerator)
|
||||
GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
|
||||
|
|
|
@ -917,7 +917,7 @@ static NSUInteger posForIndex(GSIArray array, NSUInteger index)
|
|||
if ((innerI <= lastInRange) && (innerI >= range.location))
|
||||
{
|
||||
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup, enumQueue,
|
||||
if (shouldStop) {return;}, return;,
|
||||
if (shouldStop == NO) {, },
|
||||
aBlock, innerI, &shouldStop);
|
||||
}
|
||||
if (shouldStop)
|
||||
|
|
|
@ -657,7 +657,7 @@ static SEL remSel;
|
|||
{
|
||||
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
|
||||
FOR_IN (id, obj, enumerator)
|
||||
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup, enumQueue, if (YES == shouldStop) {return;}, return, aBlock, obj, count, &shouldStop);
|
||||
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup, enumQueue, if (shouldStop == NO) {, }, aBlock, obj, count, &shouldStop);
|
||||
if (isReverse)
|
||||
{
|
||||
count--;
|
||||
|
@ -865,33 +865,35 @@ static SEL remSel;
|
|||
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
|
||||
FOR_IN (id, obj, enumerator)
|
||||
# if __has_feature(blocks) && (GS_USE_LIBDISPATCH == 1)
|
||||
dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
|
||||
if (shouldStop)
|
||||
if (enumQueue != NULL)
|
||||
{
|
||||
return;
|
||||
dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
|
||||
if (shouldStop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (predicate(obj, count, &shouldStop))
|
||||
{
|
||||
// FIXME: atomic operation on the shouldStop variable would be nicer,
|
||||
// but we don't expose the GSAtomic* primitives anywhere.
|
||||
[indexLock lock];
|
||||
index = count;
|
||||
// Cancel all other predicate evaluations:
|
||||
shouldStop = YES;
|
||||
[indexLock unlock];
|
||||
}
|
||||
});
|
||||
}
|
||||
if (predicate(obj, count, &shouldStop))
|
||||
{
|
||||
// FIXME: atomic operation on the shouldStop variable would be nicer,
|
||||
// but we don't expose the GSAtomic* primitives anywhere.
|
||||
[indexLock lock];
|
||||
index = count;
|
||||
// Cancel all other predicate evaluations:
|
||||
shouldStop = YES;
|
||||
[indexLock unlock];
|
||||
}
|
||||
});
|
||||
# else
|
||||
else // call block directly
|
||||
# endif
|
||||
if (CALL_BLOCK(predicate, obj, count, &shouldStop))
|
||||
{
|
||||
|
||||
index = count;
|
||||
shouldStop = YES;
|
||||
index = count;
|
||||
shouldStop = YES;
|
||||
}
|
||||
# endif
|
||||
if (shouldStop)
|
||||
{
|
||||
break;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
END_FOR_IN(enumerator)
|
||||
|
@ -940,30 +942,32 @@ static SEL remSel;
|
|||
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
|
||||
FOR_IN (id, obj, enumerator)
|
||||
# if __has_feature(blocks) && (GS_USE_LIBDISPATCH == 1)
|
||||
|
||||
dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
|
||||
if (shouldStop)
|
||||
if (enumQueue != NULL)
|
||||
{
|
||||
return;
|
||||
dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
|
||||
if (shouldStop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (predicate(obj, count, &shouldStop))
|
||||
{
|
||||
[setLock lock];
|
||||
[set addIndex: count];
|
||||
[setLock unlock];
|
||||
}
|
||||
});
|
||||
}
|
||||
if (predicate(obj, count, &shouldStop))
|
||||
{
|
||||
[setLock lock];
|
||||
[set addIndex: count];
|
||||
[setLock unlock];
|
||||
}
|
||||
});
|
||||
# else
|
||||
else // call block directly
|
||||
# endif
|
||||
if (CALL_BLOCK(predicate, obj, count, &shouldStop))
|
||||
{
|
||||
/* TODO: It would be more efficient to collect an NSRange and only
|
||||
* pass it to the index set when CALL_BLOCK returned NO. */
|
||||
[set addIndex: count];
|
||||
/* TODO: It would be more efficient to collect an NSRange and only
|
||||
* pass it to the index set when CALL_BLOCK returned NO. */
|
||||
[set addIndex: count];
|
||||
}
|
||||
# endif
|
||||
if (shouldStop)
|
||||
{
|
||||
break;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
END_FOR_IN(enumerator)
|
||||
|
|
|
@ -907,7 +907,7 @@ static Class NSMutableSet_concrete_class;
|
|||
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
|
||||
FOR_IN (id, obj, enumerator)
|
||||
{
|
||||
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup,enumQueue, if (shouldStop) {return;}, return;, aBlock, obj, &shouldStop);
|
||||
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup,enumQueue, if (shouldStop == NO) {, }, aBlock, obj, &shouldStop);
|
||||
if (shouldStop)
|
||||
{
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue