Repository: react-native
Issue: [TM][C++] Improve C++ TurboModule system
Labels: Help Wanted :octocat:,RN Team

# Context
The C++ TurboModule system, unlike the ObjC and Java systems, is fairly immature. It has a lot of problems that need to be addressed, and gaps that need to be filled. Unfortunately, at Facebook, we only have a handful of C++ NativeModules. Additionally, there's no urgency to convert these NativeModules to C++-only TurboModules, because these NativeModules are already going through a bridging layer in the TurboModule system (i.e: we could turn off the old NativeModule system and still have these NativeModules fully functional). Therefore, it's difficult to prioritize development of the C++ TurboModule system internally. 

For transparency, this is my focus in H1 2020:
   - Open source and internal iOS NativeModule migration.
   - Internal rollout of the TurboModule system (on both iOS and Android).
   - The effort to make TurboModules and Codegen open source ready. 

With all the things on my plate this half, I find it unlikely that the C++ TurboModule system will be finished any time soon. So, this GitHub issue documents the work that's required to build out the C++ TurboModule system. If anyone is interested in taking ownership over this portion of the TurboModule project, please feel free to comment below. I'd love to work together and see this completed. 😁

## Problem
This is currently the interface of our C++-only TurboModules:
```C++
class SampleTurboCxxModule : public NativeSampleTurboCxxModuleSpecJSI {
 public:
  SampleTurboCxxModule(std::shared_ptr<CallInvoker> jsInvoker);

  void voidFunc(jsi::Runtime &rt) override;
  bool getBool(jsi::Runtime &rt, bool arg) override;
  double getNumber(jsi::Runtime &rt, double arg) override;
  jsi::String getString(jsi::Runtime &rt, const jsi::String &arg) override;
  jsi::Array getArray(jsi::Runtime &rt, const jsi::Array &arg) override;
  jsi::Object getObject(jsi::Runtime &rt, const jsi::Object &arg) override;
  jsi::Object getValue(
      jsi::Runtime &rt,
      double x,
      const jsi::String &y,
      const jsi::Object &z) override;
  void getValueWithCallback(jsi::Runtime &rt, const jsi::Function &callback)
      override;
  jsi::Value getValueWithPromise(jsi::Runtime &rt, bool error) override;
  jsi::Object getConstants(jsi::Runtime &rt) override;
};
```
There are a few problems with this API.
1. Hermes is a "bring your own locks" VM. The application/framework using Hermes is responsible for making sure that `jsi::Runtime` is accessed safely. This simply isn't possible if we provide all C++-only TurboModules with access to the `jsi::Runtime`.
2. It's not a good idea to give C++-only TurboModules ownership of JSI objects (eg:`jsi::Function`, `jsi::String`, `jsi::Array`, `jsi::Object`). First, this isn't a very clean API. Second, by design, many of these JSI objects (`jsi::Function` especially) can't outlive the `jsi::Runtime` (see [the jsi.h](https://github.com/facebook/react-native/blob/145151ef0f44156bcc03fa4a44a5aa630cc3e5f2/ReactCommon/jsi/jsi/jsi.h#L140-L146)). So, we should had C++-only TurboModules safe wrappers around these objects, so that the TurboModule framework can manage their lifecycles.

Other things to think about:
1. How do we perform cleanup for C++-only TurboModules? Should we expect C++-only TurboModules to have a custom invalidate method, or is using their destructor fine?
2. There is no `CxxTurboModule` class, like we have `JavaTurboModule`, and `ObjCTurboModule`. If we want to make C++-only TurboModules more robust without bloating the codegen, this class would be necessary.

