current position:Home>Familiar and unfamiliar handler-3

Familiar and unfamiliar handler-3

2022-01-27 03:51:04 AaronOhOhOh

This is my participation 8 The fourth of the yuegengwen challenge 5 God , Check out the activity details :8 Yuegengwen challenge

Familiar and strange Handler-3

Continued above :

Familiar and strange Handler-1

Familiar and strange Handler-2

nativeInit:

In the above , About Handler Three piece set creation process , The first involves JNI The call is MessageQueue Of nativeInit Method .

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}
 Copy code 

The concrete realization is framework/base/core/jni/android_os_MessageQueue.cpp in :

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    //  First create NativeMessageQueue object , The object holds Native On the side Looper object 
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    // If the creation fails , Direct anomaly 
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }
    //  increase NativeMessageQueue Reference count of 
    nativeMessageQueue->incStrong(env);
    //  return nativeMessageQueue This pointer is for Java layer 
    return reinterpret_cast<jlong>(nativeMessageQueue);
}
 Copy code 

stay NativeMessageQueue In the constructor of :

NativeMessageQueue::NativeMessageQueue() :
    mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    //  adopt TLS(Thread Local Storage Thread local storage ) Gets the current thread's Native Of Looper object 
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        //  without , Then I will create a Native Of Looper object 
        mLooper = new Looper(false);
        //  And will create Looper Object to save to TLS in 
        Looper::setForThread(mLooper);
    }
}
 Copy code 

You need to pay attention to the Looper Objects and Java Layer of Looper Objects are not directly related . In its construction method , The two most relevant things :

Looper::Looper(bool allowNonCallbacks)
     : mAllowNonCallbacks(allowNonCallbacks),
      mSendingMessage(false),
      mPolling(false),
      mEpollRebuildRequired(false),
      mNextRequestSeq(0),
      mResponseIndex(0),
      mNextMessageUptime(LLONG_MAX) {
    //  Initialize the file descriptor representing the wake-up event mWakeEventFd
    // eventfd This system call is used to create or open a eventfd The file of , Like a document open operation 
    //  The initial value passed in here is 0, Then set the flag bit to 
    // EFD_CLOEXEC:FD_CLOEXEC, In short, it is fork Child processes do not inherit , There is nothing wrong with setting this value for multithreaded programs .
    // EFD_NONBLOCK:
    //  The file will be set to O_NONBLOCK( Non blocking IO, When the data cannot be read or the write buffer is full return -1),
    //  Instead of blocking calls , Generally, you need to set .
    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
    AutoMutex _l(mLock);
    //  Recreate the current Looper Of Epoll event 
    rebuildEpollLocked();
}
 Copy code 

rebuildEpollLocked The implementation is as follows :

void Looper::rebuildEpollLocked() {
    //  If at present Looper Already there. EpollFd, That is, there are old epoll example , Then reset it first 
    if (mEpollFd >= 0) {
        mEpollFd.reset();
    }
    //  Create a new Epoll example 
    mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
    struct epoll_event eventItem;
    //  initialization eventItem Occupied memory space 
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    // EPOLLIN : Indicates that the corresponding file descriptor can be read 
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeEventFd.get();
    //  call epoll_ctl operation mEpollFd Corresponding Epoll example , take mWakeEventFd( Wake Events )
    //  Add to mEpoll Corresponding epoll For instance 
    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
    for (size_t i = 0; i < mRequests.size(); i++) {
        const Request& request = mRequests.valueAt(i);
        struct epoll_event eventItem;
        request.initEventItem(&eventItem);
        //  take Request Also added to epoll For instance 
        int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
        if (epollResult < 0) {
            ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
                  request.fd, strerror(errno));
        }
    }
}
 Copy code 

epoll_event The structure is as follows :

struct epoll_event {
  uint32_t events;
  epoll_data_t data;
}

typedef union epoll_data {
  void* ptr;
  int fd;
  uint32_t u32;
  uint64_t u64;
} epoll_data_t;
 Copy code 

events Member variables : It can be a collection of the following macros :

  • EPOLLIN : Indicates that the corresponding file descriptor can be read ( Including the end SOCKET Normally shut down );
  • EPOLLOUT: Indicates that the corresponding file descriptor can write ;
  • EPOLLPRI: Indicates that the corresponding file descriptor has urgent data readability ( This should indicate that there is out-of-band data coming in );
  • EPOLLERR: Indicates that there is an error in the corresponding file descriptor ;
  • EPOLLHUP: Indicates that the corresponding file descriptor is suspended ;
  • EPOLLET: take EPOLL Set edge trigger (Edge Triggered) Pattern , This is relative to the horizontal trigger (Level Triggered) Speaking of .
  • EPOLLONESHOT: Listen for only one event , After listening to this event , If you need to keep listening to this socket Words , I have to do this again socket Add to EPOLL In the queue .

