facebook / folly

An open-source C++ library developed and used at Facebook.
https://groups.google.com/forum/?fromgroups#!forum/facebook-folly
Apache License 2.0
28.34k stars 5.56k forks source link

Time.cpp's clock_gettime fallback breaks libc++ steady_clock::now #1602

Open tux3 opened 3 years ago

tux3 commented 3 years ago

Hello,

We maintain a Native Module for React-Native that supports Apple platforms, and so we (and our users) have a dependency on Folly through the RN ecosystem.

Our module statically links with libc++, and some of our own C++ dependencies use std::steady_clock, which in recent versions of libc++ calls clock_gettime(CLOCK_MONOTONIC_RAW, &tp) on Apple platforms. Our users have been reporting that std::steady_clock::now() throws after calling clock_gettime, resulting in an uncaught exception and a crash:

* thread #23, stop reason = signal SIGABRT
  * frame #0: 0x00007fff6113133a libsystem_kernel.dylib`__pthread_kill + 10
    frame #1: 0x00007fff61166e60 libsystem_pthread.dylib`pthread_kill + 430
    frame #2: 0x00007fff200fab94 libsystem_c.dylib`abort + 120
    frame #3: 0x000000010da81ff3 ExampleApp`abort_message + 195
    frame #4: 0x000000010d878fae ExampleApp`demangling_terminate_handler() + 238
    frame #5: 0x000000010d92f0f8 ExampleApp`std::__terminate(void (*)()) + 8
    frame #6: 0x000000010d92f079 ExampleApp`std::terminate() + 41
    frame #7: 0x000000010d85dfbb ExampleApp`__clang_call_terminate + 11
    frame #8: 0x000000010e04bdf9 ExampleApp`std::__1::chrono::steady_clock::now() + 73

Why do things break

It turns out that Folly's fallback implementation exports its clock_gettime implementation globally, as a weak symbol! So when our native module is statically linked into the user's application, Folly's clock_gettime symbol will bind to libc++'s clock_gettime import, even on devices >= iOS 10 where the symbol would otherwise be loaded from libsystem_c.dylib at runtime.

That would be fine, except Folly's clock_gettime does not support CLOCK_MONOTONIC_RAW. It returns EINVAL, which causes steady_clock to throw, and our users get to keep both pieces. This is suboptimal :(

The macro in Time.h

The React-Native build system can be fairly complex, and as a native module we don't have control on all the other dependencies our users may have and on how Folly is compiled by those deps. However it is our understanding that the clock_gettime fallback can end up getting pulled in either when a dep supports iOS platforms < 10 (which triggers the macro in Time.h). (This can also happen if a dep is still using an older version of Folly where the macro was accidentally always true in some cases, but presumably these deps will update eventually).

Note that this is a different issue from https://github.com/facebook/folly/issues/1470, which results in breakage when that macro is false. Here things break when the macro is true.

Expected behavior

This incompatibility is caused by a combination of two things:

We are currently working around the bug by using a patched version of libc++ that unconditionally calls mach_absolute_time instead of clock_gettime, even when our target platforms have this support, however considering the above I think the bug can be fixed in one of three ways:

Any one of these would fix this issue, though I think we would be happy with any solution that resolves the incompatibility between React-Native libraries that use Folly and libc++.
Thank you!

rcari commented 2 years ago

I proposed #1725 to get rid of this issue along with some others @yfeldblum