| 208 | } else if ((flags & ValueFunctionFlagsBoundedMainThreadSync) != ValueFunctionFlagsNone) { |
| 209 | // The main thread must not be held for an unbounded time waiting on the JS queue. |
| 210 | // Unlike the throttled path above, the call is never coalesced away, so nothing is |
| 211 | // dropped, only the return value on timeout. Uses the longer shared input deadline |
| 212 | // rather than the throttled bound: a throttled call is fire-and-forget by design, |
| 213 | // whereas these carry side effects the caller wants to land. |
| 214 | auto result = dispatchAndWaitOnJsThread(taskScheduler, |
| 215 | std::chrono::steady_clock::now() + kInputSyncCallDeadline, |
| 216 | callContext.getParameters(), |
| 217 | callContext.getParametersSize()); |
| 218 | if (result.success()) { |
| 219 | retValue = result.value(); |
| 220 | } |
| 221 | } else { |
| 222 | taskScheduler->dispatchOnJsThreadSync(getContext(), [&](auto& jsEntry) { |
| 223 | MainThreadBatchAllowScope mainThreadBatchAllowScope; |
| 224 | retValue = this->doJsCall(jsEntry, |
| 225 | callContext.getParameters(), |
| 226 | callContext.getParametersSize(), |
| 227 | propagatesError ? &callContext.getExceptionTracker() : nullptr, |
| 228 | false); |
| 229 | }); |
| 230 | } |
| 231 | |
| 232 | _mainThreadManager->endBatch(); |
| 233 | } else { |
| 234 | taskScheduler->dispatchOnJsThreadSync(getContext(), [&](auto& jsEntry) { |
| 235 | retValue = this->doJsCall(jsEntry, |
| 236 | callContext.getParameters(), |
| 237 | callContext.getParametersSize(), |
| 238 | propagatesError ? &callContext.getExceptionTracker() : nullptr, |
| 239 | false); |
| 240 | }); |
| 241 | } |
| 242 | }); |
| 243 | |
| 244 | return retValue; |
| 245 | } |
| 246 | |
| 247 | Value ValueFunctionWithJSValue::operator()(const ValueFunctionCallContext& callContext) noexcept { |
| 248 | auto taskScheduler = getTaskScheduler(); |
| 249 | if (taskScheduler == nullptr) { |
| 250 | return Value::undefined(); |
| 251 | } |
| 252 | if (_ignoreIfValdiContextIsDestroyed) { |
| 253 | if (Context::isDestroyedContextFixEnabled()) { |
| 254 | auto ctx = _creationContext.lock(); |
| 255 | if (ctx == nullptr || ctx->isDestroyed()) { |
| 256 | VALDI_WARN(getContext()->getLogger(), |
| 257 | "Function call skipped: creation context {} is destroyed (function: {})", |
| 258 | ctx != nullptr ? std::to_string(ctx->getContextId()) : "expired", |
| 259 | getReferenceInfo().toString()); |
| 260 | return Value::undefined(); |
| 261 | } |
| 262 | } else if (getContext()->isDestroyed()) { |
| 263 | VALDI_WARN(getContext()->getLogger(), |
| 264 | "Function call skipped: ValdiContext {} is destroyed (function: {})", |
| 265 | getContext()->getContextId(), |
| 266 | getReferenceInfo().toString()); |
| 267 | return Value::undefined(); |
nothing calls this directly
no test coverage detected