Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bebc68c5 authored by Christian Wailes's avatar Christian Wailes Committed by Android (Google) Code Review
Browse files

Merge changes I9ee74d76,Idfeb30fa,Id00d0cfd,I7328f304

* changes:
  Resets the refill action/timing info after a pool fill event.
  Make the USAP Pool refill delay configurable.
  Adjust Java Language thread priority in new processes.
  Adjusts the USAP pool refill mechanism.
parents 9eccb8cc 45264b74
Loading
Loading
Loading
Loading
+33 −17
Original line number Diff line number Diff line
@@ -172,6 +172,11 @@ public final class Zygote {
     */
    public static final int SOCKET_BUFFER_SIZE = 256;

    /**
     * @hide for internal use only
     */
    private static final int PRIORITY_MAX = -20;

    /** a prototype instance for a future List.toArray() */
    static final int[][] INT_ARRAY_2D = new int[0][0];

@@ -236,8 +241,7 @@ public final class Zygote {
            int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
            int targetSdkVersion) {
        ZygoteHooks.preFork();
        // Resets nice priority for zygote process.
        resetNicePriority();

        int pid = nativeForkAndSpecialize(
                uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                fdsToIgnore, startChildZygote, instructionSet, appDataDir);
@@ -249,6 +253,7 @@ public final class Zygote {
            // Note that this event ends at the end of handleChildProc,
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
        }

        ZygoteHooks.postForkCommon();
        return pid;
    }
@@ -335,15 +340,16 @@ public final class Zygote {
    static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
            int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
        ZygoteHooks.preFork();
        // Resets nice priority for zygote process.
        resetNicePriority();

        int pid = nativeForkSystemServer(
                uid, gid, gids, runtimeFlags, rlimits,
                permittedCapabilities, effectiveCapabilities);

        // Enable tracing as soon as we enter the system_server.
        if (pid == 0) {
            Trace.setTracingEnabled(true, runtimeFlags);
        }

        ZygoteHooks.postForkCommon();
        return pid;
    }
@@ -461,13 +467,16 @@ public final class Zygote {
    /**
     * Fork a new unspecialized app process from the zygote
     *
     * @param usapPoolSocket  The server socket the USAP will call accept on
     * @param sessionSocketRawFDs  Anonymous session sockets that are currently open
     * @param isPriorityFork  Value controlling the process priority level until accept is called
     * @return In the Zygote process this function will always return null; in unspecialized app
     *         processes this function will return a Runnable object representing the new
     *         application that is passed up from usapMain.
     */
    static Runnable forkUsap(LocalServerSocket usapPoolSocket,
                             int[] sessionSocketRawFDs) {
                             int[] sessionSocketRawFDs,
                             boolean isPriorityFork) {
        FileDescriptor[] pipeFDs = null;

        try {
@@ -477,7 +486,8 @@ public final class Zygote {
        }

        int pid =
                nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(), sessionSocketRawFDs);
                nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(),
                               sessionSocketRawFDs, isPriorityFork);

        if (pid == 0) {
            IoUtils.closeQuietly(pipeFDs[0]);
@@ -492,7 +502,8 @@ public final class Zygote {

    private static native int nativeForkUsap(int readPipeFD,
                                             int writePipeFD,
                                                 int[] sessionSocketRawFDs);
                                             int[] sessionSocketRawFDs,
                                             boolean isPriorityFork);

    /**
     * This function is used by unspecialized app processes to wait for specialization requests from
@@ -515,6 +526,11 @@ public final class Zygote {
        // Load resources
        ZygoteInit.nativePreloadGraphicsDriver();

        // Change the priority to max before calling accept so we can respond to new specialization
        // requests as quickly as possible.  This will be reverted to the default priority in the
        // native specialization code.
        boostUsapPriority();

        while (true) {
            try {
                sessionSocket = usapPoolSocket.accept();
@@ -627,6 +643,9 @@ public final class Zygote {
            // End of the postFork event.
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

            // Set the Java Language thread priority to the default value for new apps.
            Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

            return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
                                         args.mRemainingArgs,
                                         null /* classLoader */);
@@ -648,6 +667,12 @@ public final class Zygote {

    private static native void nativeUnblockSigTerm();

    private static void boostUsapPriority() {
        nativeBoostUsapPriority();
    }

    private static native void nativeBoostUsapPriority();

    static void setAppProcessName(ZygoteArguments args, String loggingTag) {
        if (args.mNiceName != null) {
            Process.setArgV0(args.mNiceName);
@@ -903,15 +928,6 @@ public final class Zygote {
        ZygoteHooks.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
    }

    /**
     * Resets the calling thread priority to the default value (Thread.NORM_PRIORITY
     * or nice value 0). This updates both the priority value in java.lang.Thread and
     * the nice value (setpriority).
     */
    static void resetNicePriority() {
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
    }

    /**
     * Executes "/system/bin/sh -c <command>" using the exec() system call.
     * This method throws a runtime exception if exec() failed, otherwise, this
+4 −1
Original line number Diff line number Diff line
@@ -331,7 +331,7 @@ class ZygoteConnection {
            if (zygoteServer.isUsapPoolEnabled()) {
                Runnable fpResult =
                        zygoteServer.fillUsapPool(
                                new int[]{mSocket.getFileDescriptor().getInt$()});
                                new int[]{mSocket.getFileDescriptor().getInt$()}, false);

                if (fpResult != null) {
                    zygoteServer.setForkChild();
@@ -472,6 +472,9 @@ class ZygoteConnection {

        Zygote.setAppProcessName(parsedArgs, TAG);

        // Set the Java Language thread priority to the default value for new apps.
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

        // End of the postFork event.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        if (parsedArgs.mInvokeWith != null) {
+15 −2
Original line number Diff line number Diff line
@@ -507,6 +507,9 @@ public class ZygoteInit {
            }
        }

        // Set the Java Language thread priority to the default value for the system server.
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

        if (parsedArgs.mInvokeWith != null) {
            String[] args = parsedArgs.mRemainingArgs;
            // If we have a non-null system server class path, we'll have to duplicate the
@@ -815,6 +818,18 @@ public class ZygoteInit {
        return result;
    }

    /**
     * This is the entry point for a Zygote process.  It creates the Zygote server, loads resources,
     * and handles other tasks related to preparing the process for forking into applications.
     *
     * This process is started with a nice value of -20 (highest priority).  All paths that flow
     * into new processes are required to either set the priority to the default value or terminate
     * before executing any non-system code.  The native side of this occurs in SpecializeCommon,
     * while the Java Language priority is changed in ZygoteInit.handleSystemServerProcess,
     * ZygoteConnection.handleChildProc, and Zygote.usapMain.
     *
     * @param argv  Command line arguments used to specify the Zygote's configuration.
     */
    @UnsupportedAppUsage
    public static void main(String argv[]) {
        ZygoteServer zygoteServer = null;
@@ -878,8 +893,6 @@ public class ZygoteInit {
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                        SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            } else {
                Zygote.resetNicePriority();
            }

            // Do an initial gc to clean up after startup
+228 −121
Original line number Diff line number Diff line
@@ -66,6 +66,12 @@ class ZygoteServer {
    /** The default value used for the USAP_POOL_SIZE_MIN device property */
    private static final String USAP_POOL_SIZE_MIN_DEFAULT = "1";

    /** The default value used for the USAP_REFILL_DELAY_MS device property */
    private static final String USAP_POOL_REFILL_DELAY_MS_DEFAULT = "3000";

    /** The "not a timestamp" value for the refill delay timestamp mechanism. */
    private static final int INVALID_TIMESTAMP = -1;

    /**
     * Indicates if this Zygote server can support a unspecialized app process pool.  Currently this
     * should only be true for the primary and secondary Zygotes, and not the App Zygotes or the
@@ -131,6 +137,24 @@ class ZygoteServer {
     */
    private int mUsapPoolRefillThreshold = 0;

    /**
     * Number of milliseconds to delay before refilling the pool if it hasn't reached its
     * minimum value.
     */
    private int mUsapPoolRefillDelayMs = -1;

    /**
     * If and when we should refill the USAP pool.
     */
    private UsapPoolRefillAction mUsapPoolRefillAction;
    private long mUsapPoolRefillTriggerTimestamp;

    private enum UsapPoolRefillAction {
        DELAYED,
        IMMEDIATE,
        NONE
    }

    ZygoteServer() {
        mUsapPoolEventFD = null;
        mZygoteSocket = null;
@@ -267,6 +291,13 @@ class ZygoteServer {
                        mUsapPoolSizeMax);
            }

            final String usapPoolRefillDelayMsPropString = Zygote.getConfigurationProperty(
                    ZygoteConfig.USAP_POOL_REFILL_DELAY_MS, USAP_POOL_REFILL_DELAY_MS_DEFAULT);

            if (!usapPoolRefillDelayMsPropString.isEmpty()) {
                mUsapPoolRefillDelayMs = Integer.parseInt(usapPoolRefillDelayMsPropString);
            }

            // Sanity check
            if (mUsapPoolSizeMin >= mUsapPoolSizeMax) {
                Log.w(TAG, "The max size of the USAP pool must be greater than the minimum size."
@@ -293,9 +324,16 @@ class ZygoteServer {
        }
    }

    private void fetchUsapPoolPolicyPropsIfUnfetched() {
        if (mIsFirstPropertyCheck) {
            mIsFirstPropertyCheck = false;
            fetchUsapPoolPolicyProps();
        }
    }

    /**
     * Checks to see if the current policy says that pool should be refilled, and spawns new USAPs
     * if necessary.
     * Refill the USAP Pool to the appropriate level, determined by whether this is a priority
     * refill event or not.
     *
     * @param sessionSocketRawFDs  Anonymous session sockets that are currently open
     * @return In the Zygote process this function will always return null; in unspecialized app
@@ -303,25 +341,36 @@ class ZygoteServer {
     *         application that is passed up from usapMain.
     */

    Runnable fillUsapPool(int[] sessionSocketRawFDs) {
    Runnable fillUsapPool(int[] sessionSocketRawFDs, boolean isPriorityRefill) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillUsapPool");

        // Ensure that the pool properties have been fetched.
        fetchUsapPoolPolicyPropsWithMinInterval();
        fetchUsapPoolPolicyPropsIfUnfetched();

        int usapPoolCount = Zygote.getUsapPoolCount();
        int numUsapsToSpawn = mUsapPoolSizeMax - usapPoolCount;
        int numUsapsToSpawn;

        if (isPriorityRefill) {
            // Refill to min
            numUsapsToSpawn = mUsapPoolSizeMin - usapPoolCount;

        if (usapPoolCount < mUsapPoolSizeMin
                || numUsapsToSpawn >= mUsapPoolRefillThreshold) {
            Log.i("zygote",
                    "Priority USAP Pool refill. New USAPs: " + numUsapsToSpawn);
        } else {
            // Refill up to max
            numUsapsToSpawn = mUsapPoolSizeMax - usapPoolCount;

            Log.i("zygote",
                    "Delayed USAP Pool refill. New USAPs: " + numUsapsToSpawn);
        }

        // Disable some VM functionality and reset some system values
        // before forking.
        ZygoteHooks.preFork();
            Zygote.resetNicePriority();

            while (usapPoolCount++ < mUsapPoolSizeMax) {
                Runnable caller = Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs);
        while (--numUsapsToSpawn >= 0) {
            Runnable caller =
                    Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs, isPriorityRefill);

            if (caller != null) {
                return caller;
@@ -332,9 +381,7 @@ class ZygoteServer {
        // are re-enabled in specializeAppProcess.
        ZygoteHooks.postForkCommon();

            Log.i("zygote",
                    "Filled the USAP pool. New USAPs: " + numUsapsToSpawn);
        }
        resetUsapRefillState();

        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

@@ -358,13 +405,18 @@ class ZygoteServer {
        mUsapPoolEnabled = newStatus;

        if (newStatus) {
            return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() });
            return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() }, false);
        } else {
            Zygote.emptyUsapPool();
            return null;
        }
    }

    void resetUsapRefillState() {
        mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
        mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
    }

    /**
     * Runs the zygote process's select loop. Accepts new connections as
     * they happen, and reads commands from connections one spawn-request's
@@ -377,8 +429,11 @@ class ZygoteServer {
        socketFDs.add(mZygoteSocket.getFileDescriptor());
        peers.add(null);

        mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;

        while (true) {
            fetchUsapPoolPolicyPropsWithMinInterval();
            mUsapPoolRefillAction = UsapPoolRefillAction.NONE;

            int[] usapPipeFDs = null;
            StructPollfd[] pollFDs;
@@ -430,12 +485,39 @@ class ZygoteServer {
                }
            }

            int pollTimeoutMs;

            if (mUsapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) {
                pollTimeoutMs = -1;
            } else {
                int elapsedTimeMs =
                        (int) (System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp);

                if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
                    // Normalize the poll timeout value when the time between one poll event and the
                    // next pushes us over the delay value.  This prevents poll receiving a 0
                    // timeout value, which would result in it returning immediately.
                    pollTimeoutMs = -1;
                } else {
                    pollTimeoutMs = mUsapPoolRefillDelayMs - elapsedTimeMs;
                }
            }

            int pollReturnValue;
            try {
                Os.poll(pollFDs, -1);
                pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }

            if (pollReturnValue == 0) {
                // The poll timeout has been exceeded.  This only occurs when we have finished the
                // USAP pool refill delay period.

                mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
                mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;

            } else {
                boolean usapPoolFDRead = false;

                while (--pollIndex >= 0) {
@@ -459,8 +541,8 @@ class ZygoteServer {

                            // TODO (chriswailes): Is this extra check necessary?
                            if (mIsForkChild) {
                            // We're in the child. We should always have a command to run at this
                            // stage if processOneCommand hasn't called "exec".
                                // We're in the child. We should always have a command to run at
                                // this stage if processOneCommand hasn't called "exec".
                                if (command == null) {
                                    throw new IllegalStateException("command == null");
                                }
@@ -474,7 +556,8 @@ class ZygoteServer {

                                // We don't know whether the remote side of the socket was closed or
                                // not until we attempt to read from it from processOneCommand. This
                            // shows up as a regular POLLIN event in our regular processing loop.
                                // shows up as a regular POLLIN event in our regular processing
                                // loop.
                                if (connection.isClosedByPeer()) {
                                    connection.closeSocket();
                                    peers.remove(pollIndex);
@@ -483,10 +566,10 @@ class ZygoteServer {
                            }
                        } catch (Exception e) {
                            if (!mIsForkChild) {
                            // We're in the server so any exception here is one that has taken place
                            // pre-fork while processing commands or reading / writing from the
                            // control socket. Make a loud noise about any such exceptions so that
                            // we know exactly what failed and why.
                                // We're in the server so any exception here is one that has taken
                                // place pre-fork while processing commands or reading / writing
                                // from the control socket. Make a loud noise about any such
                                // exceptions so that we know exactly what failed and why.

                                Slog.e(TAG, "Exception executing zygote command: ", e);

@@ -499,28 +582,33 @@ class ZygoteServer {
                                socketFDs.remove(pollIndex);
                            } else {
                                // We're in the child so any exception caught here has happened post
                            // fork and before we execute ActivityThread.main (or any other main()
                            // method). Log the details of the exception and bring down the process.
                                // fork and before we execute ActivityThread.main (or any other
                                // main() method). Log the details of the exception and bring down
                                // the process.
                                Log.e(TAG, "Caught post-fork exception in child process.", e);
                                throw e;
                            }
                        } finally {
                            // Reset the child flag, in the event that the child process is a child-
                        // zygote. The flag will not be consulted this loop pass after the Runnable
                        // is returned.
                            // zygote. The flag will not be consulted this loop pass after the
                            // Runnable is returned.
                            mIsForkChild = false;
                        }

                    } else {
                        // Either the USAP pool event FD or a USAP reporting pipe.

                        // If this is the event FD the payload will be the number of USAPs removed.
                        // If this is a reporting pipe FD the payload will be the PID of the USAP
                    // that was just specialized.
                    long messagePayload = -1;
                        // that was just specialized.  The `continue` statements below ensure that
                        // the messagePayload will always be valid if we complete the try block
                        // without an exception.
                        long messagePayload;

                        try {
                            byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
                        int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
                            int readBytes =
                                    Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);

                            if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
                                DataInputStream inputStream =
@@ -552,18 +640,37 @@ class ZygoteServer {
                    }
                }

            // Check to see if the USAP pool needs to be refilled.
                if (usapPoolFDRead) {
                    int usapPoolCount = Zygote.getUsapPoolCount();

                    if (usapPoolCount < mUsapPoolSizeMin) {
                        // Immediate refill
                        mUsapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE;
                    } else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) {
                        // Delayed refill
                        mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
                    }
                }
            }

            if (mUsapPoolRefillAction != UsapPoolRefillAction.NONE) {
                int[] sessionSocketRawFDs =
                        socketFDs.subList(1, socketFDs.size())
                                .stream()
                                .mapToInt(FileDescriptor::getInt$)
                                .toArray();

                final Runnable command = fillUsapPool(sessionSocketRawFDs);
                final boolean isPriorityRefill =
                        mUsapPoolRefillAction == UsapPoolRefillAction.IMMEDIATE;

                final Runnable command =
                        fillUsapPool(sessionSocketRawFDs, isPriorityRefill);

                if (command != null) {
                    return command;
                } else if (isPriorityRefill) {
                    // Schedule a delayed refill to finish refilling the pool.
                    mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
                }
            }
        }
+36 −7

File changed.

Preview size limit exceeded, changes collapsed.