Page 1 of 1

how to access kCFRunLoopDefaultMode

Posted: Mon May 11, 2015 4:01 pm
by mariosk8s
I'm trying to do some fsevents work on the mac, and it's not working with me.
As soon as a file system event arrives, it takes a dump.

Below is stripped down version of my code trying to implement this

Code: Select all

EnableExplicit
ImportC "-framework CoreServices"
  CFStringCreateWithCharacters.i (*alloc, *bytes, numChars.i)
  CFArrayCreate.i(*allocator, *values, numValues.i, *callBacks)
  FSEventStreamCreate(*allocator, *callback, *context, *pathsToWatch, sinceWhen.q, latency.d, flags.l)
  FSEventStreamScheduleWithRunLoop(*streamRef, *runLoop, *runLoopMode)
  FSEventStreamStart(*streamRef)
  CFRunLoopRun()
  CFRunLoopGetCurrent.i()
  FSEventStreamInvalidate(*streamRef)
  FSEventStreamRelease(*streamRef)
EndImport

#kCFStringEncodingMacRoman = 0
#kFSEventStreamCreateFlagNone  = $00000000
#kFSEventStreamEventIdSinceNow = $FFFFFFFFFFFFFFFF

Global kCFRunLoopDefaultModeStr.s = "kCFRunLoopDefaultMode"
Global kCFRunLoopDefaultMode.i = CFStringCreateWithCharacters(#Null, 
               @kCFRunLoopDefaultModeStr, Len(kCFRunLoopDefaultModeStr))
 
Procedure callback(*stream, *ctx, numEvents.i, *eventPaths, *eventFlags_l, *eventIds_q)
  PrintN("callback")
EndProcedure