since Java Layer of MessageQueue At creation time , Created Native Layer of MessageQueue, So the same Java layer MQ At the time of destruction , It will also trigger NativeMessageQueue Destruction of ,Native layer MQ The destruction of is relatively simple , In essence Native Clear an object in the layer :

  1. Remove the reference relationship of the object .
  2. delete Call to clear the memory space of the object .

nativeWake:

From the above analysis , We know , stay Java Layer when we call MessageQueue.enqueueMessage When , stay Java When the layer feels the need to wake up the message queue , Would call nativeWake This native Method :

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    //  call NativeMessageQueue Of wake Method .
    nativeMessageQueue->wake();
}
 Copy code 

NativeMessageQueue Of wake The way is to adjust Native Looper Of wake Method :

void Looper::wake() {
    uint64_t inc = 1;
    //  To the pipe mWakeEventFd Write characters 1
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
                             mWakeEventFd.get(), nWrite, strerror(errno));
        }
    }
}
 Copy code 

TEMP_FAILURE_RETRY The function of this macro expression is , Evaluate the passed in expression , When the passed in expression evaluates to -1, It means failure , When the expression returns a value -1 And the error code is set to EINITR(4), Then he'll keep trying , Until success .

nativePollOnce:

From the above analysis , We know , stay Java Layer message queue processing Message Before , Will call first nativePollOnce, Handle Native Layer messages :

nativePollOnce(ptr, nextPollTimeoutMillis);
 Copy code 

ptr It was before Native Created by layer MessageQueue Of “ The pointer ”,nextPollTimeoutMillis Indicates the time when the next message will be taken out .

stay android_os_MessageQueue.cpp in ,nativePollOnce The implementation is as follows :

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    //  take Java The layer passed mPtr Convert to NativeMessageQueue The pointer 
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    //  call nativeMessageQueue Of pollOnce Method 
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
 Copy code 

pollOnce:

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    // ...
    //  Call to Looper Of pollOnce Method 
    mLooper->pollOnce(timeoutMillis);
    // ...
}
 Copy code 

Looper Of pollOnce The ultimate realization is system/core/libutils/Looper.cpp:

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        //  First deal with Native Layer of Response
        while (mResponseIndex < mResponses.size()) {
            const Response& response = mResponses.itemAt(mResponseIndex++)
            //  When ident>=0 When , It means that there is no callback
            int ident = response.request.ident;
            if (ident >= 0) {
                int fd = response.request.fd;
                int events = response.events;
                void* data = response.request.data;
                if (outFd != nullptr) *outFd = fd;
                if (outEvents != nullptr) *outEvents = events;
                if (outData != nullptr) *outData = data;
                return ident;
            }
        }
        //  If there is result, Then quit 
        if (result != 0) {
            if (outFd != nullptr) *outFd = 0;
            if (outEvents != nullptr) *outEvents = 0;
            if (outData != nullptr) *outData = nullptr;
            return result;
        }
        //  call pollInner
        result = pollInner(timeoutMillis);
    }
}
 Copy code 

Response and Request The structure of is as follows :

    struct Request {
        // request Associated file descriptor 
        int fd;
        // requestId, When it comes to POLL_CALLBACK(-2) When , Express callback
        int ident;
        int events;
        int seq;
        // request Processing callbacks for 
        sp<LooperCallback> callback;
        void* data;
        void initEventItem(struct epoll_event* eventItem) const;
    };
    struct Response {
        int events;
        Request request;
    };
 Copy code 

Looper::pollInner The implementation is as follows , The internal first is to call epoll_wait This blocking method , obtain Epoll Number of events , Then according to this number ,

