Hi Quinn, really appreciate all your answers I've been reading on here. I'm trying to transfer an IOSurface over an NSXPCConnection on 10.14 right now, based on your comments in this thread:
> However, that specific problem got resolved on 10.12 where we introduced a new Objective-C
IOSurface
object, and that object is transportable directly over
NSXPCConnection
. So double yay!
but it doesn't work. I have a very simple service protocol that includes
(void)sendFrame:(IOSurfaceRef)frame;
I have a valid (non-NULL) IOSurface in my app that I send via my NSXPCConnection proxy object, and when the call is executed in the helper, frame is always NULL. On the other hand, I've also tried creating an IOSurface with the (deprecated) kIOSurfaceIsGlobal property and send the IOSurface's by ID instead:
[_service sendFrameID:IOSurfaceGetID(surface)];
and on the helper app side, looking up the ID to get an IOSurfaceRef:
IOSurfaceRef frame = IOSurfaceLookup(frameID);
and it works correctly (I get a valid IOSurface which I can display and see the same pixel contents in both the app and the helper).
So can you elaborate on what you mean by the new IOSurface object in 10.12 is "transportable directly" over NSXPCConnection? How can I make this work without resorting to kIOSurfaceIsGlobal? I'm specifically interested in no-copy transfer.
Thanks!
Post
Replies
Boosts
Views
Activity
Some additional info: when I try the same code on a 10.15 mac, sendFrame results in an uncaught exception "encodeDataAt:ofObjCType: unencodable type (^{__IOSurface=})". sendFrameID still works.
Fresh eyes this morning and I was able to debug this a bit more: in my Xcode scheme options, I set Console to Use Terminal, which let me then see the full command line invocation Xcode uses to launch the app, which I was able to copy-paste to my own terminal and confirm the problem persists, so it's because of how Xcode is running my app. Process of elimination found the culprit. Xcode sets the env var (among others)
DYLD_LIBRARY_PATH=$SYMROOT:$OBJROOT:/usr/lib/system/introspection
My plugin feature.dylib gets built into $OBJROOT and then copied into my app's expected plugin folder $PLUGINROOT along with its library dependency libexternal.so. When I call dlopen to load the plugin, I use the absolute path: dlopen("$PLUGINROOT/feature.dylib", 0). I use DYLD_PRINT_LIBRARIES=1 to see what dlopen actually loads. When I run MyApp.app without setting DYLD_LIBRARY_PATH, I see that dlopen opens $PLUGINROOT/feature.dylib as I expected. However, when I run MyApp.app with the DYLD_LIBRARY_PATH used by Xcode, I can see that dlopen opens $OBJROOT/feature.dylib. So that explains why running from within Xcode causes the plugin load to fail, because libexternal.so does not exist in $OBJROOT.
This seems like a bug! Right? I'm providing an absolute path to dlopen and it's using a different path based on the env var!
Wow, man dlopen says this isn't a bug!
When path contains a slash but is not a framework path (i.e. a full path or a partial path to a dylib), dlopen() searches the following until it finds a compatible Mach-O file: $DYLD_LIBRARY_PATH (with leaf name from path ), then the supplied path (using current working directory for relative paths), then $DYLD_FALLBACK_LIBRARY_PATH (with leaf name from path ).
This seems crazy to me, but at least now I can figure out how to solve the problem.