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

Commit 5c2688a8 authored by Daichi Hirono's avatar Daichi Hirono
Browse files

Process FuseAppLoop messages in Handler.Callback

Previously FuseAppLoop instantiates Runnable for each command, which
causes lots of instantiation and GC.

Test: CTS
Bug: 35229514
Change-Id: Ifea098e5ade044b1a954c0b714c5b3270a95cd1a
parent 1ce83f00
Loading
Loading
Loading
Loading
+123 −83
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.ProxyFileDescriptorCallback;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.system.ErrnoException;
import android.system.OsConstants;
@@ -28,10 +29,11 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ThreadFactory;

public class FuseAppLoop {
public class FuseAppLoop implements Handler.Callback {
    private static final String TAG = "FuseAppLoop";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    public static final int ROOT_INODE = 1;
@@ -43,13 +45,11 @@ public class FuseAppLoop {
        }
    };
    private static final int FUSE_OK = 0;
    private static final int ARGS_POOL_SIZE = 50;

    private final Object mLock = new Object();
    private final int mMountPointId;
    private final Thread mThread;
    private final Handler mDefaultHandler;

    private static final int CMD_FSYNC = 1;

    @GuardedBy("mLock")
    private final SparseArray<CallbackEntry> mCallbackMap = new SparseArray<>();
@@ -57,6 +57,9 @@ public class FuseAppLoop {
    @GuardedBy("mLock")
    private final BytesMap mBytesMap = new BytesMap();

    @GuardedBy("mLock")
    private final LinkedList<Args> mArgsPool = new LinkedList<>();

    /**
     * Sequential number can be used as file name and inode in AppFuse.
     * 0 is regarded as an error, 1 is mount point. So we start the number from 2.
@@ -83,7 +86,6 @@ public class FuseAppLoop {
            }
        });
        mThread.start();
        mDefaultHandler = null;
    }

    public int registerCallback(@NonNull ProxyFileDescriptorCallback callback,
@@ -110,7 +112,8 @@ public class FuseAppLoop {
                    break;
                }
            }
            mCallbackMap.put(id, new CallbackEntry(callback, handler));
            mCallbackMap.put(id, new CallbackEntry(
                    callback, new Handler(handler.getLooper(), this)));
            return id;
        }
    }
@@ -137,22 +140,25 @@ public class FuseAppLoop {
    // Defined in FuseBuffer.h
    private static final int FUSE_MAX_WRITE = 256 * 1024;

    // Called by JNI.
    @SuppressWarnings("unused")
    private void onCommand(int command, long unique, long inode, long offset, int size,
            byte[] data) {
        synchronized(mLock) {
            try {
                final CallbackEntry entry = getCallbackEntryOrThrowLocked(inode);
                entry.postRunnable(() -> {
    @Override
    public boolean handleMessage(Message msg) {
        final Args args = (Args) msg.obj;
        final CallbackEntry entry = args.entry;
        final long inode = args.inode;
        final long unique = args.unique;
        final int size = args.size;
        final long offset = args.offset;
        final byte[] data = args.data;

        try {
                        switch (command) {
            switch (msg.what) {
                case FUSE_LOOKUP: {
                    final long fileSize = entry.callback.onGetSize();
                    synchronized (mLock) {
                        if (mInstance != 0) {
                            native_replyLookup(mInstance, unique, inode, fileSize);
                        }
                        recycleLocked(args);
                    }
                    break;
                }
@@ -162,15 +168,18 @@ public class FuseAppLoop {
                        if (mInstance != 0) {
                            native_replyGetAttr(mInstance, unique, inode, fileSize);
                        }
                        recycleLocked(args);
                    }
                    break;
                }
                case FUSE_READ:
                                final int readSize = entry.callback.onRead(offset, size, data);
                    final int readSize = entry.callback.onRead(
                            offset, size, data);
                    synchronized (mLock) {
                        if (mInstance != 0) {
                            native_replyRead(mInstance, unique, readSize, data);
                        }
                        recycleLocked(args);
                    }
                    break;
                case FUSE_WRITE:
@@ -179,6 +188,7 @@ public class FuseAppLoop {
                        if (mInstance != 0) {
                            native_replyWrite(mInstance, unique, writeSize);
                        }
                        recycleLocked(args);
                    }
                    break;
                case FUSE_FSYNC:
@@ -187,6 +197,7 @@ public class FuseAppLoop {
                        if (mInstance != 0) {
                            native_replySimple(mInstance, unique, FUSE_OK);
                        }
                        recycleLocked(args);
                    }
                    break;
                case FUSE_RELEASE:
@@ -196,19 +207,46 @@ public class FuseAppLoop {
                            native_replySimple(mInstance, unique, FUSE_OK);
                        }
                        mBytesMap.stopUsing(entry.getThreadId());
                        recycleLocked(args);
                    }
                    break;
                default:
                                throw new IllegalArgumentException(
                                        "Unknown FUSE command: " + command);
                    throw new IllegalArgumentException("Unknown FUSE command: " + msg.what);
            }
        } catch (Exception error) {
            synchronized (mLock) {
                Log.e(TAG, "", error);
                        replySimple(unique, getError(error));
                replySimpleLocked(unique, getError(error));
                recycleLocked(args);
            }
                });
            } catch (ErrnoException error) {
                Log.e(TAG, "", error);
        }

        return true;
    }

    // Called by JNI.
    @SuppressWarnings("unused")
    private void onCommand(int command, long unique, long inode, long offset, int size,
            byte[] data) {
        synchronized (mLock) {
            try {
                final Args args;
                if (mArgsPool.size() == 0) {
                    args = new Args();
                } else {
                    args = mArgsPool.pop();
                }
                args.unique = unique;
                args.inode = inode;
                args.offset = offset;
                args.size = size;
                args.data = data;
                args.entry = getCallbackEntryOrThrowLocked(inode);
                if (!args.entry.handler.sendMessage(
                        Message.obtain(args.entry.handler, command, 0, 0, args))) {
                    throw new ErrnoException("onCommand", OsConstants.EBADF);
                }
            } catch (Exception error) {
                replySimpleLocked(unique, getError(error));
            }
        }
@@ -253,9 +291,9 @@ public class FuseAppLoop {
        return entry;
    }

    private void replySimple(long unique, int result) {
        synchronized (mLock) {
            replySimpleLocked(unique, result);
    private void recycleLocked(Args args) {
        if (mArgsPool.size() < ARGS_POOL_SIZE) {
            mArgsPool.add(args);
        }
    }

@@ -296,13 +334,6 @@ public class FuseAppLoop {
        long getThreadId() {
            return handler.getLooper().getThread().getId();
        }

        void postRunnable(Runnable runnable) throws ErrnoException {
            final boolean result = handler.post(runnable);
            if (!result) {
                throw new ErrnoException("postRunnable", OsConstants.EBADF);
            }
        }
    }

    /**
@@ -342,4 +373,13 @@ public class FuseAppLoop {
            mEntries.clear();
        }
    }

    private static class Args {
        long unique;
        long inode;
        long offset;
        int size;
        byte[] data;
        CallbackEntry entry;
    }
}