Ccbloooongfix leak mem
8c56a14e创建于 1月5日历史提交
/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "async_call.h"

#include <algorithm>

#include "global.h"
#include "js_utils.h"

namespace OHOS {
namespace MiscServices {
using namespace std::chrono;
constexpr size_t ARGC_MAX = 6;
constexpr int32_t MAX_WAIT_TIME = 500; // ms
static inline uint64_t GetTimeStamp()
{
    return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
}
AsyncCall::AsyncCall(napi_env env, napi_callback_info info, std::shared_ptr<Context> context, size_t maxParamCount)
    : env_(env)
{
    context_ = new (std::nothrow) AsyncContext();
    if (context_ == nullptr) {
        IMSA_HILOGE("context_  is nullptr");
        return;
    }
    size_t argc = ARGC_MAX;
    napi_value self = nullptr;
    napi_value argv[ARGC_MAX] = { nullptr };
    IMF_CALL_RETURN_VOID(napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
    napi_valuetype valueType = napi_undefined;
    argc = std::min(argc, maxParamCount);
    if (argc > 0) {
        napi_typeof(env, argv[argc - 1], &valueType);
        if (valueType == napi_function) {
            napi_create_reference(env, argv[argc - 1], 1, &context_->callback);
            argc = argc - 1;
        }
    }
    if (context == nullptr) {
        IMSA_HILOGE("context is nullptr!");
        return;
    }
    IMF_CALL_RETURN_VOID((*context)(env, argc, argv, self));
    context_->ctx = std::move(context);
    napi_create_reference(env, self, 1, &context_->self);
}

AsyncCall::~AsyncCall()
{
    if (context_ == nullptr) {
        return;
    }

    DeleteContext(env_, context_);
}

napi_value AsyncCall::Call(napi_env env, Context::ExecAction exec, const std::string &resourceName)
{
    if (context_ == nullptr) {
        IMSA_HILOGE("context_ is nullptr!");
        return nullptr;
    }
    if (context_->ctx == nullptr) {
        IMSA_HILOGE("context_->ctx is nullptr!");
        return nullptr;
    }
    context_->ctx->exec_ = std::move(exec);
    napi_value promise = nullptr;
    if (context_->callback == nullptr) {
        napi_create_promise(env, &context_->defer, &promise);
    } else {
        napi_get_undefined(env, &promise);
    }
    CallImpl(env, context_, resourceName);
    context_ = nullptr;
    return promise;
}

napi_value AsyncCall::Post(napi_env env, Context::ExecAction exec, std::shared_ptr<TaskQueue> queue, const char *func)
{
    if (context_ == nullptr || context_->ctx == nullptr || queue == nullptr) {
        IMSA_HILOGE("context is nullptr!");
        return nullptr;
    }
    context_->ctx->exec_ = std::move(exec);
    napi_value promise = nullptr;
    if (context_->callback == nullptr) {
        napi_create_promise(env, &context_->defer, &promise);
    } else {
        napi_get_undefined(env, &promise);
    }
    napi_async_work work = context_->work;
    napi_value resource = nullptr;
    napi_create_string_utf8(env, func, NAPI_AUTO_LENGTH, &resource);
    napi_create_async_work(env, nullptr, resource, AsyncCall::OnExecuteSeq, AsyncCall::OnComplete, context_, &work);
    context_->work = work;
    context_->queue = queue;
    std::unique_lock<ffrt::mutex> lock(queue->queuesMutex_);
    queue->taskQueue_.emplace(env, work, func);
    if (!queue->isRunning) {
        auto status = napi_queue_async_work_with_qos(env, work, napi_qos_user_initiated);
        queue->isRunning = status == napi_ok;
        if (status != napi_ok) {
            IMSA_HILOGE("async work failed.status:%{public}d, func:%{public}s!", status, func);
        }
    }
    context_ = nullptr;
    return promise;
}

napi_value AsyncCall::SyncCall(napi_env env, AsyncCall::Context::ExecAction exec)
{
    if ((context_ == nullptr) || (context_->ctx == nullptr)) {
        IMSA_HILOGE("context_ or context_->ctx is nullptr!");
        return nullptr;
    }
    context_->ctx->exec_ = std::move(exec);
    napi_value promise = nullptr;
    if (context_->callback == nullptr) {
        napi_create_promise(env, &context_->defer, &promise);
    } else {
        napi_get_undefined(env, &promise);
    }
    AsyncCall::OnExecute(env, context_);
    AsyncCall::OnComplete(env, context_->ctx->status_, context_);
    return promise;
}

void AsyncCall::OnExecute(napi_env env, void *data)
{
    AsyncContext *context = reinterpret_cast<AsyncContext *>(data);
    if (context == nullptr || context->ctx == nullptr) {
        IMSA_HILOGE("context or context->ctx is nullptr!");
        return;
    }
    context->ctx->Exec();
}

void AsyncCall::OnExecuteSeq(napi_env env, void *data)
{
    OnExecute(env, data);
    AsyncContext *context = reinterpret_cast<AsyncContext *>(data);
    if (context == nullptr || context->queue == nullptr) {
        IMSA_HILOGE("context or context->queue is nullptr!");
        return;
    }
    auto queue = context->queue;
    std::unique_lock<ffrt::mutex> lock(queue->queuesMutex_);
    if (!queue->taskQueue_.empty()) {
        queue->taskQueue_.pop();
    }
    queue->isRunning = !queue->taskQueue_.empty() &&
                       napi_queue_async_work_with_qos(queue->taskQueue_.front().env,
                           queue->taskQueue_.front().work, napi_qos_user_initiated) == napi_ok;
}

void AsyncCall::OnComplete(napi_env env, napi_status status, void *data)
{
    JsUtil::ScopeGuard scopeGuard(env);
    AsyncContext *context = reinterpret_cast<AsyncContext *>(data);
    napi_value output = nullptr;
    if (context == nullptr || context->ctx == nullptr) {
        IMSA_HILOGE("context or context->ctx is nullptr!");
        return;
    }
    napi_status runStatus = (*context->ctx)(env, &output);
    napi_value result[ARG_BUTT] = { 0 };
    if (status == napi_ok && runStatus == napi_ok) {
        napi_get_undefined(env, &result[ARG_ERROR]);
        if (output != nullptr) {
            IMSA_HILOGD("output != nullptr!");
            result[ARG_DATA] = output;
        } else {
            IMSA_HILOGD("output is nullptr!");
            napi_get_undefined(env, &result[ARG_DATA]);
        }
    } else {
        IMSA_HILOGE("failed, [status:%{public}d, runStatus:%{public}d, errorCode:%{public}d, errMessage:%{public}s].",
            status, runStatus, context->ctx->errorCode_, context->ctx->errMessage_.c_str());
        result[ARG_ERROR] = JsUtils::ToError(env, context->ctx->errorCode_, context->ctx->errMessage_);
        napi_get_undefined(env, &result[ARG_DATA]);
    }
    if (context->defer != nullptr) {
        if (status == napi_ok && runStatus == napi_ok) {
            napi_resolve_deferred(env, context->defer, result[ARG_DATA]);
        } else {
            napi_reject_deferred(env, context->defer, result[ARG_ERROR]);
        }
    } else {
        napi_value callback = nullptr;
        napi_get_reference_value(env, context->callback, &callback);
        napi_value returnValue;
        napi_call_function(env, nullptr, callback, ARG_BUTT, result, &returnValue);
    }
    DeleteContext(env, context);
}

void AsyncCall::DeleteContext(napi_env env, AsyncContext *context)
{
    if (context == nullptr) {
        IMSA_HILOGE("context is nullptr!");
        return;
    }
    if (env != nullptr) {
        napi_delete_reference(env, context->callback);
        napi_delete_reference(env, context->self);
        napi_delete_async_work(env, context->work);
    }
    delete context;
}

AsyncCall::InnerTask::InnerTask(napi_env env, napi_async_work work, const char *name)
    : env(env), work(work), name(name), startTime(GetTimeStamp())
{
}

AsyncCall::InnerTask::~InnerTask()
{
    auto endTime = GetTimeStamp();
    if (startTime > endTime) {
        IMSA_HILOGE("startTime:%{public}" PRIu64 ", endTime:%{public}" PRIu64, startTime, endTime);
        return;
    }
    if (endTime - startTime > MAX_WAIT_TIME) {
        IMSA_HILOGW("async work timeout! func:%{public}s, startTime:%{public}" PRIu64 ", endTime:%{public}" PRIu64
                    ", cost:%{public}" PRIu64 "ms",
            name, startTime, endTime, endTime - startTime);
    } else {
        IMSA_HILOGD("async work finished! func:%{public}s, startTime:%{public}" PRIu64 ", endTime:%{public}" PRIu64
                    ", cost:%{public}" PRIu64 "ms",
            name, startTime, endTime, endTime - startTime);
    }
}

napi_value AsyncCall::Call(napi_env env, Context::AsynExecAction exec, const std::string &resourceName)
{
    if ((context_ == nullptr) || (context_->ctx == nullptr)) {
        IMSA_HILOGE("context_ or context_->ctx is nullptr!");
        return nullptr;
    }
    context_->ctx->asyncExec_ = std::move(exec);
    napi_value promise = nullptr;
    if (context_->callback == nullptr) {
        napi_create_promise(env, &context_->defer, &promise);
    } else {
        napi_get_undefined(env, &promise);
    }
    CallImpl(env, context_, resourceName);
    context_ = nullptr;
    return promise;
}

void AsyncCall::OnExecuteAsync(napi_env env, AsyncContext *context, Context::CallBackAction cb)
{
    if (context == nullptr || context->ctx == nullptr) {
        IMSA_HILOGE("context or context->ctx is nullptr!");
        return;
    }

    context->ctx->AsyncExec(cb);
}

void AsyncCall::CallImpl(napi_env env, AsyncContext *context, const std::string &resourceName)
{
    if (context == nullptr) {
        IMSA_HILOGE("context is nullptr!");
        return;
    }
    napi_async_work work = context->work;
    napi_value resource = nullptr;
    std::string name = "IMF_" + resourceName;
    napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &resource);
    napi_create_async_work(env, nullptr, resource, AsyncCall::OnExecute, AsyncCall::OnComplete, context, &work);
    context->work = work;
    napi_queue_async_work_with_qos(env, work, napi_qos_user_initiated);
}
} // namespace MiscServices
} // namespace OHOS