## How do I start?
There is currently only one pure C++ TurboModule: [`SampleTurboCxxModule`](https://github.com/facebook/react-native/blob/d4d8887b5018782eeb3f26efa85125e6bbff73e4/ReactCommon/turbomodule/samples/SampleTurboCxxModule.cpp#L1). This TurboModule extends its "code-generated" spec [`NativeSampleTurboCxxModuleSpecJSI`](https://github.com/facebook/react-native/blob/d4d8887b5018782eeb3f26efa85125e6bbff73e4/ReactCommon/turbomodule/samples/NativeSampleTurboCxxModuleSpecJSI.cpp#L1) (the spec is really just hand-written).  This spec directly extends [`TurboModule`](https://github.com/facebook/react-native/blob/d4d8887b5018782eeb3f26efa85125e6bbff73e4/ReactCommon/turbomodule/core/TurboModule.h#L38-L61), as you can see [here](https://github.com/facebook/react-native/blob/d4d8887b5018782eeb3f26efa85125e6bbff73e4/ReactCommon/turbomodule/samples/NativeSampleTurboCxxModuleSpecJSI.h#L19). 

1. Create a new `CxxTurboModule` class and have it extend TurboModule. Have `NativeSampleTurboCxxModuleSpecJSI` extend `CxxTurboModule`.
2. You can call into CxxTurboModule from the ["code-generated" __hostFunctions](https://github.com/facebook/react-native/blob/d4d8887b5018782eeb3f26efa85125e6bbff73e4/ReactCommon/turbomodule/samples/NativeSampleTurboCxxModuleSpecJSI.cpp#L15-L113)  in `NativeSampleTurboCxxModuleSpecJSI`. Feel free to modify the contents of the __hostFunctions in the codegen. Please look at [`RCTSampleTurboModuleSpec.mm`](https://github.com/facebook/react-native/blob/d4d8887b5018782eeb3f26efa85125e6bbff73e4/ReactCommon/turbomodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm#L1) for an example of how we do this in iOS. Some of the responsibilities of `CxxTurboModule`:
   1. Convert JS arguments (JSI objects) to vanilla C++ objects. 
       1. What do we convert the `Object` type to? Should we use `folly::dynamic`?
       2. What do we convert object literals (eg: `{| foo: bar |}`) to? In ObjC, we convert them to structs, since we know the type of each property.
       3. What do we convert `Array<T>` to? Should we just use an STL container?
   2. Convert C++ returns to JSI objects. 
       1. How do we handle `Promise` returns? 
       2. Do we simply pass in the C++-only TurboModule method a `resolve` and a `reject` lambda, like in ObjC, or do we pass in a "Promise" object, like we do in Android. Alternatively, should we leverage some STL data structure (`std::promise`)?
   3. Dispatch async method calls appropriately on each platform. This will require the `CxxTurboModule` constructor to accept a native `CallInvoker` that dispatches work to a background thread.
      - On iOS, every C++ NativeModule calls its async methods on its own ObjC method queue. 
         1. MethodQueue is created here, for each NativeModule: [code](https://github.com/facebook/react-native/blob/145151ef0f44156bcc03fa4a44a5aa630cc3e5f2/React/Base/RCTModuleData.mm#L210).
         2. C++ NativeModule list is created here, from `RCTModuleData` objects: [code](https://github.com/facebook/react-native/blob/145151ef0f44156bcc03fa4a44a5aa630cc3e5f2/React/CxxModule/RCTCxxUtils.mm#L25-L40).
        3. `DispatchMessageQueueThread` is the class our C++ NativeModules use to dispatch to the  MessageQueue. Here's the async dispatch method: [code](https://github.com/facebook/react-native/blob/145151ef0f44156bcc03fa4a44a5aa630cc3e5f2/React/CxxModule/DispatchMessageQueueThread.h#L26).
      - On Android, all NativeModule async methods dispatch to the same NativeModules thread. 
         1. The NativeModules thread is created by the bridge here: [code](https://github.com/facebook/react-native/blob/145151ef0f44156bcc03fa4a44a5aa630cc3e5f2/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp#L153-L154).
         2. It's passed into buildNativeModuleList here: [code](https://github.com/facebook/react-native/blob/145151ef0f44156bcc03fa4a44a5aa630cc3e5f2/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp#L173-L177).
         3. And used to create CxxNativeModule (i.e: legacy Cxx Native Modules) here: [code](https://github.com/facebook/react-native/blob/145151ef0f44156bcc03fa4a44a5aa630cc3e5f2/ReactAndroid/src/main/jni/react/jni/ModuleRegistryBuilder.cpp#L57-L58).
   4. Asynchronously invoke JS callbacks (ex: `jsi::Function`s) on the JS Thread. This will require the `CxxTurboModule` constructor to accept a `CallInvoker` that dispatches work to the JS thread. Also, we'll have to store all the `jsi::Function` objects passed from JS to C++ so that they can be invoked later by the JS thread. This presents an additional problem:
      - When the TurboModuleManager is destroyed, ensure that all held `jsi::Function`s (i.e: JS callbacks that were passed into C++ from JS) are destroyed. In iOS and Android, we accomplish this by using `LongLivedObjectCollection`, and `CallbackWrapper`. `LongLivedObjectCollection` is cleared when the runtime is destroyed.

## How do I test?
To test your changes, in RNTester on iOS, access the TurboModule in JS via `TurboModuleRegistry.get('SampleTurboCxxModule')`.