From 7c44cd3ebbebde09212c85bfcce99e89b8397ece Mon Sep 17 00:00:00 2001 From: tarsin Date: Mon, 13 Jun 2022 12:31:01 +0800 Subject: [PATCH] Inline hippo's yorozuya-thread * No more dependencies spam thanks --- app/build.gradle | 1 - .../thread/InfiniteThreadExecutor.java | 101 +++++++++++++++++ .../com/hippo/yorozuya/thread/PVLock.java | 50 +++++++++ .../hippo/yorozuya/thread/PriorityThread.java | 40 +++++++ .../thread/PriorityThreadFactory.java | 42 +++++++ .../yorozuya/thread/SerialThreadExecutor.java | 103 ++++++++++++++++++ 6 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/hippo/yorozuya/thread/InfiniteThreadExecutor.java create mode 100644 app/src/main/java/com/hippo/yorozuya/thread/PVLock.java create mode 100644 app/src/main/java/com/hippo/yorozuya/thread/PriorityThread.java create mode 100644 app/src/main/java/com/hippo/yorozuya/thread/PriorityThreadFactory.java create mode 100644 app/src/main/java/com/hippo/yorozuya/thread/SerialThreadExecutor.java diff --git a/app/build.gradle b/app/build.gradle index 1b49d1306..092fd20ec 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -109,7 +109,6 @@ dependencies { implementation 'com.github.seven332:streampipe:0.1.0' implementation 'com.github.seven332:tuxiang:0.1.6' implementation 'com.github.seven332:unifile:9ec57bcd8f' - implementation 'com.github.seven332:yorozuya-thread:0.1.1' implementation 'com.github.seven332:yorozuya-collect:0.1.4' implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0' implementation 'com.squareup.okhttp3:okhttp:4.10.0' diff --git a/app/src/main/java/com/hippo/yorozuya/thread/InfiniteThreadExecutor.java b/app/src/main/java/com/hippo/yorozuya/thread/InfiniteThreadExecutor.java new file mode 100644 index 000000000..ef95d7d8c --- /dev/null +++ b/app/src/main/java/com/hippo/yorozuya/thread/InfiniteThreadExecutor.java @@ -0,0 +1,101 @@ +/* + * Copyright 2015-2016 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.yorozuya.thread; + +import androidx.annotation.NonNull; + +import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +public class InfiniteThreadExecutor implements Executor { + + private final long mKeepAliveMillis; + private final Queue mWorkQueue; + private final ThreadFactory mThreadFactory; + + private final AtomicInteger mThreadCount = new AtomicInteger(); + private int mEmptyThreadCount; + + private final Object mLock = new Object(); + + public InfiniteThreadExecutor(long keepAliveMillis, Queue workQueue, ThreadFactory threadFactory) { + mKeepAliveMillis = keepAliveMillis; + mWorkQueue = workQueue; + mThreadFactory = threadFactory; + } + + @Override + public void execute(@NonNull Runnable command) { + synchronized (mLock) { + mWorkQueue.add(command); + if (mEmptyThreadCount > 0) { + --mEmptyThreadCount; + mLock.notify(); + return; + } + } + + mThreadFactory.newThread(new Task()).start(); + } + + public int getThreadCount() { + return mThreadCount.get(); + } + + private class Task implements Runnable { + + @Override + public void run() { + mThreadCount.incrementAndGet(); + + boolean hasWait = false; + for (;;) { + Runnable command; + synchronized (mLock) { + command = mWorkQueue.poll(); + if (command == null) { + if (hasWait) { + --mEmptyThreadCount; + } + break; + } + } + + try { + command.run(); + } catch (Exception e) { + e.printStackTrace(); + } + + synchronized (mLock) { + ++mEmptyThreadCount; + try { + mLock.wait(mKeepAliveMillis); + } catch (InterruptedException e) { + // Ignore + } + } + + hasWait = true; + } + + mThreadCount.decrementAndGet(); + } + } +} diff --git a/app/src/main/java/com/hippo/yorozuya/thread/PVLock.java b/app/src/main/java/com/hippo/yorozuya/thread/PVLock.java new file mode 100644 index 000000000..7c40843f2 --- /dev/null +++ b/app/src/main/java/com/hippo/yorozuya/thread/PVLock.java @@ -0,0 +1,50 @@ +/* + * Copyright 2015-2016 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.yorozuya.thread; + +public class PVLock { + + private int mCounter; + + public PVLock(int count) { + mCounter = count; + } + + /** + * Obtain + */ + public synchronized void p() throws InterruptedException { + while (true) { + if (mCounter > 0) { + mCounter--; + break; + } else { + this.wait(); + } + } + } + + /** + * Release + */ + public synchronized void v() { + mCounter++; + if (mCounter > 0) { + this.notify(); + } + } +} diff --git a/app/src/main/java/com/hippo/yorozuya/thread/PriorityThread.java b/app/src/main/java/com/hippo/yorozuya/thread/PriorityThread.java new file mode 100644 index 000000000..afdbc7752 --- /dev/null +++ b/app/src/main/java/com/hippo/yorozuya/thread/PriorityThread.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015-2016 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.yorozuya.thread; + +import android.os.Process; + +public class PriorityThread extends Thread { + + private int mPriority; + + public PriorityThread(Runnable runnable, int priority) { + super(runnable); + mPriority = priority; + } + + public PriorityThread(Runnable runnable, String name, int priority) { + super(runnable, name); + mPriority = priority; + } + + @Override + public void run() { + Process.setThreadPriority(mPriority); + super.run(); + } +} diff --git a/app/src/main/java/com/hippo/yorozuya/thread/PriorityThreadFactory.java b/app/src/main/java/com/hippo/yorozuya/thread/PriorityThreadFactory.java new file mode 100644 index 000000000..197764e52 --- /dev/null +++ b/app/src/main/java/com/hippo/yorozuya/thread/PriorityThreadFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright 2015-2016 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.yorozuya.thread; + +import androidx.annotation.NonNull; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A thread factory that creates threads with a given thread priority. + */ +public class PriorityThreadFactory implements ThreadFactory { + + private final int mPriority; + private final AtomicInteger mIdGenerator = new AtomicInteger(); + private final String mName; + + public PriorityThreadFactory(String name, int priority) { + mName = name; + mPriority = priority; + } + + @Override + public Thread newThread(@NonNull Runnable r) { + return new PriorityThread(r, mName + "-" + mIdGenerator.getAndIncrement(), mPriority); + } +} diff --git a/app/src/main/java/com/hippo/yorozuya/thread/SerialThreadExecutor.java b/app/src/main/java/com/hippo/yorozuya/thread/SerialThreadExecutor.java new file mode 100644 index 000000000..9ad37bc07 --- /dev/null +++ b/app/src/main/java/com/hippo/yorozuya/thread/SerialThreadExecutor.java @@ -0,0 +1,103 @@ +/* + * Copyright 2015-2016 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.yorozuya.thread; + +import androidx.annotation.NonNull; + +import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class SerialThreadExecutor implements Executor, Runnable { + + private long mKeepAliveMillis; + private Queue mWorkQueue; + private ThreadFactory mThreadFactory; + + // The worker + private Thread mThread; + + private final Lock mThreadLock = new ReentrantLock(); + private final Object mWaitLock = new Object(); + + public SerialThreadExecutor(long keepAliveMillis, + Queue workQueue, ThreadFactory threadFactory) { + mKeepAliveMillis = keepAliveMillis; + mWorkQueue = workQueue; + mThreadFactory = threadFactory; + } + + @Override + public void execute(@NonNull Runnable command) { + mThreadLock.lock(); + + mWorkQueue.add(command); + + // Ensure thread + if (mThread == null) { + mThread = mThreadFactory.newThread(this); + mThread.start(); + } else { + synchronized (mWaitLock) { + mWaitLock.notify(); + } + } + + mThreadLock.unlock(); + } + + @Override + public void run() { + // It tell whether has waited + boolean hasWaited = false; + + for (;;) { + mThreadLock.lock(); + Runnable runnable = mWorkQueue.poll(); + if (runnable == null) { + if (hasWaited) { + // Have waited enough time + mThread = null; + mThreadLock.unlock(); + break; + } else { + mThreadLock.unlock(); + hasWaited = true; + synchronized (mWaitLock) { + try { + mWaitLock.wait(mKeepAliveMillis); + } catch (InterruptedException e) { + // Ignore + } + } + continue; + } + } else { + mThreadLock.unlock(); + } + + hasWaited = false; + try { + runnable.run(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +}