Thread and watchdog mechanism in ArkUI

I. Introduction

This article mainly analyzes the thread and watchdog mechanism involved in ArkUI.

2. Threads in ArkUI

The process of creating an interface for the first time by applying Ability is roughly as follows:

illustrate:

• AceContainer is a container class, which is composed of front end, task executor, resource manager, rendering pipeline, view, etc. It provides various capabilities of life cycle docking, function scheduling interface and UI rendering.

• Ability is actually AceAbility in the FA model, which is matched with the management interface of the AceContainer container class. Create an AceContainer instance in AceAbility's lifecycle function AceAbility::OnStart(const Want& want).

• For the Stage model, create an AceContainer instance in the UIContentImpl::CommonInitialize() function. AceContainer creates a task executor in the constructor to execute ArkUI related tasks.

void AceContainer::InitializeTask()
{
    auto flutterTaskExecutor = Referenced::MakeRefPtr<FlutterTaskExecutor>();
    flutterTaskExecutor->InitPlatformThread(useCurrentEventRunner_);
    taskExecutor_ = flutterTaskExecutor;
    // No need to create JS Thread for DECLARATIVE_JS
    if (type_ == FrontendType::DECLARATIVE_JS) {
        GetSettings().useUIAsJSThread = true;
    } else {
        flutterTaskExecutor->InitJsThread();
    }
}

There are several types of tasks as follows, and each type of task (except Background tasks) will be executed by a fml::TaskRunner. The TaskRunner code is in the third-party library third_party\flutter\engine\flutter\common\task_runners.h. The implementation principle is similar to that of EventRunner and EventHandler.

enum class TaskType : uint32_t {
        PLATFORM = 0,
        UI,
        IO,
        GPU,
        JS,
        BACKGROUND,
        UNKNOWN,
    };

The FlutterTaskExecutor class diagram is as follows:

illustrate:

• The task executor can be used to execute asynchronous (PostTask) and synchronous (PostSyncTask) tasks.

• Asynchronous task: throw the task to the specified type of thread for processing without blocking the current thread.

• Synchronous task: throw the task to the specified type of thread for processing and block the current thread until the task is executed and continue the current thread.

• For example, the processing of touch events will be thrown to the UI thread for processing in the form of asynchronous tasks.

 auto&& touchEventCallback = [context = pipelineContext_, id = instanceId_](
                                    const TouchEvent& event, const std::function<void()>& markProcess) {
        ContainerScope scope(id);
        context->GetTaskExecutor()->PostTask(
            [context, event, markProcess]() {
                context->OnTouchEvent(event);
                CHECK_NULL_VOID_NOLOG(markProcess);
                markProcess();
            },
            TaskExecutor::TaskType::UI);
    };

3. How to initialize various types of TaskRunner?

1. platformRunner_

Initialize in the InitPlatformThread function.

void FlutterTaskExecutor::InitPlatformThread(bool useCurrentEventRunner)
{
#ifdef OHOS_STANDARD_SYSTEM
    platformRunner_ = flutter::PlatformTaskRunner::CurrentTaskRunner(useCurrentEventRunner);
#else
    fml::MessageLoop::EnsureInitializedForCurrentThread();
    platformRunner_ = fml::MessageLoop::GetCurrent().GetTaskRunner();
#endif

    FillTaskTypeTable(TaskType::PLATFORM);
}

For standard OHOS, platformRunner_ is actually

flutter::PlatformTaskRunner::CurrentTaskRunner(useCurrentEventRunner)

Look at the specific implementation:

fml::RefPtr<fml::TaskRunner> PlatformTaskRunner::CurrentTaskRunner(bool useCurrentEventRunner)
{
    return PlatformTaskRunnerAdapter::CurrentTaskRunner(useCurrentEventRunner);
}
fml::RefPtr<fml::TaskRunner> PlatformTaskRunnerAdapter::CurrentTaskRunner(bool useCurrentEventRunner)
{
    if (useCurrentEventRunner) {
        return fml::MakeRefCounted<PlatformTaskRunnerAdapter>(useCurrentEventRunner);
    }
    if (taskRunner_) {
        return taskRunner_;
    }
    taskRunner_ = fml::MakeRefCounted<PlatformTaskRunnerAdapter>(useCurrentEventRunner);
    return taskRunner_;
}

illustrate:

The actual type of platformRunner is PlatformTaskRunnerAdapter.

