From 6b67c7261a5dff0722772ce345e0c5cc3b8454fb Mon Sep 17 00:00:00 2001 From: Frederik Seiffert Date: Fri, 23 Oct 2026 08:58:36 +0200 Subject: [PATCH] Bridge CFRunLoop to NSRunLoop mode methods CFRunLoopAddCommonMode(), CFRunLoopCopyAllModes() and CFRunLoopCopyCurrentMode() now dispatch to the corresponding NSRunLoop methods when passed an Objective-C object. A new NSCFRunLoop bridge class implements those methods for CFRunLoopRefs, so the functions of the same names in GNUstep Base work on them too. Also fixes the count of classes required before bridging is initialized, which was not updated when NSCFDate was added. --- ChangeLog | 13 ++++++ Source/CFRunLoop.c | 45 ++++++++++++++++---- Source/GNUmakefile.in | 1 + Source/NSCFRunLoop.m | 88 ++++++++++++++++++++++++++++++++++++++++ Source/NSCFType.m | 3 +- Tests/CFRunLoop/bridge.m | 56 +++++++++++++++++++++++++ 6 files changed, 197 insertions(+), 9 deletions(-) create mode 100644 Source/NSCFRunLoop.m create mode 100644 Tests/CFRunLoop/bridge.m diff --git a/ChangeLog b/ChangeLog index e11a158c..c065bd3c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2026-10-23 Frederik Seiffert + * Source/CFRunLoop.c (CFRunLoopAddCommonMode, CFRunLoopCopyAllModes, + CFRunLoopCopyCurrentMode): Dispatch to the corresponding NSRunLoop + methods when passed an Objective-C object. Move the implementations + into private functions for use by the bridge. + * Source/NSCFRunLoop.m: New bridge class for CFRunLoop implementing + -addCommonMode:, -allModes and -currentMode, so the functions of the + same names in GNUstep Base also work on CFRunLoopRefs. + * Source/NSCFType.m: Bridge CFRunLoop. Fix count of classes required + before bridging is initialized. + * Source/GNUmakefile.in: Add NSCFRunLoop.m. + * Tests/CFRunLoop/bridge.m: New test. + 2026-07-11 Todd White * Source/CFPropertyList.c (CFPropertyListCreateDeepCopy): Return NULL for an invalid property list. diff --git a/Source/CFRunLoop.c b/Source/CFRunLoop.c index 88a4ad23..3aae1fdb 100644 --- a/Source/CFRunLoop.c +++ b/Source/CFRunLoop.c @@ -969,8 +969,13 @@ CFRunLoopIsWaiting (CFRunLoopRef rl) return rl->_isWaiting; } -void -CFRunLoopAddCommonMode (CFRunLoopRef rl, CFStringRef mode) +/* The ObjC bridge (NSCFRunLoop) calls these private implementations rather + * than the public functions, because GNUstep Base exports functions of the + * same names which send the corresponding messages to the run loop, and + * calling those would recurse. + */ +GS_PRIVATE void +_CFRunLoopAddCommonMode (CFRunLoopRef rl, CFStringRef mode) { GSMutexLock (&rl->_lock); @@ -985,10 +990,18 @@ CFRunLoopAddCommonMode (CFRunLoopRef rl, CFStringRef mode) GSRunLoopContextAddFunc, new); CFDictionaryAddValue (rl->_contexts, mode, new); } - + GSMutexUnlock (&rl->_lock); } +void +CFRunLoopAddCommonMode (CFRunLoopRef rl, CFStringRef mode) +{ + CF_OBJC_FUNCDISPATCHV(_kCFRunLoopTypeID, void, rl, + "addCommonMode:", mode); + _CFRunLoopAddCommonMode (rl, mode); +} + static void CFRunLoopCopyKeysFunc (const void *key, const void *value, void *context) { @@ -996,8 +1009,8 @@ CFRunLoopCopyKeysFunc (const void *key, const void *value, void *context) CFArrayAppendValue (array, key); } -CFArrayRef -CFRunLoopCopyAllModes (CFRunLoopRef rl) +GS_PRIVATE CFArrayRef +_CFRunLoopCopyAllModes (CFRunLoopRef rl) { CFArrayRef ret; CFMutableArrayRef a; @@ -1013,12 +1026,20 @@ CFRunLoopCopyAllModes (CFRunLoopRef rl) ret = CFArrayCreateCopy (NULL, a); CFRelease (a); - + return ret; } -CFStringRef -CFRunLoopCopyCurrentMode (CFRunLoopRef rl) +CFArrayRef +CFRunLoopCopyAllModes (CFRunLoopRef rl) +{ + CF_OBJC_FUNCDISPATCHV_RETAINED(_kCFRunLoopTypeID, CFArrayRef, rl, + "allModes"); + return _CFRunLoopCopyAllModes (rl); +} + +GS_PRIVATE CFStringRef +_CFRunLoopCopyCurrentMode (CFRunLoopRef rl) { CFStringRef mode = rl->_currentMode; @@ -1028,6 +1049,14 @@ CFRunLoopCopyCurrentMode (CFRunLoopRef rl) return NULL; } +CFStringRef +CFRunLoopCopyCurrentMode (CFRunLoopRef rl) +{ + CF_OBJC_FUNCDISPATCHV_RETAINED(_kCFRunLoopTypeID, CFStringRef, rl, + "currentMode"); + return _CFRunLoopCopyCurrentMode (rl); +} + static void CFRunLoopPerformBlock_nolock (CFRunLoopRef rl, CFTypeRef mode, PerformBlockType block) diff --git a/Source/GNUmakefile.in b/Source/GNUmakefile.in index e3a787af..c6077da4 100644 --- a/Source/GNUmakefile.in +++ b/Source/GNUmakefile.in @@ -104,6 +104,7 @@ libgnustep-corebase_OBJC_FILES = \ NSCFInputStream.m \ NSCFOutputStream.m \ NSCFDate.m \ + NSCFRunLoop.m \ NSCFType.m libgnustep-corebase_LIBRARIES_DEPEND_UPON += @LIBS@ $(FND_LIBS) diff --git a/Source/NSCFRunLoop.m b/Source/NSCFRunLoop.m new file mode 100644 index 00000000..e307859b --- /dev/null +++ b/Source/NSCFRunLoop.m @@ -0,0 +1,88 @@ +/* NSCFRunLoop.m + + Copyright (C) 2026 Free Software Foundation, Inc. + + Written by: Frederik Seiffert + Date: October, 2026 + + This file is part of GNUstep CoreBase 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.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#import +#import +#import + +#include "NSCFType.h" +#include "GSPrivate.h" +#include "CoreFoundation/CFRunLoop.h" + +GS_PRIVATE void +_CFRunLoopAddCommonMode (CFRunLoopRef rl, CFStringRef mode); +GS_PRIVATE CFArrayRef +_CFRunLoopCopyAllModes (CFRunLoopRef rl); +GS_PRIVATE CFStringRef +_CFRunLoopCopyCurrentMode (CFRunLoopRef rl); + +/* Implements the NSRunLoop methods used by the CFRunLoop mode functions + * in GNUstep Base, so that those functions also work on CFRunLoopRefs. + */ +@interface NSCFRunLoop : NSObject +NSCFTYPE_VARS +@end + +@implementation NSCFRunLoop ++ (void) load +{ + NSCFInitialize (); +} + ++ (void) initialize +{ + GSObjCAddClassBehavior (self, [NSCFType class]); +} + +- (void) addCommonMode: (NSString*)mode +{ + _CFRunLoopAddCommonMode ((CFRunLoopRef)self, (CFStringRef)mode); +} + +/* These return Base objects rather than autoreleasing the CF objects, as + * autoreleasing bridged CF objects crashes with the gnustep-1.9 runtime. + */ +- (NSArray*) allModes +{ + CFArrayRef modes = _CFRunLoopCopyAllModes ((CFRunLoopRef)self); + NSArray *result = [NSArray arrayWithArray: (NSArray*)modes]; + + CFRelease (modes); + return result; +} + +- (NSString*) currentMode +{ + CFStringRef mode = _CFRunLoopCopyCurrentMode ((CFRunLoopRef)self); + NSString *result; + + if (mode == NULL) + return nil; + result = [NSString stringWithString: (NSString*)mode]; + CFRelease (mode); + return result; +} +@end diff --git a/Source/NSCFType.m b/Source/NSCFType.m index 387590da..741de8c2 100644 --- a/Source/NSCFType.m +++ b/Source/NSCFType.m @@ -40,7 +40,7 @@ void NSCFInitialize (void) { - static int requiredClasses = 12; + static int requiredClasses = 14; --requiredClasses; if (requiredClasses == 0) @@ -68,6 +68,7 @@ void NSCFInitialize (void) CFRuntimeBridgeClass (CFReadStreamGetTypeID(), "NSCFInputStream"); CFRuntimeBridgeClass (CFWriteStreamGetTypeID(), "NSCFOutputStream"); CFRuntimeBridgeClass (CFDateGetTypeID(), "NSCFDate"); + CFRuntimeBridgeClass (CFRunLoopGetTypeID(), "NSCFRunLoop"); GSRuntimeInitializeConstants (); } diff --git a/Tests/CFRunLoop/bridge.m b/Tests/CFRunLoop/bridge.m new file mode 100644 index 00000000..5025bef4 --- /dev/null +++ b/Tests/CFRunLoop/bridge.m @@ -0,0 +1,56 @@ +#import +#import +#include "CoreFoundation/CFArray.h" +#include "CoreFoundation/CFRunLoop.h" +#include "../CFTesting.h" + +void testNSonCF(void); +void testCFonNS(void); + +static Boolean +containsMode (CFArrayRef modes, CFStringRef mode) +{ + return CFArrayContainsValue (modes, CFRangeMake (0, CFArrayGetCount (modes)), + mode); +} + +int main(void) +{ + testNSonCF(); + testCFonNS(); + return 0; +} + +void testNSonCF(void) +{ + CFRunLoopRef cfrl = CFRunLoopGetCurrent(); + NSRunLoop *nsrl = (NSRunLoop*) cfrl; + + testHopeful = true; + PASS_CF(cfrl != NULL, "CFRunLoopGetCurrent() returns a run loop"); + testHopeful = false; + if (cfrl == NULL) + return; + + [nsrl addCommonMode: (NSString*) CFSTR("ns_on_cf")]; + PASS_CF(containsMode((CFArrayRef) [nsrl allModes], CFSTR("ns_on_cf")), + "-addCommonMode: and -allModes work on a CFRunLoop"); + PASS_CF([nsrl currentMode] == nil, + "-currentMode works on a CFRunLoop"); +} + +void testCFonNS(void) +{ + NSRunLoop *nsrl = [NSRunLoop currentRunLoop]; + CFRunLoopRef cfrl = [nsrl getCFRunLoop]; + CFArrayRef modes; + + CFRunLoopAddCommonMode(cfrl, CFSTR("cf_on_ns")); + modes = CFRunLoopCopyAllModes(cfrl); + PASS_CF(containsMode(modes, CFSTR("cf_on_ns")), + "CFRunLoopAddCommonMode() and CFRunLoopCopyAllModes() work on an NSRunLoop"); + CFRelease(modes); + + PASS_CF(CFRunLoopCopyCurrentMode(cfrl) == NULL, + "CFRunLoopCopyCurrentMode() works on an NSRunLoop"); +}