Procedure main(filePath.s)
  PrintN("Watching " + filePath)
  Protected *strRef = CFStringCreateWithCharacters(#Null, @filePath, 
                                                   Len(filePath))
  Protected *pathsToWatch = CFArrayCreate(#Null, @*strRef, 1, #Null)
  Protected latency.d = 1.0 ; Latency in seconds
  Protected *stream = FSEventStreamCreate(#Null, @callback(), #Null, *pathsToWatch,
                                #kFSEventStreamEventIdSinceNow, latency, 
                                #kFSEventStreamCreateFlagNone)
  FSEventStreamScheduleWithRunLoop(*stream, CFRunLoopGetCurrent(), 
                                   kCFRunLoopDefaultMode)
  FSEventStreamStart(*stream)
  PrintN("entering CFRunLoop");
  CFRunLoopRun()
  PrintN("exited CFRunLoop");

  FSEventStreamInvalidate (*stream)
  FSEventStreamRelease (*stream)
EndProcedure

main("/")
Assuming this code is in the notorious foo.pb, i run the following

Code: Select all

$ pbcompiler -ds -c -u -t -e foo foo.pb && lldb foo

******************************************
PureBasic 5.24 LTS (MacOS X - x86)
******************************************

Loading external modules...
Starting compilation...
47 lines processed.
Creating the executable.

- Feel the ..PuRe.. Power -

Current executable set to 'foo' (i386).
(lldb) r
Process 65891 launched: '/Users/regify/src/apps/regibox/foo' (i386)
Watching /
entering CFRunLoop
callback
Process 65891 stopped
* thread #1: tid = 0x104cea, 0x00000001, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x1)
    frame #0: 0x00000001
error: memory read failed for 0x0
(lldb) bt all
* thread #1: tid = 0x104cea, 0x00000001, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x1)
  * frame #0: 0x00000001

  thread #2: tid = 0x104d00, 0x90928992 libsystem_kernel.dylib`kevent64 + 10, queue = 'com.apple.libdispatch-manager'
    frame #0: 0x90928992 libsystem_kernel.dylib`kevent64 + 10
    frame #1: 0x984d9899 libdispatch.dylib`_dispatch_mgr_invoke + 238
    frame #2: 0x984d9532 libdispatch.dylib`_dispatch_mgr_thread + 52
In http://www.opensource.apple.com/source/ ... nLoop.h:68 is the following declaration

Code: Select all

CF_EXPORT const CFStringRef kCFRunLoopDefaultMode;
I have a working C sample for this, but it's using the original kCFRunLoopDefaultMode instead of my homegrown one.
Is there a way to access the original one?

When i did a home grown kCFRunLoopDefaultMode in the C sample, it failed to get events, but didn't crash.

Any other things, that stick out as sinfully wrong here?
I've been banging my head against this for 2 days now :cry:

Re: how to access kCFRunLoopDefaultMode

Posted: Mon May 11, 2015 5:01 pm
by wilbert
Does this work ?

Code: Select all

EnableExplicit
OpenConsole()

ImportC ""
  CFSTR(cStr.p-ascii) As "___CFStringMakeConstantString"
  FSEventStreamCreate(*allocator, *callback, *context, *pathsToWatch, sinceWhen.q, latency.d, flags.l)
  FSEventStreamScheduleWithRunLoop(*streamRef, *runLoop, *runLoopMode)
  FSEventStreamStart(*streamRef)
  FSEventStreamStop(*streamRef)
  CFRunLoopRun()
  CFRunLoopGetCurrent.i()
  FSEventStreamInvalidate(*streamRef)
  FSEventStreamRelease(*streamRef)
EndImport

#kFSEventStreamCreateFlagNone  = $00000000
#kFSEventStreamEventIdSinceNow = $FFFFFFFFFFFFFFFF

Global kCFRunLoopDefaultMode.i = CFSTR("kCFRunLoopDefaultMode")
 
ProcedureC callback(*stream, *ctx, numEvents.i, *eventPaths, *eventFlags_l, *eventIds_q)
  PrintN("callback")
EndProcedure

Procedure main(filePath.s)
  PrintN("Watching " + filePath)
  Protected *pathsToWatch = CocoaMessage(0, 0, "NSArray arrayWithObject:$", @filePath)
  Protected latency.d = 1.0 ; Latency in seconds
  Protected *stream = FSEventStreamCreate(#Null, @callback(), #Null, *pathsToWatch,
                                #kFSEventStreamEventIdSinceNow, latency, 
                                #kFSEventStreamCreateFlagNone)
  FSEventStreamScheduleWithRunLoop(*stream, CFRunLoopGetCurrent(), 
                                   kCFRunLoopDefaultMode)
  FSEventStreamStart(*stream)
  PrintN("entering CFRunLoop");
  CFRunLoopRun()
  PrintN("exited CFRunLoop");

  FSEventStreamStop(*stream)
  FSEventStreamInvalidate (*stream)
  FSEventStreamRelease (*stream)
EndProcedure

main("/")

Re: how to access kCFRunLoopDefaultMode

Posted: Tue May 12, 2015 8:28 am
by mariosk8s
Awesome @wilbert, you're a life saver.
Thank you :D
One last question. Should i

Code: Select all

CFRelease(*pathsToWatch)
and if so can i do this right after FSEventStreamCreate, or does it need to hang around for the life time of the stream?

Re: how to access kCFRunLoopDefaultMode

Posted: Tue May 12, 2015 8:54 am
by wilbert
mariosk8s wrote:One last question. Should i

Code: Select all

CFRelease(*pathsToWatch)
and if so can i do this right after FSEventStreamCreate, or does it need to hang around for the life time of the stream?
It's a NSArray instead of a CFArray. Some type of objects can be 'Toll-Free Bridged' meaning you can pass a NSArray when a CFArray is expected and the other way around. This is also the case for NSString and CFString.
Since it's a NSArray you don't use CFRelease.
In its current form it is an autorelease object. In a gui application those are automatically released when the autorelease pool PureBasic creates is drained.
If you want to handle things manually, change the line that creates the array to

Code: Select all

Protected *pathsToWatch = CocoaMessage(0, CocoaMessage(0, 0, "NSArray alloc"), "initWithObject:$", @filePath)
and release it like this

Code: Select all

CocoaMessage(0, *pathsToWatch, "release")
I seems to be working to do it right after FSEventStreamCreate but if you want to be sure you can also do it after the stream is released.

Re: how to access kCFRunLoopDefaultMode

Posted: Tue May 12, 2015 9:02 am
by mariosk8s
Ah, so if i read your post right, then it's best to just leave it alone, as there will only be one path in there for me. So resource consumption is not an issue.

Re: how to access kCFRunLoopDefaultMode

Posted: Tue May 12, 2015 10:05 am
by wilbert
mariosk8s wrote:Ah, so if i read your post right, then it's best to just leave it alone, as there will only be one path in there for me. So resource consumption is not an issue.
In that case you can indeed leave it as it is. :)

Re: how to access kCFRunLoopDefaultMode

Posted: Tue May 12, 2015 12:09 pm
by mariosk8s
Thanks for your help, wilbert.