1. 25 Nov, 2015 8 commits
    • Giuseppe Ottaviano's avatar
      Update symlinks · 422c78be
      Giuseppe Ottaviano authored
      Summary: See D2696092
      
      Reviewed By: meyering
      
      Differential Revision: D2696360
      
      fb-gh-sync-id: 2cfa9258be899a93a58c59dae70a10fef6f457c8
      422c78be
    • Giuseppe Ottaviano's avatar
      Skip reallocation in shrink_to_fit for empty fbvector · ac06a309
      Giuseppe Ottaviano authored
      Reviewed By: philippv
      
      Differential Revision: D2696314
      
      fb-gh-sync-id: 105de2d99bb9f5ac82310e37f9302022c103befc
      ac06a309
    • Mark Isaacson's avatar
      Make folly's T_CHECK_TIMEOUT/T_CHECK_TIME_LT use SKIP() on failure · 140c62d2
      Mark Isaacson authored
      Summary: Make these more spurious failure-aware/tolerant.
      
      Reviewed By: yfeldblum
      
      Differential Revision: D2689775
      
      fb-gh-sync-id: 1a9b247b97cc3529b12f6f7b76a4af2e32822d45
      140c62d2
    • Jim Meyering's avatar
      folly/detail/ThreadLocalDetail.h: avoid UBSAN-detected memcpy abuse · f6f52f3e
      Jim Meyering authored
      Summary: [technically, the existing code is probably a no-op on
      all systems we care about, but since it is officially UB, switching
      to a more strict platform could cause trouble, so it's worth fixing]
      
      Calling memcpy with "nullptr" as 2nd argument is undefined, even when
      the third argument is zero, and causes a failure when testing with an
      UBSAN-enabled binary (-fsanitize=undefined).
      Before this change, the buck-run test below would evoke this failure:
      
        [ RUN      ] ThreadLocalPtr.BasicDestructor
        folly/detail/ThreadLocalDetail.h:533:29: runtime error: null pointer passed as argument 2, which is declared to never be null
        third-party-buck/build/glibc/include/string.h:47:45: note: nonnull attribute specified here
      
      Ironically, the failure of the target-determinator-buck_push_blocking test (due to an unrelated proxygen dep problem) would block me from landing this, so I am adding this line to override it.
      
      Reviewed By: luciang, alexshap
      
      Differential Revision: D2692625
      
      fb-gh-sync-id: 8bdc5cd2899705f39c9565d640921de1f363807d
      f6f52f3e
    • Adrien Conrath's avatar
      Remove undefined behavior in goodMallocSize() · ffe26edc
      Adrien Conrath authored
      Reviewed By: ot
      
      Differential Revision: D2695741
      
      fb-gh-sync-id: d33263c1ffa5651d66f37992ce365dae157ed449
      ffe26edc
    • Giuseppe Ottaviano's avatar
      Make goodMallocSize always use nallocx · 5d820ea6
      Giuseppe Ottaviano authored
      Summary: `goodMallocSize` is used extensively in `folly` data structures,
      especially for containers optimized for small contents, such as
      `fbstring` and `small_vector`.
      
      However, it makes the design decision to align the allocation size to
      a x86 cache line, forcing a minimum allocation size of `64` bytes,
      despite jemalloc can provide smaller size classes (8, 16, 32,
      48). This causes a large discontinuity between small contents that can
      be inlined and heap-allocated contents:
      
      - For `fbstring`, a string of 23 bytes (including terminator) occupies
        24 bytes (`sizeof(fbstring)`), a string of 24 bytes occupies 24 + 64
        + allocation overhead when it could be 24 + 32 + allocation
        overhead. The waste is more than 50%.
      
      - For `small_vector<uint32_t, 1, uint32_t>`, for instance, a vector
        with 1 element occupies 12 bytes, a vector with 2 elements occupies
        12 + 64 + allocation overhead when it could be 12 + 8 + allocation
        overhead. The waste is more than 250%.
      
      With this diff we just trust jemalloc and always use `nallocx`. If a
      data structure need cache-line alignment it should be implemented at
      its level.
      
      Reviewed By: elsteveogrande
      
      Differential Revision: D2688156
      
      fb-gh-sync-id: 46548d4a91952e7c673d4f0997c4c067e03c190d
      5d820ea6
    • Yedidya Feldblum's avatar
      A well-known instance of folly::Unit · 1f4a8221
      Yedidya Feldblum authored
      Summary: [Folly] A well-known instance of `folly::Unit`.
      
      Reviewed By: jsedgwick
      
      Differential Revision: D2693426
      
      fb-gh-sync-id: fe595abc852500275a827b4f400cfda7091a55f7
      1f4a8221
    • Yedidya Feldblum's avatar
      ExceptionWrapper::with_exception variant, deducing exception type from functor type · 085c0a8d
      Yedidya Feldblum authored
      Summary: [Folly] `ExceptionWrapper::with_exception` variant deducing exception type from functor type.
      
      From the `README.md`:
      
          // TODO(jsedgwick) infer exception type from the type of the function
      
      Reviewed By: Gownta
      
      Differential Revision: D2685579
      
      fb-gh-sync-id: 158bcd030019f28b4f392d17167ab0bd67cc1edb
      085c0a8d
  2. 24 Nov, 2015 2 commits
    • Qinfan Wu's avatar
      Add sdallocx for jemalloc · e9df6f79
      Qinfan Wu authored
      Summary: Make jemalloc's sdallocx function available in folly and allow future code to
      use sized deallocation.
      
      Reviewed By: elsteveogrande, ot
      
      Differential Revision: D2687746
      
      fb-gh-sync-id: 9ef8a16d2c63c4eb8cecab7639add423ef093c2d
      e9df6f79
    • Yedidya Feldblum's avatar
      Fix Build: folly/test/ThreadLocalTest · 442584e7
      Yedidya Feldblum authored
      Summary: [Folly] Fix Build: `folly/test/ThreadLocalTest`.
      
      The problem is that the to the shared lib used in the test is hardcoded. It's a relative path, so that's nice, but it has too many assumptions about where the build system deposits test binaries.
      
      Instead, let the code simply look for the shared lib in the same directory as the test binary. This makes fewer assumptions on the build system.
      
      Reviewed By: andriigrynenko
      
      Differential Revision: D2689047
      
      fb-gh-sync-id: 648da079b7af5bc9893709d009e577347dd4a463
      442584e7
  3. 23 Nov, 2015 5 commits
    • Haijun Zhu's avatar
      Fix HHWheelTimerTest.AtMostEveryN · aba93096
      Haijun Zhu authored
      Summary: If it takes too long to finish it is probably heavily loaded. Skip if
      so.
      
      Reviewed By: mmcduff
      
      Differential Revision: D2687346
      
      fb-gh-sync-id: 282cac7e4df361f714de1089f11b098af79b1512
      aba93096
    • Chad Parry's avatar
      Simplify an exception handler · 6bafdff6
      Chad Parry authored
      Summary: It looks like there is no reason to catch a `std::exception` and then `dynamic_cast` to a derived exception. We can just catch the derived exception directly.
      
      whatcouldgowrong4
      
      Reviewed By: yfeldblum
      
      Differential Revision: D2677284
      
      fb-gh-sync-id: 0149e4d4aecc96257376d410f592620205441f66
      6bafdff6
    • Michael Oliver's avatar
      Benchmark std::to_string vs. folly::to<std::string> · 4b750cf3
      Michael Oliver authored
      Summary: We wanted to compare the benefits of using folly::to<std::string> directly, and decided this benchmarking might as well be contributed back to folly.  Here are the results:
      
      ```
        ============================================================================
        folly/test/ConvTest.cpp                         relative  time/iter  iters/s
        ============================================================================
        ...
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(1)                                  157.88ns    6.33M
        u64ToStringFollyMeasure(1)                       212.52%    74.29ns   13.46M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(12)                                 158.14ns    6.32M
        u64ToStringFollyMeasure(12)                      212.55%    74.40ns   13.44M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(123)                                152.26ns    6.57M
        u64ToStringFollyMeasure(123)                     210.95%    72.18ns   13.85M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(1234)                               152.40ns    6.56M
        u64ToStringFollyMeasure(1234)                    204.82%    74.41ns   13.44M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(12345)                              156.01ns    6.41M
        u64ToStringFollyMeasure(12345)                   208.90%    74.68ns   13.39M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(123456)                             178.21ns    5.61M
        u64ToStringFollyMeasure(123456)                  238.77%    74.63ns   13.40M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(1234567)                            161.38ns    6.20M
        u64ToStringFollyMeasure(1234567)                 203.09%    79.46ns   12.58M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(12345678)                           159.01ns    6.29M
        u64ToStringFollyMeasure(12345678)                192.42%    82.64ns   12.10M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(123456789)                          169.07ns    5.91M
        u64ToStringFollyMeasure(123456789)               203.70%    83.00ns   12.05M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(1234567890)                         166.56ns    6.00M
        u64ToStringFollyMeasure(1234567890)              187.21%    88.97ns   11.24M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(12345678901)                        176.22ns    5.67M
        u64ToStringFollyMeasure(12345678901)             197.57%    89.20ns   11.21M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(123456789012)                       174.74ns    5.72M
        u64ToStringFollyMeasure(123456789012)            182.52%    95.74ns   10.45M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(1234567890123)                      178.47ns    5.60M
        u64ToStringFollyMeasure(1234567890123)           186.81%    95.54ns   10.47M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(12345678901234)                     187.33ns    5.34M
        u64ToStringFollyMeasure(12345678901234)          184.05%   101.78ns    9.82M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(123456789012345)                    198.16ns    5.05M
        u64ToStringFollyMeasure(123456789012345)         182.48%   108.59ns    9.21M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(1234567890123456)                   182.56ns    5.48M
        u64ToStringFollyMeasure(1234567890123456)        164.19%   111.19ns    8.99M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(12345678901234567)                  186.80ns    5.35M
        u64ToStringFollyMeasure(12345678901234567)       162.33%   115.07ns    8.69M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(123456789012345678)                 195.08ns    5.13M
        u64ToStringFollyMeasure(123456789012345678)      165.51%   117.87ns    8.48M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(1234567890123456789)                200.27ns    4.99M
        u64ToStringFollyMeasure(1234567890123456789)     164.62%   121.65ns    8.22M
        ----------------------------------------------------------------------------
        u64ToStringClibMeasure(12345678901234567890U)              203.18ns    4.92M
        u64ToStringFollyMeasure(12345678901234567890U)   162.38%   125.13ns    7.99M
        ----------------------------------------------------------------------------
        ...
        ============================================================================
      ```
      
      Reviewed By: yfeldblum
      
      Differential Revision: D2680998
      
      fb-gh-sync-id: 179fbd756c714c92b7230d3750af024865ecbb0a
      4b750cf3
    • Sara Golemon's avatar
      Add missing header files to folly-automake · eebb50f2
      Sara Golemon authored
      Summary: Closes https://github.com/facebook/folly/issues/304
      
      Reviewed By: yfeldblum
      
      Differential Revision: D2685289
      
      fb-gh-sync-id: 563697fb01ee358472b9080058848d2b557ba466
      eebb50f2
    • Sara Golemon's avatar
      Fix minor typo in folly/Conv.h · f757d940
      Sara Golemon authored
      Summary: 'flating point' => 'floating point'
      
      Closes https://github.com/facebook/folly/issues/241
      
      Reviewed By: yfeldblum
      
      Differential Revision: D2685280
      
      fb-gh-sync-id: 2e9c2b5419d0c631af4a32c5ab6b7366033ca34c
      f757d940
  4. 21 Nov, 2015 1 commit
  5. 20 Nov, 2015 5 commits
    • Lucian Grijincu's avatar
      Revert "Fix folly::ThreadLocal to work in a shared library" · 7ce0da39
      Lucian Grijincu authored
      Summary: This reverts commit 9d355f96c4fe073c889f28070d5952fa4eb007a0.
      
      build-break
      
      Reviewed By: sctaylor
      
      Differential Revision: D2680784
      
      fb-gh-sync-id: a2df233bcd3721ffbfcd6fd71ea3cabbebebd4c4
      7ce0da39
    • Haijun Zhu's avatar
      get back AsyncServerSocket's setMaxNumMessagesInQueue · 16811575
      Haijun Zhu authored
      Summary: D2613681 codemod'ed a thrift server api but it also renamed that api
      in AsyncServerSocket, which is a mistake. Fix that in AsyncServerSocket, and
      all other places that calls this api.
      
      Reviewed By: alandau, JoelMarcey
      
      Differential Revision: D2677837
      
      fb-gh-sync-id: 0d91f1a623229e99be59ca9dcd27f1330a9a1b64
      16811575
    • Anirudh Ramachandran's avatar
      Remove unnecessary resetClientHelloParsing callback in AsyncSSLSocket · 422bf874
      Anirudh Ramachandran authored
      Summary: Remove unnecessary resetClientHelloParsing callback which causes problems wiht
      Openssl 1.0.2
      
      Reviewed By: knekritz
      
      Differential Revision: D2664730
      
      fb-gh-sync-id: d1b55ae493b4c92627ad41e7bf85f1e1a777bd2b
      422bf874
    • Alexander Shaposhnikov's avatar
      Remove busy wait and support multiple wait · badc3ebe
      Alexander Shaposhnikov authored
      Summary: Remove busy wait from Future::wait.
      If future.wait(timeout) has succeded we should
      return a ready future, otherwise we should return a future
      for the final result (not necessarily ready).
      
      Reviewed By: yfeldblum
      
      Differential Revision: D2646860
      
      fb-gh-sync-id: 62671d09073ad86e84df8c9257e961d2a8c2a339
      badc3ebe
    • Delyan Kratunov's avatar
      Expose move result from LockFreeRingBuffer::Cursor · 587e4b4d
      Delyan Kratunov authored
      Summary: Without feedback that a `moveForward` or `moveBackward` did nothing, a user
      cannot implement this idiom safely:
      
      ```
      while(rb.tryRead(entry, cursor)) {
        doSomething(entry);
        cursor.moveBackward();
      }
      ```
      
      In the case where the ring buffer has not overflowed and slot 0 is still
      available, the reader will get stuck at it (read it continuously) until
      the buffer overflows.
      
      This diff allows the above example to become:
      
      ```
      while(rb.tryRead(entry, cursor)) {
        doSomething(etnry);
        if (!cursor.moveBackward()) {
          break;
        }
      }
      ```
      
      Reviewed By: bryceredd
      
      Differential Revision: D2674975
      
      fb-gh-sync-id: a0c5857daf186ef19e203f90acc2145590f85c3b
      587e4b4d
  6. 19 Nov, 2015 3 commits
    • Giuseppe Ottaviano's avatar
      Fix usingJEMalloc with Clang · 78022b6e
      Giuseppe Ottaviano authored
      Summary: Clang is too clever and in some contexts optimizes away the `malloc`, but we rely on a side-effect. Declaring the variable as static forces it to call `malloc`. We could free the pointer relying on the fact that the lambda is guaranteed to be called only once, but I feel more comfortable just leaking it (LSan won't complain).
      
      Reviewed By: philippv
      
      Differential Revision: D2674769
      
      fb-gh-sync-id: 1153a3ca226c6b7aa64c453bd61b036dcbf3ffcc
      78022b6e
    • Anton Likhtarov's avatar
      Fix invalid DCHECK · 2fa98577
      Anton Likhtarov authored
      Summary: There's a race between insert() and erase(): as soon as insert()
      releases the lock (swaps kLockedKey_ with the actual key), an erase() might
      jump in and invalidate the key.
      
      As far as I can tell, this bug existed since the beginning.
      
      Reviewed By: nbronson
      
      Differential Revision: D2673099
      
      fb-gh-sync-id: 4721893d2ad4836e11acc0fb4ecb0dd7b2b69be1
      2fa98577
    • Andrii Grynenko's avatar
      Fix folly::ThreadLocal to work in a shared library · f2daf056
      Andrii Grynenko authored
      Reviewed By: bmaurer
      
      Differential Revision: D2667499
      
      fb-gh-sync-id: 463f86752240bd88761910de934ba25d6e62fafe
      f2daf056
  7. 18 Nov, 2015 1 commit
  8. 17 Nov, 2015 1 commit
  9. 16 Nov, 2015 1 commit
  10. 15 Nov, 2015 1 commit
  11. 13 Nov, 2015 2 commits
    • Andrii Grynenko's avatar
      Make collect work for types with no default constructors · 5bad4e9f
      Andrii Grynenko authored
      Summary: This doesn't make the code less efficient, because RVO can't be used for CollectVariadicContext. Thus moving existing tuple vs constructing new tuple by moving all values from other tuple (where each value is wrapped in folly::Optional) should be pretty much the same.
      
      Reviewed By: hannesr
      
      Differential Revision: D2650293
      
      fb-gh-sync-id: 648a358bf093a0bb9d058a997af9bf59014ad77c
      5bad4e9f
    • Yang Chi's avatar
      Allow a AsyncSocket to be corked the whole time · 07c15086
      Yang Chi authored
      Summary: Add a new method to cork a socket in a persistent manner, instead of the current on-off manner. This is default to false. The liger part of turning this on will be in a separate diff.
      
      I thought about whether I need to turn cork off based on some criteria to alleviate the perf degradation. The obvious things I can think off is just amount of data written as a threshold, or a timeout. But TCP is doing this already for us, unless we want the data threshold to be less than MSS, or we want the timeout to be less than 200ms. THoughts?
      
      Reviewed By: shikong
      
      Differential Revision: D2639260
      
      fb-gh-sync-id: 2821f669c9f72d5ac4c33195bb192fc4110ffe9d
      07c15086
  12. 12 Nov, 2015 3 commits
    • Giuseppe Ottaviano's avatar
      Make Malloc.h self-contained · 19d24e21
      Giuseppe Ottaviano authored
      Reviewed By: philippv
      
      Differential Revision: D2643313
      
      fb-gh-sync-id: 10b9f735725ce47fab4bbfaa5972b3863357365f
      19d24e21
    • Giuseppe Ottaviano's avatar
      Disable assertions in FBString when used as std::string · a348e154
      Giuseppe Ottaviano authored
      Reviewed By: Gownta
      
      Differential Revision: D2643850
      
      fb-gh-sync-id: 2c4bb844ea2006215b0637cb1ba08c636faefe05
      a348e154
    • Chad Parry's avatar
      Restore the definition of HHWheelTimer::UniquePtr · 65b8a020
      Chad Parry authored
      Summary: Changing the definition of `HHWheelTimer::UniquePtr` wasn't safe, because some clients were using that type outside of the `HHWheelTimer::newTimer` helper. I'm changing that part back. We'll still be able to proceed with my other codemod to `HHWheelTimer`, but we'll always have two different smart pointer types to manage: `UniquePtr` and `IntrusivePtr`.
      
      Reviewed By: djwatson
      
      Differential Revision: D2644721
      
      fb-gh-sync-id: 14685be62355f09d39c4139ef7186d60b5f48dcd
      65b8a020
  13. 11 Nov, 2015 6 commits
    • Stepan Palamarchuk's avatar
      Make Optional nothrow_default_constructible · 2cf78a6a
      Stepan Palamarchuk authored
      Summary: It never throws.
      
      Reviewed By: pavlo-fb
      
      Differential Revision: D2640886
      
      fb-gh-sync-id: cd643f8847f4bf5619415731484f91fb07116784
      2cf78a6a
    • Yang Chi's avatar
      Add a buffer callback to AsyncSocket · 7749a469
      Yang Chi authored
      Summary: This is probably easier than D2612490. The idea is just to add a callback to write, writev and writeChain in AsyncSocket, so upper layer can know when data starts to buffer up
      
      Reviewed By: mzlee
      
      Differential Revision: D2623385
      
      fb-gh-sync-id: 98d32ca83871aaa4f6c75a769b5f1bf0b5d62c3e
      7749a469
    • Chad Parry's avatar
      Improve the DelayedDestruction smart pointer · 596d6f6d
      Chad Parry authored
      Summary: There have been several ASAN bugs cropping up because the lifetime of an `HHWheelTimer` is not being manager properly. I think that people are too comfortable passing around a raw `HHWheelTimer*` even when it is difficult to prove correctness. The recommended solution used to be to create a `DestructorGuard` every time it was needed. There is enough friction there that not everyone is doing that like they should. We should make resource management easier---as easy as using raw pointers, in fact.
      
      I've fixed the broken copy semantics of `DestructorGuard` and added the operators that allow it to be used as a smart pointer. I added the `IntrusivePtr` helper that can manage an arbitrary derived class of `DelayedDestructionBase`. Now, clients can all safely pass around an `IntrusivePtr` everywhere they used to use a raw pointer. They will get automatic resource management for free.
      
      If you are not convinced that `DestructorGuard` should be changed, then note that the existing behavior is dangerously buggy. Consider the following innocent code that accidentally uses the implicitly-defined copy constructor:
        auto d = DestructorGuard(p);
      This results in undefined behavior, because `p` can be accessed after it is destroyed! The bug happens because the default copy constructor copies the raw pointer but doesn't increment the count.
      
      In a separate diff, I'll change all clients who pass around a raw `HHWheelTimer*` to use an `IntrusivePtr<HHWheelTimer>` instead. Then we can even entertain a long-term plan of switching from intrusive pointers to the standard `shared_ptr`.
      
      Reviewed By: djwatson
      
      Differential Revision: D2627941
      
      fb-gh-sync-id: 58a934d64540d0bbab334adc4f23d31d507692da
      596d6f6d
    • Shaft Wu's avatar
      UNSYNCHRONIZED does NOT unlock the mutex · 6762f08b
      Shaft Wu authored
      Summary: My colleague tuomaspelkonen discovered a weird UNSYNCHRONIZED issue a few weeks ago and we ever since stopped using it. Now I finally have some time to root cause it. It turns out UNSYNCHRONIZED unlock the mutex then lock the mutex again, because it copy constructs LockedPtr for overriding the name within the scope, and copy construct locks the mutex again. A one character fix here is to take a reference of LockedPtr instead of copy construct it. However since this is my first time look at the code here, please advise if this is horribly wrong or propose better fix. Also added a test to reproduce the issue without the fix as well as verify the fix.
      
      Reviewed By: yfeldblum
      
      Differential Revision: D2633028
      
      fb-gh-sync-id: a9e8d39b08d4d1265979f8bdaae83619566d10a0
      6762f08b
    • Alexander Shaposhnikov's avatar
      Revert my change (which broke down the cont build) · 7ab42fa8
      Alexander Shaposhnikov authored
      Summary: hg backout -r c9f7b5f3185a
      Revert my change (which broke down the cont build t9048692)
      
      Reviewed By: djwatson
      
      Differential Revision: D2640797
      
      fb-gh-sync-id: 51f196ac5a3560fde4dc8fe7bb6ef278d74136e5
      7ab42fa8
    • Alexander Shaposhnikov's avatar
      Remove busy wait · 52b003af
      Alexander Shaposhnikov authored
      Summary: Wait uses baton & callback running baton.post
      when the original future is ready. However wrapping baton.post
      with a funciton call (preparing a new value) adds the following race: baton.wait wakes up
      before that function has call actually finished.
      The explanation is the following: to prepare the value of the new future it's necessary
      1. baton.post() 2. set the value (move constructor, memory operations, ...)
      and this code is executed in a separate thread.
      The main idea of this fix is to avoid creating a new future
      (whose value is calculated using that 2-step procedure)
      and set a callback instead. This callback will be executed when the future is ready and actually
      it either will be the last step of promise.setValue or it will run immediately if the future
      we are waiting for already contains a value.
      
      Reviewed By: fugalh
      
      Differential Revision: D2636409
      
      fb-gh-sync-id: df3e9bbcc56a5fac5834ffecc63f1bcb94ace02c
      52b003af
  14. 10 Nov, 2015 1 commit