Detect the presence of libdispatch and, if available, use it for collection

enumeration methods that take blocks as arguments. This allows us to implement
the NSEnumerationConcurrent option of those methods with minimal effort.

The searching methods on NSIndexSet and all the sorting methods are still
missing.



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@35010 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
thebeing 2012-03-27 17:05:19 +00:00
parent de00ca1cc4
commit a5c6cfa9f7
15 changed files with 22804 additions and 4578 deletions

View file

@ -1,3 +1,28 @@
2012-03-27 Niels Grewe <niels.grewe@halbordnung.de>
* configure.ac
* config.mak.in
* Headers/GNUstepBase/GSConfig.h.in
* Headers/GNUstepBase/config.h.in:
Implement configure check for libdispatch.
* configure: Regenerate
* Source/GSDispatch.h: Add portability header with macros for compiling
with or without libdispatch.
* Source/NSArray.m
* Source/NSDictionary.m
* Source/NSIndexSet.m
* Source/NSSet.m
* Headers/Foundation/NSIndexSet.h:
Implement libdispatch support for block enumeration methods.
* Tests/base/NSMutableIndexSet/blocks.m
* Tests/base/NSDictionary/blocks.m
* Tests/base/NSArray/blocks.m:
Test cases for new code.
Detect the presence of libdispatch and, if available, use it for
collection enumeration methods that take blocks as arguments. This
allows us to implement the NSEnumerationConcurrent option of those
methods with minimal effort.
2012-03-27 Niels Grewe <niels.grewe@halbordnung.de>
* Source/NSOperation.m: Remove leftover debugging statements.

View file

