google / eDistantObject

eDistantObject (eDO) - Remote invocation library for iOS
Apache License 2.0
176 stars 36 forks source link

Blocks cannot be serialized properly to remote objects due to load order bugs #157

Open LeoNatan opened 5 years ago

LeoNatan commented 5 years ago

As discussed in #156, it seems that blocks cannot be properly serialized to remote objects, when attempting a remote method call in a +load method, due to load order woes.

The code linked in the other issue shows that __attribute__((constructor)) is used, which is ordered later in the start chaing, after +load methods.

Moving the code to +load is also not the best solution, as +load are also subject to call order issues, as I recently discovered. I've settled on nasty hacks in my case. But those hacks aren't suitable here, as the code is in a class category.

The result is that when a block is called from the remote process, it is not serialized correctly on the client side (because the above-linked code has not executed yet), and an exception is raised +[NSInvocation _invocationWithMethodSignature:frame:]: method signature argument cannot be nil in:

    frame #0: 0x00007fff503b5af0 libobjc.A.dylib`objc_exception_throw
    frame #1: 0x00007fff23b9bc83 CoreFoundation`+[NSInvocation _invocationWithMethodSignature:frame:] + 355
  * frame #2: 0x0000000103e6efb8 DetoxHelper`__38+[EDOInvocationRequest requestHandler]_block_invoke(.block_descriptor=0x0000600003a7cd20, originalRequest=0x0000600002174540, service=0x0000600001f78540) at EDOInvocationMessage.m:313:34
    frame #3: 0x0000000103e6bb1e DetoxHelper`-[EDOExecutor edo_handleMessage:](self=0x0000600003a7ce10, _cmd="edo_handleMessage:", message=0x0000600003a68060) at EDOExecutor.m:143:16
    frame #4: 0x0000000103e6ac2f DetoxHelper`-[EDOExecutor runWithBlock:](self=0x0000600003a7ce10, _cmd="runWithBlock:", executeBlock=0x0000000103e7c660) at EDOExecutor.m:88:5
    frame #5: 0x0000000103e7bca9 DetoxHelper`+[EDOClientService sendSynchronousRequest:onPort:withExecutor:](self=EDOClientService, _cmd="sendSynchronousRequest:onPort:withExecutor:", request=0x000060000216c400, port=0x0000600003478180, executor=0x0000600003a7ce10) at EDOClientService.m:273:9
    frame #6: 0x0000000103e66bfb DetoxHelper`-[EDOObject(self=0x0000600002178700, _cmd="edo_forwardInvocation:selector:returnByValue:", invocation=0x0000600002172d80, selector="waitForIdleWithCompletionHandler:", returnByValue=NO) edo_forwardInvocation:selector:returnByValue:] at EDOObject+Invocation.m:89:32
    frame #7: 0x0000000103e669f9 DetoxHelper`-[EDOObject(self=0x0000600002178700, _cmd="forwardInvocation:", invocation=0x0000600002172d80) forwardInvocation:] at EDOObject+Invocation.m:66:3
    frame #8: 0x00007fff23b9d566 CoreFoundation`___forwarding___ + 838
    frame #9: 0x00007fff23b9f6c8 CoreFoundation`__forwarding_prep_0___ + 120
    frame #10: 0x0000000103e65300 DetoxHelper`__23-[DetoxManager connect]_block_invoke(.block_descriptor=0x0000000103e9b4e0) at DetoxManager.m:58:3
    frame #11: 0x00007fff511fc7f9 libdispatch.dylib`_dispatch_client_callout + 8
    frame #12: 0x00007fff511fda25 libdispatch.dylib`_dispatch_once_callout + 20
    frame #13: 0x0000000103e65184 DetoxHelper`-[DetoxManager connect] [inlined] _dispatch_once(predicate=0x0000000103ea7830, block=0x0000000103e651b0) at once.h:84:3
    frame #14: 0x0000000103e65169 DetoxHelper`-[DetoxManager connect](self=0x000060000347c780, _cmd="connect") at DetoxManager.m:54
    frame #15: 0x0000000103e65036 DetoxHelper`+[DetoxManager load](self=DetoxManager, _cmd="load") at DetoxManager.m:36:3
    frame #16: 0x00007fff503bcdf0 libobjc.A.dylib`load_images + 1226
    frame #17: 0x0000000103dc2d79 dyld_sim`dyld::notifySingle(dyld_image_states, ImageLoader const*, ImageLoader::InitializerTimingList*) + 418
    frame #18: 0x0000000103dcf970 dyld_sim`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 438
    frame #19: 0x0000000103dce786 dyld_sim`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 188
    frame #20: 0x0000000103dce826 dyld_sim`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 82
    frame #21: 0x0000000103dc3046 dyld_sim`dyld::initializeMainExecutable() + 129
    frame #22: 0x0000000103dc70fc dyld_sim`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 3831
    frame #23: 0x0000000103dc21cd dyld_sim`start_sim + 122
    frame #24: 0x00000001094eb6b7 dyld`dyld::useSimulatorDyld(int, macho_header const*, char const*, int, char const**, char const**, char const**, unsigned long*, unsigned long*) + 2308
    frame #25: 0x00000001094e9375 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 818
    frame #26: 0x00000001094e4227 dyld`dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*) + 453
    frame #27: 0x00000001094e4025 dyld`_dyld_start + 37
wuhao5 commented 5 years ago

We could actually implement those methods in NSBlock category, and the reason why we use the runtime API to add them dynamically is because we want to be cautious if there is a conflict and fail early.

One quick fix (probably the best fix) is, instead having a constructor, move the code into here using dispatch_once.

Do you want to try it out?

LeoNatan commented 5 years ago

Unfortunately, I have opted to implement an IPC solution of my own for our project with the needs of asynchronicity. I may be able to test in the future, but not right now.

wuhao5 commented 5 years ago

Kk, I'll send a quick fix in next few days then.

bartekpacia commented 1 year ago

Kk, I'll send a quick fix in next few days then.

Ummm... hi! I'd love to get that fix!