|
| 1 | +# Folly Integration Example |
| 2 | + |
| 3 | +This example demonstrates comprehensive async interoperability between Rust and C++ using the [Folly](https://github.com/facebook/folly) library. It showcases bidirectional async function calls, parallel computation, exception handling, stream processing, and complex communication patterns. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The Folly example is a sophisticated demonstration of Rust-C++ async integration that covers: |
| 8 | + |
| 9 | +- **Bidirectional Async Calls**: Both Rust calling C++ async functions and C++ calling Rust async functions |
| 10 | +- **Parallel Computation**: Multithreaded dot product calculation using both Folly coroutines and futures |
| 11 | +- **Exception Handling**: Proper propagation of exceptions across language boundaries |
| 12 | +- **Stream Processing**: Async stream generators (FizzBuzz implementations) |
| 13 | +- **Complex Communication**: Ping-pong pattern with recursive async calls |
| 14 | +- **Resource Management**: Future dropping and coroutine lifecycle management |
| 15 | + |
| 16 | +## Prerequisites |
| 17 | + |
| 18 | +### System Requirements |
| 19 | + |
| 20 | +- **C++20 compiler** (GCC 10+, Clang 11+, or MSVC 2019+) |
| 21 | +- **Folly library** installed and available |
| 22 | +- **Rust 2018 edition** or later |
| 23 | + |
| 24 | +### Folly Installation |
| 25 | + |
| 26 | +The build script uses the `find-folly` crate to automatically locate your Folly installation. Ensure Folly is properly installed on your system: |
| 27 | + |
| 28 | +#### Ubuntu/Debian |
| 29 | +```bash |
| 30 | +sudo apt-get install libfolly-dev |
| 31 | +``` |
| 32 | + |
| 33 | +#### macOS (with Homebrew) |
| 34 | +```bash |
| 35 | +brew install folly |
| 36 | +``` |
| 37 | + |
| 38 | +#### Building from Source |
| 39 | +Follow the [official Folly installation guide](https://github.com/facebook/folly#build) for your platform. |
| 40 | + |
| 41 | +## Building and Running |
| 42 | + |
| 43 | +### Build the Example |
| 44 | +```bash |
| 45 | +cd examples/folly |
| 46 | +cargo build |
| 47 | +``` |
| 48 | + |
| 49 | +### Run the Interactive Demo |
| 50 | +```bash |
| 51 | +cargo run |
| 52 | +``` |
| 53 | + |
| 54 | +### Run Tests |
| 55 | +```bash |
| 56 | +cargo test |
| 57 | +``` |
| 58 | + |
| 59 | +## Architecture |
| 60 | + |
| 61 | +### Key Components |
| 62 | + |
| 63 | +#### Rust Side (`src/main.rs`) |
| 64 | +- **Future Types**: `RustFutureVoid`, `RustFutureF64`, `RustFutureString`, `RustFutureStringNamespaced` |
| 65 | +- **Stream Types**: `RustStreamString` |
| 66 | +- **Parallel Computation**: Recursive async dot product using `async-recursion` |
| 67 | +- **Thread Pool**: Shared `ThreadPool` for concurrent execution |
| 68 | + |
| 69 | +#### C++ Side (`src/folly_example.cpp`) |
| 70 | +- **Folly Coroutines**: Using `folly::coro::Task` for async operations |
| 71 | +- **Folly Futures**: Traditional future combinators with `folly::Future` |
| 72 | +- **Thread Pool**: `folly::CPUThreadPoolExecutor` for parallel work |
| 73 | +- **Exception Handling**: Custom exception types with proper propagation |
| 74 | + |
| 75 | +#### Headers (`include/folly_example.h`) |
| 76 | +- **Future Definitions**: `CXXASYNC_DEFINE_FUTURE` macros for type mapping |
| 77 | +- **Stream Definitions**: `CXXASYNC_DEFINE_STREAM` macros |
| 78 | +- **Custom Exception Handling**: Specialized `TryCatch` implementation |
| 79 | + |
| 80 | +## Features Demonstrated |
| 81 | + |
| 82 | +### 1. Bidirectional Async Calls |
| 83 | + |
| 84 | +**Rust → C++**: |
| 85 | +```rust |
| 86 | +// Call C++ coroutine from Rust |
| 87 | +let result = ffi::folly_dot_product_coro().await.unwrap(); |
| 88 | + |
| 89 | +// Call C++ future combinator from Rust |
| 90 | +let result = ffi::folly_dot_product_futures().await.unwrap(); |
| 91 | +``` |
| 92 | + |
| 93 | +**C++ → Rust**: |
| 94 | +```cpp |
| 95 | +// Call Rust async function from C++ |
| 96 | +auto future = rust_dot_product(); |
| 97 | +double result = co_await std::move(future); |
| 98 | +``` |
| 99 | +
|
| 100 | +### 2. Parallel Computation |
| 101 | +
|
| 102 | +Both languages implement the same parallel dot product algorithm: |
| 103 | +- **Vector Size**: 16,384 elements |
| 104 | +- **Split Threshold**: 32 elements |
| 105 | +- **Thread Pool**: 8 threads (C++), futures thread pool (Rust) |
| 106 | +- **Algorithm**: Recursive divide-and-conquer with async/await |
| 107 | +
|
| 108 | +### 3. Exception Handling |
| 109 | +
|
| 110 | +**C++ Exceptions → Rust**: |
| 111 | +```cpp |
| 112 | +// C++ throws custom exception |
| 113 | +throw MyException("kaboom"); |
| 114 | +``` |
| 115 | + |
| 116 | +```rust |
| 117 | +// Rust catches as CxxAsyncException |
| 118 | +match result { |
| 119 | + Err(err) => assert_eq!(err.what(), "kaboom"), |
| 120 | + Ok(_) => panic!("should have failed"), |
| 121 | +} |
| 122 | +``` |
| 123 | + |
| 124 | +**Rust Errors → C++**: |
| 125 | +```rust |
| 126 | +// Rust returns error |
| 127 | +Err(CxxAsyncException::new("kapow".into())) |
| 128 | +``` |
| 129 | + |
| 130 | +```cpp |
| 131 | +// C++ catches exception |
| 132 | +try { |
| 133 | + co_await rust_function(); |
| 134 | +} catch (const rust::async::Error& e) { |
| 135 | + std::cout << e.what() << std::endl; // "kapow" |
| 136 | +} |
| 137 | +``` |
| 138 | + |
| 139 | +### 4. Stream Processing |
| 140 | + |
| 141 | +Async stream generators demonstrating: |
| 142 | +- **Basic Streams**: FizzBuzz sequence generation |
| 143 | +- **Nested Async**: Streams that internally await other futures |
| 144 | +- **Exception Propagation**: Streams that throw exceptions mid-stream |
| 145 | +- **Resource Cleanup**: Proper stream termination and cleanup |
| 146 | + |
| 147 | +### 5. Complex Communication Patterns |
| 148 | + |
| 149 | +**Ping-Pong Pattern**: |
| 150 | +```rust |
| 151 | +fn rust_folly_ping_pong(i: i32) -> RustFutureString { |
| 152 | + RustFutureString::infallible(async move { |
| 153 | + format!("{}ping ", |
| 154 | + if i < 4 { |
| 155 | + ffi::folly_ping_pong(i + 1).await.unwrap() |
| 156 | + } else { |
| 157 | + "".to_owned() |
| 158 | + } |
| 159 | + ) |
| 160 | + }) |
| 161 | +} |
| 162 | +``` |
| 163 | + |
| 164 | +This creates a recursive call chain: Rust → C++ → Rust → C++ → ... until termination. |
| 165 | + |
| 166 | +## Test Coverage |
| 167 | + |
| 168 | +The example includes comprehensive tests covering: |
| 169 | + |
| 170 | +### Core Functionality Tests |
| 171 | +- `test_rust_calling_cpp_synchronously_coro()` - Rust → C++ coroutine calls |
| 172 | +- `test_rust_calling_cpp_synchronously_futures()` - Rust → C++ future calls |
| 173 | +- `test_rust_calling_cpp_on_scheduler()` - Scheduled execution |
| 174 | +- `test_cpp_calling_void_rust_synchronously()` - C++ → Rust void calls |
| 175 | +- `test_cpp_calling_rust_synchronously()` - C++ → Rust value calls |
| 176 | +- `test_cpp_calling_rust_on_scheduler()` - Scheduled Rust execution |
| 177 | + |
| 178 | +### Error Handling Tests |
| 179 | +- `test_cpp_async_functions_throwing_exceptions()` - C++ exception propagation |
| 180 | +- `test_rust_async_functions_returning_errors()` - Rust error propagation |
| 181 | + |
| 182 | +### Advanced Pattern Tests |
| 183 | +- `test_ping_pong()` - Recursive async communication |
| 184 | +- `test_complete()` - Void future completion |
| 185 | +- `test_dropping_futures()` - Resource cleanup |
| 186 | +- `test_fizzbuzz()` - Basic stream processing |
| 187 | +- `test_indirect_fizzbuzz()` - Nested async streams |
| 188 | +- `test_streams_throwing_exceptions()` - Stream error handling |
| 189 | +- `test_dropping_coroutines()` - Coroutine lifecycle management |
| 190 | + |
| 191 | +## Implementation Notes |
| 192 | + |
| 193 | +### Thread Safety |
| 194 | +- All async operations are thread-safe |
| 195 | +- Shared thread pools coordinate work between languages |
| 196 | +- Proper synchronization for cross-language communication |
| 197 | + |
| 198 | +### Memory Management |
| 199 | +- Automatic resource cleanup for futures and streams |
| 200 | +- RAII patterns ensure proper destruction |
| 201 | +- No memory leaks in cross-language async operations |
| 202 | + |
| 203 | +### Performance Considerations |
| 204 | +- Minimal overhead for language boundary crossings |
| 205 | +- Efficient parallel computation with proper work distribution |
| 206 | +- Optimized for both throughput and latency |
| 207 | + |
| 208 | +### Namespace Support |
| 209 | +The example demonstrates C++ namespace mapping: |
| 210 | +```cpp |
| 211 | +CXXASYNC_DEFINE_FUTURE(::rust::String, foo, rust, bar, RustFutureStringNamespaced); |
| 212 | +``` |
| 213 | +
|
| 214 | +```rust |
| 215 | +#[cxx_async::bridge(namespace = foo::rust::bar)] |
| 216 | +unsafe impl Future for RustFutureStringNamespaced { |
| 217 | + type Output = StringNamespaced; |
| 218 | +} |
| 219 | +``` |
| 220 | + |
| 221 | +## Troubleshooting |
| 222 | + |
| 223 | +### Common Build Issues |
| 224 | + |
| 225 | +**Folly Not Found**: |
| 226 | +``` |
| 227 | +error: Couldn't find the Folly library! |
| 228 | +``` |
| 229 | +Solution: Ensure Folly is installed and `PKG_CONFIG_PATH` includes Folly's `.pc` file. |
| 230 | + |
| 231 | +**C++20 Support**: |
| 232 | +``` |
| 233 | +error: coroutines are a C++20 extension |
| 234 | +``` |
| 235 | +Solution: Verify your compiler supports C++20 and the `--std=c++20` flag is properly set. |
| 236 | + |
| 237 | +**Linker Errors**: |
| 238 | +``` |
| 239 | +undefined reference to folly symbols |
| 240 | +``` |
| 241 | +Solution: Ensure all Folly dependencies are properly linked. Check `find-folly` output. |
| 242 | + |
| 243 | +### Runtime Issues |
| 244 | + |
| 245 | +**Thread Pool Errors**: Ensure sufficient system resources for the configured thread pools. |
| 246 | + |
| 247 | +**Segmentation Faults**: Usually indicate improper future/stream lifecycle management. Review the test patterns for proper usage. |
| 248 | + |
| 249 | +## Contributing |
| 250 | + |
| 251 | +When modifying this example: |
| 252 | + |
| 253 | +1. **Maintain Test Coverage**: All new features must include comprehensive tests |
| 254 | +2. **Document Changes**: Update this README for any new functionality |
| 255 | +3. **Follow Patterns**: Use existing error handling and async patterns |
| 256 | +4. **Verify Cross-Platform**: Test on multiple platforms if possible |
| 257 | + |
| 258 | +## Related Examples |
| 259 | + |
| 260 | +- [`examples/cppcoro`](../cppcoro/README.md) - Similar patterns using cppcoro instead of Folly |
| 261 | +- [`examples/common`](../common/README.md) - Shared utilities and patterns |
| 262 | + |
| 263 | +## License |
| 264 | + |
| 265 | +This example is licensed under both MIT and Apache 2.0 licenses, following the same licensing as the parent project. |
0 commit comments