@ -28,6 +28,7 @@
#ifndef _NSIndexSet_h_GNUSTEP_BASE_INCLUDE
#define _NSIndexSet_h_GNUSTEP_BASE_INCLUDE
#import <GNUstepBase/GSVersionMacros.h>
#import <GNUstepBase/GSBlocks.h>
#if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST)
@ -96,6 +97,21 @@ extern "C" {
- (NSUInteger) countOfIndexesInRange: (NSRange)range;
#endif
#if OS_API_VERSION(100600,GS_API_LATEST)
DEFINE_BLOCK_TYPE(GSIndexSetEnumerationBlock, void, NSUInteger, BOOL*);
- (void)enumerateIndexesInRange: (NSRange)range
options: (NSEnumerationOptions)opts
usingBlock: (GSIndexSetEnumerationBlock)aBlock;
/**
* Enumerate all indices in the set by applying a block to them.
*/
- (void)enumerateIndexesUsingBlock: (GSIndexSetEnumerationBlock)aBlock;
- (void)enumerateIndexesWithOptions: (NSEnumerationOptions)opts
usingBlock: (GSIndexSetEnumerationBlock)aBlock;
#endif
/**
* Returns the first index value in the receiver or NSNotFound if the
* receiver is empty.

View file

@ -226,7 +226,7 @@ typedef struct {
#define GS_USE_AVAHI @HAVE_AVAHI@
#define GS_USE_MDNS @HAVE_MDNS@
#define GS_USE_ICU @HAVE_ICU@
#define GS_USE_LIBDISPATCH @HAVE_LIBDISPATCH@
#if defined(__WIN32__) || defined(_WIN32) || defined(__MS_WIN32__)
# if !defined(__WIN32__)

View file

@ -1,8 +1,5 @@
/* Headers/GNUstepBase/config.h.in. Generated from configure.ac by autoheader. */
/* Define if building universal (internal helper macro) */
#undef AC_APPLE_UNIVERSAL_BUILD
/* The normal alignment of `pthread_cond_t', in bytes. */
#undef ALIGNOF_PTHREAD_COND_T
@ -193,6 +190,12 @@
*/
#undef HAVE_DIRENT_H
/* Define to 1 if you have the <dispatch/dispatch.h> header file. */
#undef HAVE_DISPATCH_DISPATCH_H
/* Define to 1 if you have the <dispatch.h> header file. */
#undef HAVE_DISPATCH_H
/* Define to 1 if you have the `dladdr' function. */
#undef HAVE_DLADDR
@ -762,44 +765,26 @@
/* Define if using the libffi library for invocations */
#undef USE_LIBFFI
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
/* Define if vasprintf returns the length printed */
#undef VASPRINTF_RETURNS_LENGTH
/* Define if vsprintf returns the length printed */
#undef VSPRINTF_RETURNS_LENGTH
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
# undef WORDS_BIGENDIAN
# endif
/* Define to 1 if your processor stores words with the most significant byte
first (like Motorola and SPARC, unlike Intel and VAX). */
#undef WORDS_BIGENDIAN
/* Define to 1 if on AIX 3.
System headers sometimes define this.
We just want to avoid a redefinition error message. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Define to 1 if on MINIX. */
@ -812,6 +797,14 @@
/* Define to 1 if you need to in order for `stat' and other things to work. */
#undef _POSIX_SOURCE
/* Enable extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus

139
Source/GSDispatch.h Normal file
View file

@ -0,0 +1,139 @@
/* Support header for conditionally enabling use of libdispatch.
Copyright (C) 2012 Free Software Foundation, Inc.
Written by: Niels Grewe <niels.grewe@halbordnung.de>
Date: March 2012
This file is part of the GNUstep Base Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#import "GNUstepBase/GSConfig.h"
#import <GNUstepBase/GSBlocks.h>
#if HAVE_DISPATCH_H
#include <dispatch.h>
#elif HAVE_DISPATCH_DISPATCH_H
#include <dispatch/dispatch.h>
#endif
/*
* If gnustep-base is built with libdispatch support, these macros will expand
* to code for creating and cleaning up after libdispach queues to which blocks
* can be submitted. If libdispatch is not available, setup and teardown will
* be no-ops, and the block will simply be executed on the calling thread.
*/
#if __has_feature(blocks) && (GS_USE_LIBDISPATCH == 1)
/*
* Older versions of libdispatch do not support concurrent queues. We define away the
* attributes in this case.
*/
#ifndef DISPATCH_QUEUE_SERIAL
#define DISPATCH_QUEUE_SERIAL NULL
#endif
#ifndef DISPATCH_QUEUE_CONCURRENT
#define DISPATCH_QUEUE_CONCURRENT NULL
#endif
#define GS_DISPATCH_GET_DEFAULT_CONCURRENT_QUEUE() dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
/**
* This macro creates a dispatch queue using the attributes
*/
#define GS_DISPATCH_QUEUE_CREATE(attr) dispatch_queue_create(NULL, attr)
/**
* Create a dispatch group
*/
#define GS_DISPATCH_GROUP_CREATE() dispatch_group_create()
/**
* Wait for the dispatch group to finish
*/
#define GS_DISPATCH_GROUP_FINISH(group) dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
/**
* 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.
*/
#define GS_DISPATCH_SUBMIT_BLOCK(group, queue, before, after, block, args, ...) \
dispatch_group_async(group, queue, ^(void){before; block(args, ## __VA_ARGS__); after;})
/**
* Submits a block without special provisions.
*/
#define GS_DISPATCH_SUBMIT_BLOCK_NO_ARGS(group, queue, block) dispatch_group_async(group, queue, block)
/**
* Convenience macro to create serial or concurrent dispatch queues for the
* various -enumerateUsingBlock: methods.
*/
#define GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(queue, opts)\
dispatch_queue_t queue;\
dispatch_group_t queue ## Group;\
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();
/**
* Convenience macro to destroy serial or 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);\
}
#else
/*
* No-Op defintions if libdispatch is not supposed to be used.
*/
#define DISPATCH_QUEUE_SERIAL 0
#define DISPATCH_QUEUE_CONCURRENT 0
#define dispatch_queue_attr_t int
#define dispatch_queue_t int
#define dispatch_group_t int
#define GS_DISPATCH_GET_DEFAULT_CONCURRENT_QUEUE() 0
#define GS_DISPATCH_QUEUE_CREATE(attr) 0
#define GS_DISPATCH_GROUP_CREATE() 0
#define GS_DISPATCH_GROUP_FINISH(group)
#define GS_DISPATCH_RELEASE(x)
#define GS_DISPATCH_SUBMIT_BLOCK(group, queue, before, after, block, args...) CALL_BLOCK(block, args)
#define GS_DISPATCH_SUBMIT_BLOCK_NO_ARGS(group, queue, block) CALL_BLOCK_NO_ARGS(block)
#define GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(queue, opts)
#define GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(queue, opts)
#endif

View file

@ -52,7 +52,7 @@
#import "GNUstepBase/NSObject+GNUstepBase.h"
#import "GSPrivate.h"
#import "GSFastEnumeration.h"
#import "GSDispatch.h"
static BOOL GSMacOSXCompatiblePropertyLists(void)
{
if (GSPrivateDefaultsFlag(NSWriteOldStylePropertyLists) == YES)
@ -1645,23 +1645,38 @@ compare(id elem1, id elem2, void* context)
usingBlock: (GSEnumeratorBlock)aBlock
{
NSUInteger count = 0;
BOOL shouldStop = NO;
BLOCK_SCOPE BOOL shouldStop = NO;
BOOL isReverse = (opts & NSEnumerationReverse);
id<NSFastEnumeration> enumerator = self;
/* If we are enumerating in reverse, use the reverse enumerator for fast
* enumeration. */
if (opts & NSEnumerationReverse)
if (isReverse)
{
enumerator = [self reverseObjectEnumerator];
count = ([self count] - 1);
}
{
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
FOR_IN (id, obj, enumerator)
CALL_BLOCK(aBlock, obj, count++, &shouldStop);
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup, enumQueue, if (YES == shouldStop) {return;}, return, aBlock, obj, count, &shouldStop);
if (isReverse)
{
count--;
}
else
{
count++;
}
if (shouldStop)
{
return;
break;
}
END_FOR_IN(enumerator)
GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
}
}
- (void) enumerateObjectsAtIndexes: (NSIndexSet*)indexSet
@ -1677,9 +1692,12 @@ compare(id elem1, id elem2, void* context)
{
/* TODO: Concurrency. */
NSMutableIndexSet *set = [NSMutableIndexSet indexSet];
BOOL shouldStop = NO;
BLOCK_SCOPE BOOL shouldStop = NO;
id<NSFastEnumeration> enumerator = self;
NSUInteger count = 0;
BLOCK_SCOPE NSLock *setLock = nil;
/* If we are enumerating in reverse, use the reverse enumerator for fast
* enumeration. */
@ -1687,20 +1705,44 @@ compare(id elem1, id elem2, void* context)
{
enumerator = [self reverseObjectEnumerator];
}
if (opts & NSEnumerationConcurrent)
{
setLock = [NSLock new];
}
{
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)
{
return;
}
if (predicate(obj, count, &shouldStop))
{
[setLock lock];
[set addIndex: count];
[setLock unlock];
}
});
# else
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];
}
# endif
if (shouldStop)
{
return set;
break;
}
count++;
END_FOR_IN(enumerator)
GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts);
}
[setLock release];
return set;
}
@ -1723,9 +1765,10 @@ compare(id elem1, id elem2, void* context)
{
/* TODO: Concurrency. */
id<NSFastEnumeration> enumerator = self;
BOOL shouldStop = NO;
BLOCK_SCOPE BOOL shouldStop = NO;
NSUInteger count = 0;
BLOCK_SCOPE NSUInteger index = NSNotFound;
BLOCK_SCOPE NSLock *indexLock = nil;
/* If we are enumerating in reverse, use the reverse enumerator for fast
* enumeration. */
if (opts & NSEnumerationReverse)
@ -1733,18 +1776,48 @@ compare(id elem1, id elem2, void* context)
enumerator = [self reverseObjectEnumerator];
}
FOR_IN (id, obj, enumerator)
if (CALL_BLOCK(predicate, obj, count, &shouldStop))
if (opts & NSEnumerationConcurrent)
{
return count;
indexLock = [NSLock new];
}
{
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)
{
return NSNotFound;
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];
}
});
# else
if (CALL_BLOCK(predicate, obj, count, &shouldStop))
{
index = count;
shouldStop = YES;
}
# endif
if (shouldStop)
{
break;
}
count++;
END_FOR_IN(enumerator)
return NSNotFound;
GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts);
}
[indexLock release];
return index;
}
- (NSUInteger) indexOfObjectPassingTest: (GSPredicateBlock)predicate

