Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
2026-10-23 Frederik Seiffert <frederik@algoriddim.com>
* 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 <todd.white@thalion.global>
* Source/CFPropertyList.c (CFPropertyListCreateDeepCopy): Return
NULL for an invalid property list.
Expand Down
45 changes: 37 additions & 8 deletions Source/CFRunLoop.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -985,19 +990,27 @@ 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)
{
CFMutableArrayRef array = (CFMutableArrayRef)context;
CFArrayAppendValue (array, key);
}

CFArrayRef
CFRunLoopCopyAllModes (CFRunLoopRef rl)
GS_PRIVATE CFArrayRef
_CFRunLoopCopyAllModes (CFRunLoopRef rl)
{
CFArrayRef ret;
CFMutableArrayRef a;
Expand All @@ -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;

Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions Source/GNUmakefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
88 changes: 88 additions & 0 deletions Source/NSCFRunLoop.m
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/> or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/

#import <Foundation/NSObject.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>

#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
3 changes: 2 additions & 1 deletion Source/NSCFType.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

void NSCFInitialize (void)
{
static int requiredClasses = 12;
static int requiredClasses = 14;
--requiredClasses;

if (requiredClasses == 0)
Expand Down Expand Up @@ -68,6 +68,7 @@ void NSCFInitialize (void)
CFRuntimeBridgeClass (CFReadStreamGetTypeID(), "NSCFInputStream");
CFRuntimeBridgeClass (CFWriteStreamGetTypeID(), "NSCFOutputStream");
CFRuntimeBridgeClass (CFDateGetTypeID(), "NSCFDate");
CFRuntimeBridgeClass (CFRunLoopGetTypeID(), "NSCFRunLoop");

GSRuntimeInitializeConstants ();
}
Expand Down
56 changes: 56 additions & 0 deletions Tests/CFRunLoop/bridge.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSRunLoop.h>
#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");
}
Loading