Environment: Flutter SDK v1.5.4-hotfix.1@stable

Corresponding flutter engine: 52 c7a1e849a170be4b2b2fe34142ca2c0a6fea1f

The focus here is on the thread representation of flutter at the C++ layer, not the thread representation at the dart layer

Thread creation

The underlying Thread of flutter (C++) (FML ::Thread) is closely associated with the message loop. That is, each FML ::Thead instance creates an instance of the message loop. Therefore, an FML ::Thread should not be used to create a naked Thread. FML ::Thread internally holds a Thread object using C++11’s STD ::Thread. See the FML ::Thread constructor (thread.cc:25).

The thread runner does two things

  1. Create message loop instances and associate threadsfml::Threadobject
  2. Of the message loopTaskRunnerObject and assign it to the threadfml::ThreadThat is, the thread also holds oneTaskRunnerThe instance

The implementation of FML ::Thread is very simple, but the key is to look at its associated FML ::MessageLoop.

Thread storage

MessageLoop FML ::MessageLoop first uses thread storage to hold a callback. This callback explicitly frees an FML ::MessageLoop memory object.

FML ::ThreadLocal is a thread storage class. The value it stores is an object of type INTPtr_t. FML ::ThreadLocal is implemented differently on different platforms

  1. The class Linux platform uses library functions for pThreadpthread_key_createTo generate a key key that identifies the thread, the value of which is a helper classBoxIt’s savedintptr_tObject and the callback method passed inThreadLocalDestroyCallback.ThreadLocalThe keywords that need to be declared before use arestaticThe order of object destructor is slightly convoluted, and the sequence of destructor calls is as follows:
ThreadLocal::~ThreadLocal()
  ThreadLocal::Box::~Box()
  pthread_key_delete(_key)
    ThreadLocal::ThreadLocalDestroy
        ThreadLocal::Box::DestroyValue
          ThreadLocalDestroyCallback() => [](intptr_t value) {}
            MessageLoop::~MessageLoop()
        ThreadLocal::Box::~Box()
Copy the code

Cc :27 delete is redundant.

  1. The Windows platformThreadLocalC++11 standard keywords are used directly before usethread_local.

Message loop

A message loop is an asynchronous processing model. It blocks the current thread when there is no message to save CPU resources. Otherwise, idling in a polling mode is a waste of CPU resources.

Associated with the thread

See store threads, then in creating FML: : the MessageLoop: when the Thread objects: EnsureInitializedForCurrentThread is very obvious (name though somewhat cumbersome), whether the current Thread to create a message loop object, if not then create and save. So the message loop is associated with the thread, by what? Tls_message_loop This thread storage class object.

The message queue

MessageLoopImpl ::delayed_tasks_ is the actual message queue, which is thread-safe by the mutex_ mutex variable delayed_tasks_mutex_. It looks like a bit of a drag. In fact, it uses a priority queue to insert by execution point, and if the execution point is the same, inserts by FIFO rules.

The queue element is an internal class DelayedTask, which contains the message execution body Task and the execution time target_time. Order is actually used for sorting.

Cycle to achieve

The MessageLoop object constructor creates two important instances, MessageLoopImpl and FML ::TaskRunner, which in turn reference MessageLoopImpl inside FML ::TaskRunner. MessageLoopImpl::Create() creates the body of the MessageLoop corresponding to different platforms, so the relationship between MessageLoop and MessageLoopImpl is clear: MessageLoop is the shell of MessageLoopImpl or the agent of MessageLoop. MessageLoopImpl is an unexposed, platform-related object that truly realizes message reading and processing.

MessageLoopImpl: : Run, the Terminate, WakeUp is pure virtual function, by the platform, For example, the implementation of Android platform MessageLoopAndroid calls ALooper_pollOnce, while MessageLoopLinux calls epoll_wait, a Blocking function of Linux.

The classes and methods involved here are a bit convoluted, but the goal is simple: the operation of reading and processing messages is uniform, but the way threads wake up or block allows platform differences

Send a message

A message loop is associated with a TaskRunner, and the TaskRunner looks at the implementation and finds all the MessageLoopImpl methods, and then contacts the TaskHost created in the AndroidShellHolder constructor. A TaskRunner simply sends messages to a specified message loop, and a message loop is associated with a Thread (FML ::Thread), thus sending messages to a specified Thread (yes, interthread communication). TaskRunner is statement became a thread-safe objects (FML: : RefCountedThreadSafe < TaskRunner >)

So it all sort of comes together: FML ::TaskRunner is similar to android.os.Handler, FML :: Closure is similar to Runnable, FML ::TaskRunner continuously adds various FML :: Closure objects to the message queue and sets the message loop to wake up and execute at a specified point in time.

End of the thread

The FML ::Thread destructor calls its own Join method, which is a bit awkward at first, but the intent is that the calling Thread needs to wait synchronously for the called Thread to finish. The name is not as concise as Exit. The Join method asynchronously sends a request to Terminate the MessageLoop (MessageLoop::GetCurrent().terminate ()), and then blocks until it finishes. Combine the above to list the call sequence for thread exit:

Thread::~Thread() Thread::Join() TaskRunner::PostTask() ... [asynchronous] MessageLoop: : the Terminate () MessageLoopImpl: : DoTerminate MessageLoopImpl: () : the Terminate () = > MessageLoopAndroid::Terminate() ALooper_wake() ... [asynchronous, Function to return] MessageLoopImpl: : Run () = > MessageLoopAndroid: : Run () MessageLoopImpl: : RunExpiredTasksNow () MessageLoopImpl::DoRun() MessageLoop::Run() ... [async] ThreadLocal::~ThreadLocal() [omitted, same as thread storage object destructor call sequence]Copy the code

Thread system

Look back at the constructors of AndroidShellHolder, which involve a flutter::ThreadHost, FML ::TaskRunner, a flutter::TaskRunners, It also creates a series of threads before the Shell object is created: UI_thread, gpu_thread, io_thread, and a series of operations on TaskRunner, which is a bit messy but now very clear.

The thread currently executing the AndroidShellHolder constructor creates a message loop (android_shell_holer.cc :81) and assigns the TaskRunner of the message loop to platform_runner (note: Platform_thread object is not created. The other TaskRunners are TaskRunner objects for the created FML ::Thread Thread.

This raises the question: when a thread sends an asynchronous request through platform_runner, when is it executed?