| 289 | //============================================================================= |
| 290 | |
| 291 | class TaskCoroutineStubImpl : public TaskCoroutineStub { |
| 292 | public: |
| 293 | TaskCoroutineStubImpl(fl::string name, |
| 294 | TaskFunction function, |
| 295 | size_t /*stack_size*/, |
| 296 | u8 /*priority*/) |
| 297 | FL_NOEXCEPT : mName(fl::move(name)) |
| 298 | , mFunction(fl::move(function)) { |
| 299 | |
| 300 | mContext = fl::detail::CoroutineContext::create(); |
| 301 | |
| 302 | auto& runner = fl::detail::CoroutineRunner::instance(); |
| 303 | runner.enqueue(mContext); |
| 304 | |
| 305 | // Capture by value for thread safety |
| 306 | TaskFunction func = mFunction; |
| 307 | fl::shared_ptr<fl::detail::CoroutineContext> ctx_shared = mContext; |
| 308 | |
| 309 | fl::thread t([ctx_shared, func]() { |
| 310 | // Mark ready and wait for wakeup |
| 311 | ctx_shared->set_thread_ready(true); |
| 312 | ctx_shared->wait(); |
| 313 | |
| 314 | if (ctx_shared->should_stop()) { |
| 315 | ctx_shared->set_completed(true); |
| 316 | return; |
| 317 | } |
| 318 | |
| 319 | auto& main_sema = fl::detail::CoroutineRunner::instance().get_main_thread_semaphore(); |
| 320 | |
| 321 | // Phase 1: signal "started" |
| 322 | main_sema.release(); |
| 323 | |
| 324 | // Set thread-local so exitCurrent() can find this context |
| 325 | runningStubCoroutineContext() = ctx_shared.get(); |
| 326 | |
| 327 | // Execute user function |
| 328 | func(); |
| 329 | |
| 330 | // Clear thread-local |
| 331 | runningStubCoroutineContext() = nullptr; |
| 332 | |
| 333 | // Mark completed and signal "done" |
| 334 | // (skipped if exitCurrent() already did this) |
| 335 | if (!ctx_shared->is_completed()) { |
| 336 | ctx_shared->set_completed(true); |
| 337 | main_sema.release(); |
| 338 | } |
| 339 | }); |
| 340 | |
| 341 | #ifdef TEST_DLL_MODE |
| 342 | CoroutineThreadRegistry::instance().add(fl::move(t)); |
| 343 | #else |
| 344 | t.detach(); |
| 345 | #endif |
| 346 | } |
| 347 | |
| 348 | ~TaskCoroutineStubImpl() override { |