int Looper::pollInner(int timeoutMillis) {
    // Adjust the timeout based on when the next message is due.
    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
        if (messageTimeoutMillis >= 0
                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
            timeoutMillis = messageTimeoutMillis;
        }
    }

    // Poll.
    int result = POLL_WAKE;
    //  The above analysis is known , stay pollInner Before being called ,mResponses It's all handled 
    mResponses.clear();
    mResponseIndex = 0;
    //  begin in a minute epoll polling .
    mPolling = true;
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    //  wait for epoll_wait System call return , return timeoutMillis Time file descriptor mEpollFd What happened on epoll Number of events 
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    //  End of poll , Let's start processing the received events .
    mPolling = false;
    // Acquire lock.
    mLock.lock();

    //  For example, something unusual happened , Need to recreate Epoll Mechanism 
    if (mEpollRebuildRequired) {
        mEpollRebuildRequired = false;
        rebuildEpollLocked();
        goto Done;
    }

    //  When epoll The number of events is less than 0 When , That is, an exception is considered to have occurred , Jump to Done Continue to execute 
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        result = POLL_ERROR;
        goto Done;
    }

    //  When epoll Event equals 0 When , Indicates that polling timed out , Jump directly to Done Continue to execute 
    if (eventCount == 0) {
        result = POLL_TIMEOUT;
        goto Done;
    }
    //  Start loop traversal , Deal with all event 
    for (int i = 0; i < eventCount; i++) {
        //  Get the of an event FD
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        //  If it's a wake-up event 
        if (fd == mWakeEventFd.get()) {
            if (epollEvents & EPOLLIN) {
                //  It has been awakened at this time , Read and empty the data in the pipeline 
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
            //  Through file descriptors , Find the corresponding Request Indexes 
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
                //  The corresponding request Encapsulated into Response Object and push To mRequests This sum Vector in 
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
Done: ;
    // Response All the events have been handled , Next processing Native Of Message event .
    mNextMessageUptime = LLONG_MAX;
    // mMessageEnvelopes It's a Vector,MessageEnvelopes As the name suggests, the message envelope 
    //  Encapsulates the Message and MessageHandler object 
    // Message A message ,MessageHandler Defined a handleMessage Method 
    //  By calling Looper::sendMessageXX You can send a message Native Message
    while (mMessageEnvelopes.size() != 0) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
        if (messageEnvelope.uptime <= now) {
            //  It's time for a message to be processed , Then remove and execute 
            //  Corresponding MessageHandler Of handleMessage Method .
            { // obtain handler
                sp<MessageHandler> handler = messageEnvelope.handler;
                Message message = messageEnvelope.message;
                mMessageEnvelopes.removeAt(0);
                mSendingMessage = true;
                mLock.unlock();
                handler->handleMessage(message);
            } // release handler
            mLock.lock();
            mSendingMessage = false;
            result = POLL_CALLBACK;
        } else {
            //  If the message at the head of the queue has not reached the time to be processed 
            //  Then the queue needs to be suspended until the header message can be processed 
            mNextMessageUptime = messageEnvelope.uptime;
            break;
        }
    }
    // Release lock.
    mLock.unlock();
    //  Then deal with the above push Come in mResponses, namely Request
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        //  Yes callback
        if (response.request.ident == POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
            //  perform callback
            int callbackResult = response.request.callback->handleEvent(fd, events, data)
            if (callbackResult == 0) {
                removeFd(fd, response.request.seq);
            }
            //  eliminate callback References to 
            response.request.callback.clear();
            result = POLL_CALLBACK;
        }
    }
    return result;
}
 Copy code 

Whole Native layer Looper The top priority of the mechanism is Looper::pollInner Medium epoll_wait This system call , This call occurs when the message queue has no work to process , Will block the current thread , Release system resources , in other words ,Looper The dead cycle mechanism of does not always occupy system resources , When there are no tasks to deal with , The main thread is blocked , Therefore, it will not cause excessive resource occupation .

Or more understandable , What we see Looper.loop Opened a dead cycle , This dead cycle is indeed a dead cycle . But what's special is , Instead of writing an infinite loop ,CPU Will always carry out , Then it leads to a surge in resource occupancy ,Looper.loop This is a dead cycle , When there are no messages to process , It will stop blocking , No more epoll_wait Carry out later .

And we don't feel the main thread stop , Because , A code we wrote , Execution is passive , We are in the sub thread post One message,MessageQueue receive messages , Main thread Looper.loop Execute code , After executing the code, remove the next Message, No, Message, The main thread continues to block . The code we wrote executed , Of course, you can't feel the main thread blocking .

Summary :

Through to Native Of pollOnce Analysis of , We know Android Our message processing mechanism actually spans Java Layer and the Native Layer of .

Java Layer and the Native layer , Through some of the above JNI Invocation and mPtr This key pointer , take Java Layer of MessageQueue and Native Of MessageQueue Association , In this way Java When the message mechanism of layer works ,Native The message mechanism of layer can also work together .

The flow of message processing is to process Native Of Message, And then deal with Native Of Request, In the end pollOnce After that , Handle Java Layer of Message, So sometimes Java There are not many messages in the layer, but the response time is relatively slow. It may be Native The message mechanism of the layer leads to .

copyright notice
author[AaronOhOhOh],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201270350549332.html

Random recommended