joinThreadPool
joinThreadPool() 不是“创建一个线程池”,而是让当前线程进入 Binder 消费循环。它先向驱动写入 BC_ENTER_LOOPER 或 BC_REGISTER_LOOPER,然后反复调用 getAndExecuteCommand();工作线程在 TIMED_OUT 时可以退出,主线程则继续等待;连接关闭或 fd 失效时,循环收束并发送 BC_EXIT_LOOPER,最后递减 mCurrentThreads。
本文面向已经读过 IPCThreadState线程绑定、ProcessState初始化 和 Binder线程管理 的读者。本文只讲 native 线程加入与退出循环,不重复讲 transact() 的发送细节,也不把 startThreadPool() 创建的线程与当前线程加入动作混为一谈。
1. 加入命令
1.1 入口函数
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
相关函数:IPCThreadState::joinThreadPool()
void IPCThreadState::joinThreadPool(bool isMain) {
LOG_THREADPOOL("**** THREAD %p IS JOINING THE THREAD POOL",
(void*)pthread_self());
mProcess->checkExpectingThreadPoolStart();
mProcess->mCurrentThreads++;
mOut.writeInt32(isMain ? BC_ENTER_LOOPER
: BC_REGISTER_LOOPER);
mIsLooper = true;
...
}isMain=true 表示当前线程发送 BC_ENTER_LOOPER;普通工作线程发送 BC_REGISTER_LOOPER。两者都增加进程级当前线程数并设置当前 IPCThreadState 的 mIsLooper。
1.2 配置警告
checkExpectingThreadPoolStart() 只检查是否设置过非默认线程配置但尚未调用 startThreadPool(),并记录 warning。它不是加入动作的硬门槛;调用仍会继续进入循环。
1.3 线程来源
源码文件:frameworks/native/libs/binder/ProcessState.cpp
相关函数:startThreadPool()、spawnPooledThread()
void ProcessState::startThreadPool() {
std::unique_lock<std::mutex> lock(mLock);
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
spawnPooledThread(true);
}
}startThreadPool() 创建一个 PoolThread,其 threadLoop() 再调用 joinThreadPool(true);手动调用 joinThreadPool(false) 则由调用方线程直接加入。线程创建和线程加入是两个阶段。
2. 请求循环
2.1 主循环
status_t result;
do {
processPendingDerefs();
result = getAndExecuteCommand();
if (result < NO_ERROR &&
result != TIMED_OUT &&
result != -ECONNREFUSED &&
result != -EBADF) {
LOG_ALWAYS_FATAL("getAndExecuteCommand returned unexpected error");
}
if (result == TIMED_OUT && !isMain) {
break;
}
} while (result != -ECONNREFUSED &&
result != -EBADF);每次迭代先处理待释放的 Binder 引用,再向驱动取并执行一个命令。只有列出的几类返回值被视为循环控制结果;其他负错误会 fatal,避免线程在未知状态下继续消费。
2.2 命令消费
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
相关函数:getAndExecuteCommand()
result = talkWithDriver();
if (result >= NO_ERROR) {
if (mIn.dataAvail() < sizeof(int32_t)) return result;
int32_t cmd = mIn.readInt32();
size_t executing =
mProcess->mExecutingThreadsCount.fetch_add(1) + 1;
result = executeCommand(cmd);
size_t remaining =
mProcess->mExecutingThreadsCount.fetch_sub(1) - 1;
...
}
return result;mCurrentThreads 表示进入线程池的线程数,mExecutingThreadsCount 表示正在执行当前 Binder command 的线程数,不能用一个计数替代另一个。执行结束后,如果有线程等待可用,代码通过条件变量唤醒等待者。
2.3 资源释放
processPendingDerefs() 在输入队列清空时处理延迟的 weak/strong deref。它放在每轮 command 前,避免 Binder 引用析构延迟到线程退出才发生;析构可能触发新的 outgoing transaction,所以实现使用外层循环直到 pending vectors 为空。
3. 退出条件
3.1 工作线程超时
工作线程 isMain=false 收到 TIMED_OUT 时跳出循环,以便线程池在需求下降时收缩。主线程即使超时也不会退出,因为 isMain=true 不满足 break 条件。
3.2 连接关闭
-ECONNREFUSED 和 -EBADF 会结束主循环。前者通常表示驱动连接被拒绝,后者表示 fd 已关闭或失效;源码把这两种结果作为退出收束,而不是未知 fatal。
3.3 清理命令
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
相关函数:joinThreadPool()
LOG_THREADPOOL("**** THREAD IS LEAVING THE THREAD POOL err=%d",
result);
mOut.writeInt32(BC_EXIT_LOOPER);
mIsLooper = false;
if (status_t res = talkWithDriver(false); res != OK) {
ALOGW("call to talkWithDriver in joinThreadPool returned error");
}
size_t oldCount = mProcess->mCurrentThreads.fetch_sub(1);
LOG_ALWAYS_FATAL_IF(oldCount == 0,
"Threadpool thread count underflowed");退出时先通知驱动离开 looper,再清除线程本地标志,最后递减进程计数。计数下溢是内部不变量破坏,会 fatal;发送 BC_EXIT_LOOPER 失败只记录 warning。
4. 线程池边界
4.1 最大线程数
setThreadPoolMaxThreadCount() 设置的是驱动按需启动上限;joinThreadPool() 增加的是当前进程已进入线程池的数量。startThreadPool() 额外创建一个用户线程,再叠加驱动可能启动的线程和手动 join 的线程,最终总数不是单一字段的值。
4.2 starvation观测
getAndExecuteCommand() 在执行线程数达到 mMaxThreads 时记录 starvation 起点;当执行数降回上限以下时计算持续时间,超过 100ms 记录线程池饥饿日志。这是观测和诊断,不是自动扩容或强制退出策略。
5. 测试边界
5.1 线程可用数
源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp
ThreadPoolAvailableThreads 通过服务事务读取另一进程的最大线程数,随后并发占用线程并验证最终可用数;断言允许 kKernelThreads + 1 或 +2,源码明确承认线程计数存在竞态。它验证线程池总量近似关系,不证明每次 join 的绝对时序。
5.2 线程池启动
ThreadPoolStarted 通过服务读取 ProcessState::isThreadPoolStarted() 并断言 true;测试进程 main 预先调用 ProcessState::startThreadPool()。它证明启动标志和 PoolThread 入口的组合,不单独测试 joinThreadPool() 的 timeout 退出。
5.3 可执行阅读
rg -n "joinThreadPool|BC_ENTER_LOOPER|BC_REGISTER_LOOPER|BC_EXIT_LOOPER|TIMED_OUT" \
frameworks/native/libs/binder/IPCThreadState.cpp
rg -n "startThreadPool|spawnPooledThread|mCurrentThreads|mExecutingThreadsCount|starvation" \
frameworks/native/libs/binder/ProcessState.cpp \
frameworks/native/libs/binder/IPCThreadState.cpp
rg -n "ThreadPoolAvailableThreads|ThreadPoolStarted|joinThreadPool" \
frameworks/native/libs/binder/tests/binderLibTest.cpp排查 Binder 线程退出时,先区分 worker 的 TIMED_OUT、连接关闭的 ECONNREFUSED/EBADF 和未知负错误;若出现 count underflow,则应回到加入/退出配对和重复执行路径,而不是先调大线程上限。