PlatformTaskRunnerAdapter inherits from fml::TaskRunner and implements interface functions such as virtual void PostTask(fml::closure task). In fact, layer encapsulation is done on the basis of EventRunner and EventHandler mechanisms. The actual parameter of useCurrentEventRunner in the code is false. It means that platformRunner actually throws the task to the main thread to do it. (The thread corresponding to MainEventRunner is the main thread, and the initialization of MainEventRunner is in the MainThread::Start() function of the Ability framework)

PlatformTaskRunnerAdapter::PlatformTaskRunnerAdapter(bool useCurrentEventRunner)
    : fml::TaskRunner(nullptr)
{
    if (useCurrentEventRunner) {
        eventRunner_ = OHOS::AppExecFwk::EventRunner::Current();
    } else {
        eventRunner_ = OHOS::AppExecFwk::EventRunner::GetMainEventRunner();
    }
    eventHandler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(eventRunner_);
}

void PlatformTaskRunnerAdapter::PostTask(fml::closure task)
{
    eventHandler_->PostTask(std::move(task));
}

2. uiRunner, ioRunner, gpuRunner_

These three types of TaskRunner initialization are in the FlutterTaskExecutor::InitOtherThreads function.

void FlutterTaskExecutor::InitOtherThreads(const flutter::TaskRunners& taskRunners)
{
    uiRunner_ = taskRunners.GetUITaskRunner();
    ioRunner_ = taskRunners.GetIOTaskRunner();
#ifdef NG_BUILD
    gpuRunner_ = taskRunners.GetRasterTaskRunner();
#else
    gpuRunner_ = taskRunners.GetGPUTaskRunner();
#endif

    //...此处省略若干行
}

Where does the parameter taskRunners of the FlutterTaskExecutor::InitOtherThreads function come from?

Some configuration items are initialized in the FlutterAceView::CreateView() function, and then a flutter::OhosShellHolder object is created.

FlutterAceView* FlutterAceView::CreateView(int32_t instanceId, bool useCurrentEventRunner, bool usePlatformThread)
{
    FlutterAceView* aceSurface = new Platform::FlutterAceView(instanceId);
    if (aceSurface != nullptr) {
        aceSurface->IncRefCount();
    }
    flutter::Settings settings;
    settings.instanceId = instanceId;
    settings.platform = flutter::AcePlatform::ACE_PLATFORM_OHOS;
#ifndef GPU_DISABLED
    settings.enable_software_rendering = false;
#else
    settings.enable_software_rendering = true;
#endif
#ifdef ENABLE_ROSEN_BACKEND
    settings.use_system_render_thread = SystemProperties::GetRosenBackendEnabled();
#endif
    settings.platform_as_ui_thread = usePlatformThread;
    settings.use_current_event_runner = useCurrentEventRunner;
    // ...此处省略若干行
    auto shell_holder = std::make_unique<flutter::OhosShellHolder>(settings, false);
    if (aceSurface != nullptr) {
        aceSurface->SetShellHolder(std::move(shell_holder));
    }
    return aceSurface;
}

The OhosShellHolder constructor will create flutter::TaskRunners according to the parameters passed in.

OhosShellHolder::OhosShellHolder(
    flutter::Settings settings,
    bool is_background_view)
    : settings_(std::move(settings))
{
  // ...此处省略若干行
  // The current thread will be used as the platform thread. Ensure that the
  // message loop is initialized.
  fml::MessageLoop::EnsureInitializedForCurrentThread();
  fml::RefPtr<fml::TaskRunner> gpu_runner;
  fml::RefPtr<fml::TaskRunner> ui_runner;
  fml::RefPtr<fml::TaskRunner> io_runner;
  fml::RefPtr<fml::TaskRunner> platform_runner =
    PlatformTaskRunnerAdapter::CurrentTaskRunner(settings_.use_current_event_runner);
  if (is_background_view) {
    auto single_task_runner = thread_host_.ui_thread->GetTaskRunner();
    gpu_runner = single_task_runner;
    ui_runner = single_task_runner;
    io_runner = single_task_runner;
  } else {
    if (settings_.platform_as_ui_thread) {
      ui_runner = platform_runner;
    } else {
      ui_runner = thread_host_.ui_thread->GetTaskRunner();
    }
    if (!settings_.use_system_render_thread) {
      gpu_runner = thread_host_.gpu_thread->GetTaskRunner();
    } else {
      gpu_runner = ui_runner;
    }
    if (settings_.use_io_thread) {
      io_runner = thread_host_.io_thread->GetTaskRunner();
    } else {
      io_runner = ui_runner;
    }
  }
  flutter::TaskRunners task_runners(thread_label,     // label
                                    platform_runner,  // platform
                                    gpu_runner,       // gpu
                                    ui_runner,        // ui
                                    io_runner         // io
  );

Note: Currently on OHOS, the configuration parameters are as follows:

Compared with the above code segment, the actual gpu_runner, ui_runner, and io_runner are the same, and the tasks are all executed on the UI thread. In addition, for the Stage model, ui_runner and platform_runner are the same, so for the Stage model, TaskType::UI, TaskType::IO, TaskType::GPU, TaskType::PLATFORM type tasks are actually executed by the main thread of.

3. jsRunner_

Initialization is in the FlutterTaskExecutor::InitJsThread(bool newThread) function.

void FlutterTaskExecutor::InitJsThread(bool newThread)
{
    if (newThread) {
        jsThread_ = std::make_unique<fml::Thread>(GenJsThreadName());
        jsRunner_ = jsThread_->GetTaskRunner();
    } else {
        jsRunner_ = uiRunner_;
    }

    PostTaskToTaskRunner(
        jsRunner_, [weak = AceType::WeakClaim(this)] { FillTaskTypeTable(weak, TaskType::JS); }, 0);
}

Explanation: For the declarative front end, the newThread parameter is false; for the JS front end, it is true. Therefore, the declarative front-end JS thread is actually a UI thread; for the JS front-end, an independent JS thread will be set up to handle JS-related tasks.

4. How to execute tasks of type TaskType::BACKGROUND?

Tasks of type TaskType::BACKGROUND will be executed by the singleton BackgroundTaskExecutor. A thread pool of 8 threads is maintained in BackgroundTaskExecutor to handle background time-consuming operations. Thread names start with "ace.bg." For example, when RosenFontLoader loads web fonts, the download operation will be done in the background task thread.

void RosenFontLoader::LoadFromNetwork(const OHOS::Ace::RefPtr<OHOS::Ace::PipelineBase>& context)
{
    auto weakContext = AceType::WeakClaim(AceType::RawPtr(context));
    context->GetTaskExecutor()->PostTask(
        [weak = AceType::WeakClaim(this), weakContext] {
            auto fontLoader = weak.Upgrade();
            auto context = weakContext.Upgrade();
            if (!fontLoader || !context) {
                return;
            }
            std::vector<uint8_t> fontData;
            if (!DownloadManager::GetInstance().Download(fontLoader->familySrc_, fontData) || fontData.empty()) {
                return;
            }
            //...此处省略若干行
        },
        TaskExecutor::TaskType::BACKGROUND);
}

To sum up: In ArkUI, an AceContainer will be created for each Ability with an interface, and a FlutterTaskExecutor will be created in each AceContainer to handle tasks related to the Ability ArkUI. According to different models, the threads created by ArkUI will be different:

• For Stage model applications, the ui thread reuses the main thread, and Stage model applications are currently declarative front-ends, causing the js thread to reuse the ui thread. So ArkUI only needs to create eight additional background task threads whose names start with "ace.bg."

• For the application of the FA model, in addition to the eight background task threads, several threads whose names end with ".ui" will be created according to the number of Abilities. If it is a JS front end, several threads whose names start with "jsThread-" will be created.

4. Watchdog in ArkUI

AceEngine is a singleton and globally unique. A WatchDog instance is created in the constructor of AceEngine. For the application of the FA model, the AceContainer::AttachView() function calls

AceEngine::Get().RegisterToWatchDog(instanceId, taskExecutor_,

GetSettings().useUIAsJSThread);

Register the held FlutterTaskExecutor to watchdog.

The watchdog only guards the UI thread and JS thread in FlutterTaskExecutor. The application of the Stage model does not need to be guarded in ArkUI because the UI thread and the JS thread are actually the main thread for multiplexing. There is a watchdog in the Ability framework to guard the main thread. If there is a task processing in the thread for more than 3s, the system event corresponding to RawEventType::WARNING will be reported to the hiview plug-in platform; if the task processing exceeds 5s, the system event corresponding to RawEventType::FREEZE will be reported to the hiview plug-in platform, hiview plug-in platform A dump file of appfreeze will be generated.

In order to prevent the main thread and UI thread from being stuck and cause appfreeze, when doing application development, do not perform time-consuming operations in callback functions such as Ability lifecycle functions or control click events.

Guess you like

Origin blog.csdn.net/OpenHarmony_dev/article/details/129547691