View file

@ -34,6 +34,7 @@
#import "Foundation/NSAutoreleasePool.h"
#import "Foundation/NSFileManager.h"
#import "Foundation/NSCoder.h"
#import "Foundation/NSLock.h"
#import "Foundation/NSSet.h"
#import "Foundation/NSValue.h"
#import "Foundation/NSKeyValueCoding.h"
@ -43,6 +44,7 @@
#import "GNUstepBase/NSObject+GNUstepBase.h"
#import "GSPrivate.h"
#import "GSFastEnumeration.h"
#import "GSDispatch.h"
static BOOL GSMacOSXCompatiblePropertyLists(void)
{
@ -162,25 +164,26 @@ static SEL appSel;
usingBlock: (GSKeysAndObjectsEnumeratorBlock)aBlock
{
/*
* NOTE: For the moment, we ignore the NSEnumerationOptions because, according
* to the Cocoa documentation, NSEnumerationReverse is undefined for
* NSDictionary and we cannot handle NSEnumerationConcurrent without
* libdispatch.
* NOTE: According to the Cocoa documentation, NSEnumerationReverse is
* undefined for NSDictionary. NSEnumerationConcurrent will be handled through
* the GS_DISPATCH_* macros if libdispatch is available.
*/
id<NSFastEnumeration> enumerator = [self keyEnumerator];
SEL objectForKeySelector = @selector(objectForKey:);
IMP objectForKey = [self methodForSelector: objectForKeySelector];
BOOL shouldStop = NO;
BLOCK_SCOPE BOOL shouldStop = NO;
id obj;
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
FOR_IN(id, key, enumerator)
obj = (*objectForKey)(self, objectForKeySelector, key);
CALL_BLOCK(aBlock, key, obj, &shouldStop);
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup, enumQueue, if (shouldStop){return;};, return;, aBlock, key, obj, &shouldStop);
if (YES == shouldStop)
{
break;
}
END_FOR_IN(enumerator)
GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
}
/**
@ -1008,24 +1011,47 @@ compareIt(id o1, id o2, void* context)
id<NSFastEnumeration> enumerator = [self keyEnumerator];
SEL objectForKeySelector = @selector(objectForKey:);
IMP objectForKey = [self methodForSelector: objectForKeySelector];
BOOL shouldStop = NO;
BLOCK_SCOPE BOOL shouldStop = NO;
NSMutableSet *buildSet = [NSMutableSet new];
SEL addObjectSelector = @selector(addObject:);
IMP addObject = [buildSet methodForSelector: addObjectSelector];
NSSet *resultSet = nil;
id obj;
id obj = nil;
BLOCK_SCOPE NSLock *setLock = nil;
if (opts & NSEnumerationConcurrent)
{
setLock = [NSLock new];
}
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
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 (CALL_BLOCK(aPredicate, key, obj, &shouldStop))
{
addObject(buildSet, addObjectSelector, key);
}
# endif
if (YES == shouldStop)
{
break;
}
END_FOR_IN(enumerator)
GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
[setLock release];
resultSet = [NSSet setWithSet: buildSet];
[buildSet release];
return resultSet;

View file

@ -29,6 +29,7 @@
#import "Foundation/NSData.h"
#import "Foundation/NSIndexSet.h"
#import "Foundation/NSException.h"
#import "GSDispatch.h"
#define GSI_ARRAY_TYPE NSRange
@ -44,6 +45,7 @@
static void sanity(GSIArray array)
{
if (array != 0)
{
NSUInteger c = GSIArrayCount(array);
NSUInteger i;
@ -861,6 +863,125 @@ static NSUInteger posForIndex(GSIArray array, NSUInteger index)
return [c initWithIndexSet: self];
}
- (void)enumerateIndexesInRange: (NSRange)range
options: (NSEnumerationOptions)opts
usingBlock: (GSIndexSetEnumerationBlock)aBlock
{
NSUInteger lastInRange = (NSMaxRange(range) - 1);
NSUInteger startArrayIndex = posForIndex(_array, range.location);
NSUInteger endArrayIndex = MIN(posForIndex(_array, lastInRange), (GSIArrayCount(_array) - 1));
NSUInteger i;
NSUInteger c;
BOOL isReverse = opts & NSEnumerationReverse;
BLOCK_SCOPE BOOL shouldStop = NO;
if ((0 == [self count]) || (NSNotFound == range.location))
{
return;
}
if (NSNotFound == startArrayIndex)
{
startArrayIndex = 0;
}
if (NSNotFound == endArrayIndex)
{
endArrayIndex = GSIArrayCount(_array) - 1;
}
if (isReverse)
{
i = endArrayIndex;
c = startArrayIndex;
}
else
{
i = startArrayIndex;
c = endArrayIndex;
}
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
while (isReverse ? i >= c : i <= c)
{
NSRange r = GSIArrayItemAtIndex(_array, i).ext;
NSUInteger innerI;
NSUInteger innerC;
if (isReverse)
{
innerI = NSMaxRange(r) - 1;
innerC = r.location;
}
else
{
innerI = r.location;
innerC = NSMaxRange(r) - 1;
}
while (isReverse ? innerI >= innerC : innerI <= innerC)
{
if ((innerI <= lastInRange) && (innerI >= range.location))
{
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup, enumQueue, if (shouldStop) {return;}, return;, aBlock, innerI, &shouldStop);
}
if (shouldStop)
{
break;
}
if (isReverse)
{
if (0 == innerI)
{
break;
}
innerI--;
}
else
{
innerI++;
}
}
if (shouldStop)
{
break;
}
if (isReverse)
{
if (0 == i)
{
break;
}
i--;
}
else
{
i++;
}
}
GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
}
- (void)enumerateIndexesWithOptions: (NSEnumerationOptions)opts
usingBlock: (GSIndexSetEnumerationBlock)aBlock
{
NSUInteger firstIndex = [self firstIndex];
NSUInteger lastIndex = [self lastIndex];
if (NSNotFound == firstIndex)
{
return;
}
[self enumerateIndexesInRange: NSMakeRange(firstIndex, ((lastIndex - firstIndex) + 1))
options: opts
usingBlock: aBlock];
}
- (void)enumerateIndexesUsingBlock: (GSIndexSetEnumerationBlock)aBlock
{
[self enumerateIndexesWithOptions: 0
usingBlock: aBlock];
}
@end

View file

@ -39,6 +39,7 @@
#import "GSPrivate.h"
#import "GNUstepBase/NSObject+GNUstepBase.h"
#import "GSFastEnumeration.h"
#import "GSDispatch.h"
@class GSSet;
@interface GSSet : NSObject // Help the compiler
@ -866,18 +867,20 @@ static Class NSMutableSet_concrete_class;
- (void) enumerateObjectsWithOptions: (NSEnumerationOptions)opts
usingBlock: (GSSetEnumeratorBlock)aBlock
{
BOOL shouldStop = NO;
BLOCK_SCOPE BOOL shouldStop = NO;
id<NSFastEnumeration> enumerator = self;
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
FOR_IN (id, obj, enumerator)
{
CALL_BLOCK(aBlock, obj, &shouldStop);
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup,enumQueue, if (shouldStop) {return;}, return;, aBlock, obj, &shouldStop);
if (shouldStop)
{
return;
break;
}
}
END_FOR_IN(enumerator)
GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
}
/** Return a set formed by adding anObject to the receiver.

View file

@ -0,0 +1,58 @@
#import "Testing.h"
#import <Foundation/NSArray.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSIndexSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSEnumerator.h>
static NSUInteger fooCount = 0;
static NSUInteger lastIndex = NSNotFound;
int main()
{
START_SET("NSArray Blocks")
# ifndef __has_feature
# define __has_feature(x) 0
# endif
# if __has_feature(blocks)
NSAutoreleasePool *arp = [NSAutoreleasePool new];
NSArray *array = [NSArray arrayWithObjects: @"foo", @"bar", @"foo", nil];
void(^enumBlock)(id,NSUInteger,BOOL*) = ^(id obj, NSUInteger index, BOOL *stop){
if ([obj isEqual: @"foo"]){ fooCount++;} lastIndex = index;};
[array enumerateObjectsUsingBlock: enumBlock];
PASS((2 == fooCount) && (lastIndex == 2),
"Can forward enumerate array using a block");
fooCount = 0;
lastIndex = NSNotFound;
[array enumerateObjectsWithOptions: NSEnumerationConcurrent
usingBlock: enumBlock];
PASS((2 == fooCount) && (lastIndex == 2),
"Can forward enumerate array concurrently using a block");
fooCount = 0;
lastIndex = NSNotFound;
[array enumerateObjectsWithOptions: NSEnumerationReverse
usingBlock: enumBlock];
PASS((0 == lastIndex), "Can enumerate array in reverse using a block");
fooCount = 0;
lastIndex = NSNotFound;
enumBlock = ^(id obj, NSUInteger index, BOOL *stop){if ([obj isEqual: @"foo"]){
fooCount++;} else if ([obj isEqual: @"bar"]){ *stop=YES;}; lastIndex =
index;};
[array enumerateObjectsUsingBlock: enumBlock];
PASS(((1 == fooCount) && (lastIndex == 1)),
"Block can stop enumeration prematurely.");
NSIndexSet *set = [array indexesOfObjectsPassingTest: ^(id obj, NSUInteger index, BOOL* stop){ if ([obj isEqual: @"foo"]) { return YES;} return NO;}];
PASS(((2 == [set count])
&& (YES == [set containsIndex: 0])
&& (YES == [set containsIndex: 2])
&& (NO == [set containsIndex: 1])),
"Can select object indices based on block predicate.");
[arp release]; arp = nil;
# else
SKIP("No Blocks support in the compiler.")
# endif
END_SET("NSArray Blocks")
return 0;
}

View file

@ -7,6 +7,7 @@
#import <Foundation/NSData.h>
#import <Foundation/NSDate.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSSet.h>
#if defined(GNUSTEP_BASE_LIBRARY)
#import <Foundation/NSSerialization.h>
@ -23,20 +24,26 @@ int main()
# endif
# if __has_feature(blocks)
NSAutoreleasePool *arp = [NSAutoreleasePool new];
__block NSLock *fooLock = [NSLock new];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: @"foo",
@"key1", @"bar", @"key2", @"foo", @"key3", nil];
[dict enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop){
if ([obj isEqual: @"foo"]){ fooCount++;}}];
void(^enumBlock)(id,id,BOOL*) = ^(id key, id obj, BOOL *stop){
if ([obj isEqual: @"foo"]){ [fooLock lock]; fooCount++; [fooLock unlock];}};
[dict enumerateKeysAndObjectsUsingBlock: enumBlock];
PASS((2 == fooCount),
"Can enumerate dictionary using a block");
fooCount = 0;
[dict enumerateKeysAndObjectsWithOptions: NSEnumerationConcurrent
usingBlock: enumBlock];
PASS((2 == fooCount),
"Can enumerate dictionary concurrently using a block");
NSSet *fooKeys = [dict keysOfEntriesPassingTest: ^(id key, id obj, BOOL *stop){
return [obj isEqual: @"foo"];}];
PASS((([fooKeys count] == 2)
&& ([fooKeys containsObject: @"key1"])
&& ([fooKeys containsObject: @"key3"]))
, "Can use blocks as predicates.");
[fooLock release];
[arp release]; arp = nil;
# else
SKIP("No Blocks support in the compiler.")

View file

@ -0,0 +1,88 @@
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSIndexSet.h>
#import <Foundation/NSLock.h>
#include <Testing.h>
int main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableIndexSet *set = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(1,5)];
[set addIndex:20];
[set addIndex:25];
START_SET("NSIndexSet Blocks")
# ifndef __has_feature
# define __has_feature(x) 0
# endif
# if __has_feature(blocks)
NSMutableIndexSet *referenceSet = nil;
__block NSMutableIndexSet *newSet = [NSMutableIndexSet indexSet];
__block BOOL didContainWrongIndex = NO;
__block NSUInteger lastIndex = NSNotFound;
NSLock *setLock = [NSLock new];
void(^enumBlock)(NSUInteger,BOOL*) = ^(NSUInteger idx, BOOL*shouldStop){
[setLock lock];
lastIndex = idx;
[newSet addIndex: idx];
[setLock unlock];
};
// Test forward enumeration:
[set enumerateIndexesUsingBlock: enumBlock];
PASS((YES == [set isEqual: newSet]),
"Can enumerate all indices in an index set using a block");
PASS((25 == lastIndex),
"Forward enumeration stops at the last index");
newSet = [NSMutableIndexSet indexSet];
didContainWrongIndex = NO;
lastIndex = NSNotFound;
// Test reverse enumeration:
[set enumerateIndexesWithOptions: NSEnumerationReverse
usingBlock: enumBlock];
PASS((YES == [set isEqual: newSet]),
"Can reverse enumerate all indices in an index set using a block");
PASS((1 == lastIndex),
"Reverse enumeration stops at the first index");
referenceSet = [NSMutableIndexSet indexSetWithIndexesInRange: (NSMakeRange(4,2))];
[referenceSet addIndex: 20];
newSet = [NSMutableIndexSet indexSet];
didContainWrongIndex = NO;
lastIndex = NSNotFound;
// Test subrange enumeration:
[set enumerateIndexesInRange: NSMakeRange(4,20)
options: 0
usingBlock: enumBlock];
PASS((YES == [referenceSet isEqual: newSet]), "Can enumerate subranges of an index set");
PASS((20 == lastIndex), "Subrange enumeration stops at the correct index");
newSet = [NSMutableIndexSet indexSet];
lastIndex = NSNotFound;
referenceSet = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(1,5)];
[referenceSet addIndex: 20];
enumBlock = ^(NSUInteger idx, BOOL*shouldStop){
[setLock lock];
[newSet addIndex: idx];
[setLock unlock];
if (20 == idx)
{
*shouldStop = YES;
}
};
// Test premature termination of enumeration:
[set enumerateIndexesUsingBlock: enumBlock];
PASS((YES == [referenceSet isEqual: newSet]), "block can prematurely terminate enumeration");
[setLock release];
# else
SKIP("No Blocks support in the compiler.")
# endif
END_SET("NSIndexSet Blocks")
[pool release];
return 0;
}

View file

@ -40,6 +40,7 @@ GNUSTEP_BASE_HAVE_GNUTLS=@HAVE_GNUTLS@
GNUSTEP_BASE_HAVE_MDNS=@HAVE_MDNS@
GNUSTEP_BASE_HAVE_AVAHI=@HAVE_AVAHI@
GNUSTEP_BASE_HAVE_ICU=@HAVE_ICU@
GNUSTEP_BASE_HAVE_LIBDISPATCH=@HAVE_LIBDISPATCH@
# Futureproofing ... if we ever use non-ascii string constants in base,
# we need to make sure that anyone building base uses the expected input

26173
configure vendored

File diff suppressed because it is too large Load diff

View file

@ -2996,6 +2996,47 @@ if test $enable_icu = yes; then
fi
AC_SUBST(HAVE_ICU)
#--------------------------------------------------------------------
# Check for libdispatch
# See DEPENDENCIES POLICY at the start of this file.
#--------------------------------------------------------------------
HAVE_LIBDISPATCH=0
AC_ARG_ENABLE(libdispatch,
[ --disable-libdispatch Disable dispatching blocks via libdispatch],
enable_libdispatch=no,
enable_libdispatch=yes)
if test $enable_libdispatch = yes; then
AC_CHECK_HEADERS(dispatch.h, have_dispatch=yes, have_dispatch=no)
if test "$have_dispatch" = "no"; then
AC_CHECK_HEADERS(dispatch/dispatch.h, have_dispatch=yes, have_dispatch=no)
fi
if test "$have_dispatch" = "yes"; then
AC_CHECK_LIB(dispatch, dispatch_queue_create, have_dispatch=yes, have_dispatch=no)
if test "$have_dispatch" = "yes"; then
saveLIBS="$LIBS"
LIBS="-lobjc -ldispatch";
# This check is needed because libdispatch might be linked against a
# version of libBlocksRuntime that defines symbols conflicting with libobjc
AC_MSG_CHECKING(whether we can link libdispatch and libobjc at the same time)
AC_TRY_LINK(,,have_dispatch=yes, have_dispatch=no)
if test "$have_dispatch" = "yes"; then
LIBS="$saveLIBS -ldispatch";
AC_MSG_RESULT(yes);
HAVE_LIBDISPATCH=1;
else
LIBS="$saveLIBS";
AC_MSG_RESULT(no);
fi
fi
else
HAVE_LIBDISPATCH=0;
# just ignore libdispatch if it's not there
fi
fi
AC_SUBST(HAVE_LIBDISPATCH)
#--------------------------------------------------------------------
# Check GMP for NSDecimal
#--------------------------------------------------------------------