bugfix: agent cppcrash

Signed-off-by: yangxuguang-huawei <yangxuguang3@h-partners.com>

AI[0%] Human Fixed[0%] Human[100%] AI Adopted[0%]

Change-Id: I00a8035c811778529027d9518f071e1d689fa871
This commit is contained in:
yangxuguang-huawei
2026-08-14 16:01:56 +08:00
parent 7a4af2a2e5
commit db46ad2586
3 changed files with 61 additions and 35 deletions
@@ -187,7 +187,11 @@ public:
*/
void SetNapiAsyncTask(std::shared_ptr<AbilityRuntime::NapiAsyncTask> &task);
void SetDisconnectAsyncTask(const std::shared_ptr<AbilityRuntime::NapiAsyncTask> &task);
// Returns false without overwriting if a pending task exists (prevents re-entrant orphaning).
bool SetDisconnectAsyncTask(const std::shared_ptr<AbilityRuntime::NapiAsyncTask> &task);
// Take ownership; first settler wins. Both guarded by stateLock_.
std::shared_ptr<AbilityRuntime::NapiAsyncTask> TakeDisconnectAsyncTask();
/**
* Add a duplicated pending task.
@@ -211,8 +215,9 @@ public:
// Reject primary/duplicated/staged low-code tasks, clear primary async task, remove connection.
// Public: DoConnectAgentExtensionAbility (js_agent_manager.cpp) rejects staged reuse on AgentManagerService
// connect failure.
void RejectConnectAndCleanup(napi_env env, napi_value error, bool hasPrimaryTask);
// connect failure. Idempotent: takes ownership of napiAsyncTask_ so the first settler wins; callers must not
// pass a stale snapshot flag.
void RejectConnectAndCleanup(napi_env env, napi_value error);
void AdoptDuplicatedPendingTasks(std::vector<std::unique_ptr<AbilityRuntime::NapiAsyncTask>> &&tasks);
@@ -373,10 +378,9 @@ private:
void HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode);
// Returns true when the connect flow must abort (no pending task, or result failure).
bool AbortOnConnectError(napi_env env, int resultCode, bool hasPrimaryTask, bool hasDuplicatedPendingTask);
bool AbortOnConnectError(napi_env env, int resultCode, bool hasDuplicatedPendingTask);
// Build the JS receiver proxy for the just-connected host; nullptr (after cleanup) on failure.
napi_value BuildAgentReceiverProxy(napi_env env, const sptr<IRemoteObject> &remoteObject,
bool hasPrimaryTask);
napi_value BuildAgentReceiverProxy(napi_env env, const sptr<IRemoteObject> &remoteObject);
protected:
napi_env env_;
@@ -268,22 +268,23 @@ void JSAgentConnection::OnAbilityConnectDone(const AppExecFwk::ElementName &elem
}
// Reject primary + duplicated + staged low-code tasks with error; remove connection from registry.
void JSAgentConnection::RejectConnectAndCleanup(napi_env env, napi_value error, bool hasPrimaryTask)
void JSAgentConnection::RejectConnectAndCleanup(napi_env env, napi_value error)
{
if (hasPrimaryTask) {
napiAsyncTask_->Reject(env, error);
// Take ownership so the first settler wins. No lock: all settlers are JS-thread-serialized.
// Do NOT hold stateLock_ — sub-calls re-acquire it (non-recursive mutex).
auto primary = std::move(napiAsyncTask_);
if (primary != nullptr) {
primary->Reject(env, error);
}
RejectDuplicatedPendingTask(env, error);
RejectPendingLowCodeReuseTasks(env, error);
napiAsyncTask_ = nullptr;
AgentConnectionUtils::RemoveAgentConnection(connectionId_);
}
// True when connect must abort: no pending task, or result-code failure (after reject+cleanup).
bool JSAgentConnection::AbortOnConnectError(napi_env env, int resultCode, bool hasPrimaryTask,
bool hasDuplicatedPendingTask)
bool JSAgentConnection::AbortOnConnectError(napi_env env, int resultCode, bool hasDuplicatedPendingTask)
{
if (!hasPrimaryTask && !hasDuplicatedPendingTask) {
if (napiAsyncTask_ == nullptr && !hasDuplicatedPendingTask) {
TAG_LOGD(AAFwkTag::SER_ROUTER, "No pending connect task");
return true;
}
@@ -292,13 +293,13 @@ bool JSAgentConnection::AbortOnConnectError(napi_env env, int resultCode, bool h
}
napi_value error = CreateJsErrorByNativeErr(env, resultCode, "",
AbilityRuntime::GetInnerErrorMsg(AbilityRuntime::AbilityInnerErrorMsg::CONNECT_AGENT_EXTENSION_FAILED));
RejectConnectAndCleanup(env, error, hasPrimaryTask);
RejectConnectAndCleanup(env, error);
return true;
}
// Build the JS receiver proxy for the connected host; nullptr (after reject+cleanup) on creation failure.
napi_value JSAgentConnection::BuildAgentReceiverProxy(napi_env env,
const sptr<IRemoteObject> &remoteObject, bool hasPrimaryTask)
const sptr<IRemoteObject> &remoteObject)
{
sptr<JsAgentConnectorStubImpl> hostStub = GetServiceHostStub();
sptr<IRemoteObject> hostProxy = nullptr;
@@ -313,7 +314,7 @@ napi_value JSAgentConnection::BuildAgentReceiverProxy(napi_env env,
napi_value error = CreateJsErrorByNativeErr(env,
static_cast<int32_t>(AbilityRuntime::AbilityErrorCode::ERROR_CODE_INNER), "",
AbilityRuntime::GetInnerErrorMsg(AbilityRuntime::AbilityInnerErrorMsg::OPERATION_FAILED));
RejectConnectAndCleanup(env, error, hasPrimaryTask);
RejectConnectAndCleanup(env, error);
return nullptr;
}
@@ -321,18 +322,17 @@ void JSAgentConnection::HandleOnAbilityConnectDone(const AppExecFwk::ElementName
const sptr<IRemoteObject> &remoteObject, int resultCode)
{
TAG_LOGI(AAFwkTag::SER_ROUTER, "HandleOnAbilityConnectDone, resultCode: %{public}d", resultCode);
bool hasPrimaryTask = napiAsyncTask_ != nullptr;
bool hasDuplicatedPendingTask = !duplicatedPendingTaskList_.empty();
if (AbortOnConnectError(env_, resultCode, hasPrimaryTask, hasDuplicatedPendingTask)) {
if (AbortOnConnectError(env_, resultCode, hasDuplicatedPendingTask)) {
return;
}
napi_value proxy = BuildAgentReceiverProxy(env_, remoteObject, hasPrimaryTask);
napi_value proxy = BuildAgentReceiverProxy(env_, remoteObject);
if (proxy == nullptr) {
return;
}
SetProxyObject(proxy);
if (hasPrimaryTask) {
if (napiAsyncTask_ != nullptr) {
napiAsyncTask_->ResolveWithNoError(env_, proxy);
}
ResolveDuplicatedPendingTask(env_, proxy);
@@ -391,13 +391,14 @@ void JSAgentConnection::HandleOnAbilityDisconnectDone(const AppExecFwk::ElementN
if (disconnectCompleteHandler_ != nullptr) {
disconnectCompleteHandler_(wptr<JSAgentConnection>(this));
}
if (disconnectAsyncTask_ != nullptr) {
// Take ownership: first settler wins, the other finds nullptr.
auto disconnectTask = TakeDisconnectAsyncTask();
if (disconnectTask != nullptr) {
if (resultCode == static_cast<int32_t>(AbilityRuntime::AbilityErrorCode::ERROR_OK)) {
disconnectAsyncTask_->ResolveWithNoError(env_, CreateJsUndefined(env_));
disconnectTask->ResolveWithNoError(env_, CreateJsUndefined(env_));
} else {
disconnectAsyncTask_->Reject(env_, CreateJsErrorByNativeErr(env_, resultCode));
disconnectTask->Reject(env_, CreateJsErrorByNativeErr(env_, resultCode));
}
disconnectAsyncTask_ = nullptr;
}
// release connect
@@ -429,9 +430,20 @@ void JSAgentConnection::SetNapiAsyncTask(std::shared_ptr<AbilityRuntime::NapiAsy
napiAsyncTask_ = task;
}
void JSAgentConnection::SetDisconnectAsyncTask(const std::shared_ptr<AbilityRuntime::NapiAsyncTask> &task)
bool JSAgentConnection::SetDisconnectAsyncTask(const std::shared_ptr<AbilityRuntime::NapiAsyncTask> &task)
{
std::lock_guard<std::mutex> lock(stateLock_);
if (disconnectAsyncTask_ != nullptr) {
return false; // re-entrant disconnect would orphan the pending promise
}
disconnectAsyncTask_ = task;
return true;
}
std::shared_ptr<AbilityRuntime::NapiAsyncTask> JSAgentConnection::TakeDisconnectAsyncTask()
{
std::lock_guard<std::mutex> lock(stateLock_);
return std::move(disconnectAsyncTask_);
}
void JSAgentConnection::AddDuplicatedPendingTask(std::unique_ptr<AbilityRuntime::NapiAsyncTask> &task)
@@ -535,7 +535,7 @@ void DoConnectAgentExtensionAbility(napi_env env,
GetAgentManagerErrorMsg(innerErrCode, AgentManagerErrorOperation::CONNECT_AGENT_EXTENSION));
// Sync connect failure: reject primary + staged low-code tasks (pendingLowCodeReuseTasks_) + remove
// the connection, else Mechanism-A tasks hang (host never connects -> drain never runs).
connection->RejectConnectAndCleanup(env, error, /*hasPrimaryTask=*/true);
connection->RejectConnectAndCleanup(env, error);
}
}
@@ -1129,28 +1129,38 @@ napi_value JsAgentManager::OnDisconnectAgentExtensionAbility(napi_env env, size_
}
connection->SetDisconnecting(true);
connection->SetDisconnectAsyncTask(disconnectTaskShared);
if (!connection->SetDisconnectAsyncTask(disconnectTaskShared)) {
// A disconnect is already pending; resolve as duplicate to avoid orphaning it.
TAG_LOGI(AAFwkTag::SER_ROUTER, "Disconnect already pending, resolve as duplicate");
disconnectTaskShared->ResolveWithNoError(env, CreateJsUndefined(env));
return result;
}
auto innerErrCode = std::make_shared<int32_t>(ERR_OK);
auto execute = std::make_unique<NapiAsyncTask::ExecuteCallback>(
[connection, innerErrCode]() {
TAG_LOGD(AAFwkTag::SER_ROUTER, "Execute disconnect, connectionId: %{public}s",
std::to_string(connection->GetConnectionId()).c_str());
*innerErrCode = AgentConnectionManager::GetInstance().DisconnectAgentExtensionAbility(connection);
if (*innerErrCode != ERR_OK) {
connection->SetDisconnecting(false);
connection->SetDisconnectAsyncTask(nullptr);
}
// Keep IsDisconnecting true until the JS-thread complete settles (prevents re-entrant overwrite);
// do not null disconnectAsyncTask_ here — it is settled via TakeDisconnectAsyncTask().
});
auto complete = std::make_unique<NapiAsyncTask::CompleteCallback>(
[innerErrCode, disconnectTaskShared, connection](napi_env env, NapiAsyncTask &task, int32_t status) {
[innerErrCode, connection](napi_env env, NapiAsyncTask &task, int32_t status) {
if (*innerErrCode == ERR_OK) {
return;
}
TAG_LOGE(AAFwkTag::SER_ROUTER, "Disconnect failed: %{public}d", *innerErrCode);
disconnectTaskShared->Reject(env,
CreateJsError(env, static_cast<int32_t>(GetJsErrorCodeByNativeError(*innerErrCode)),
GetAgentManagerErrorMsg(*innerErrCode, AgentManagerErrorOperation::DISCONNECT_AGENT_EXTENSION)));
// Take ownership: if already settled by HandleOnAbilityDisconnectDone, Take returns nullptr
// and we skip — no double-settle.
if (auto pending = connection->TakeDisconnectAsyncTask(); pending != nullptr) {
pending->Reject(env,
CreateJsError(env, static_cast<int32_t>(GetJsErrorCodeByNativeError(*innerErrCode)),
GetAgentManagerErrorMsg(
*innerErrCode, AgentManagerErrorOperation::DISCONNECT_AGENT_EXTENSION)));
}
// Reset on the JS thread after settling to keep IsDisconnecting true during the settle window.
connection->SetDisconnecting(false);
DrainReconnectPendingTasksToExistingConnection(env, connection);
});