NativeLooper addFd
本文承接 epoll机制,聚焦 Looper::addFd()。它不是简单的 epoll_ctl(ADD) 包装:同一个入口支持 callback 与 non-callback 两种消费模式,维护两张索引表,为每次注册分配新序列号,并在 FD 被关闭后复用数字时修复 epoll 状态。
1. 两种模式
源码文件:system/core/libutils/include/utils/Looper.h
相关函数:addFd()、pollOnce()
int addFd(int fd, int ident, int events,
Looper_callbackFunc callback, void* data);
int addFd(int fd, int ident, int events,
const sp<LooperCallback>& callback, void* data);callback 非空时,事件在 pollInner() 内调用 callback,ident 被忽略;callback 为空时,pollOnce() 返回 ident,并通过输出参数返回 fd、events 和 data。后者要求 Looper 以 allowNonCallbacks=true 构造。
2. 回调包装
源码文件:system/core/libutils/Looper.cpp
相关函数:addFd() 第一重载、SimpleLooperCallback::handleEvent()
int Looper::addFd(int fd, int ident, int events,
Looper_callbackFunc callback, void* data) {
sp<SimpleLooperCallback> looperCallback;
if (callback) {
looperCallback = sp<SimpleLooperCallback>::make(callback);
}
return addFd(fd, ident, events, looperCallback, data);
}
int SimpleLooperCallback::handleEvent(int fd, int events, void* data) {
return mCallback(fd, events, data);
}裸函数指针先包装成 sp<SimpleLooperCallback>,再进入统一实现。智能指针重载更容易处理跨线程注销,因为 response 和 Request 都能明确持有 callback 生命周期。
3. 参数校验
源码文件:system/core/libutils/Looper.cpp
相关函数:addFd()
if (!callback.get()) {
if (!mAllowNonCallbacks) {
return -1;
}
if (ident < 0) {
return -1;
}
} else {
ident = POLL_CALLBACK;
}无 callback 时必须同时满足“Looper 允许 non-callback”和“ident 非负”。有 callback 时调用者传入的 ident 被覆盖为 POLL_CALLBACK。当前实现和单测对无效参数使用 -1,阅读时应以实现与测试为准。
4. Request创建
源码文件:system/core/libutils/Looper.cpp
相关函数:addFd()
std::scoped_lock _l(mLock);
if (mNextRequestSeq == WAKE_EVENT_FD_SEQ) mNextRequestSeq++;
const SequenceNumber seq = mNextRequestSeq++;
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
request.callback = callback;
request.data = data;
mRequestsGeneration.fetch_add(1, std::memory_order_release);
epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);每次调用都分配新 sequence,即使只是替换同一个 FD。wake FD 的保留 sequence 会被跳过;generation 在修改 epoll 前递增,使并发 poll 能察觉请求集合发生变化。
5. 新增注册
源码文件:system/core/libutils/Looper.cpp
相关函数:addFd()
auto seq_it = mSequenceNumberByFd.find(fd);
if (seq_it == mSequenceNumberByFd.end()) {
int epollResult = epoll_ctl(mEpollFd.get(),
EPOLL_CTL_ADD, fd, &eventItem);
if (epollResult < 0) {
return -1;
}
mRequests.emplace(seq, request);
mSequenceNumberByFd.emplace(fd, seq);
}只有内核 ADD 成功后才更新两张表。失败时 Request 不进入用户态索引,调用者得到 -1;不会留下“表中存在、epoll 未注册”的半完成状态。
6. 替换注册
源码文件:system/core/libutils/Looper.cpp
相关函数:addFd()
int epollResult = epoll_ctl(mEpollFd.get(),
EPOLL_CTL_MOD, fd, &eventItem);
if (epollResult >= 0) {
const SequenceNumber oldSeq = seq_it->second;
mRequests.erase(oldSeq);
mRequests.emplace(seq, request);
seq_it->second = seq;
}同一个 FD 再注册会替换 callback、events、data 和 sequence。旧 callback 可能已经在另一个线程执行,addFd() 只能保证后续新 response 使用新 Request,不能取消已开始的回调。
7. FD复用
源码文件:system/core/libutils/Looper.cpp
相关函数:addFd()、scheduleEpollRebuildLocked()
if (epollResult < 0 && errno == ENOENT) {
epollResult = epoll_ctl(mEpollFd.get(),
EPOLL_CTL_ADD, fd, &eventItem);
if (epollResult < 0) {
return -1;
}
scheduleEpollRebuildLocked();
}旧 FD 已关闭但用户态表还保留同一数字时,MOD 可能找不到内核对象。源码尝试 ADD 新 FD,并安排完整 epoll 重建;新 sequence 使旧事件无法匹配新 Request。
8. 消费模式
源码文件:system/core/libutils/Looper.cpp
相关函数:pollOnce()、pollInner()
if (response.request.ident >= 0) {
*outFd = response.request.fd;
*outEvents = response.events;
*outData = response.request.data;
return response.request.ident;
}
if (response.request.ident == POLL_CALLBACK) {
int result = response.request.callback->handleEvent(
response.request.fd, response.events, response.request.data);
if (result == 0) removeSequenceNumberLocked(response.seq);
}non-callback 模式把处理责任交给 pollOnce() 调用者;callback 模式在 Looper 内执行并用返回值决定保留或注销。两种模式共用 epoll 和 Request,但消费者不同。
9. 注销边界
源码文件:system/core/libutils/include/utils/Looper.h
相关函数:removeFd()
int removeFd(int fd);removeFd() 返回后,Looper 不再保存该 FD 的 Request,可以关闭 FD;但 callback 可能已经开始执行。callback 自己返回 0 或调用 removeFd() 可保证未重新注册前不再收到后续回调,无法倒退取消当前执行栈。
10. 测试断言
源码文件:system/core/libutils/Looper_test.cpp
相关测试:AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError()、PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked()
int result = mLooper->addFd(pipe.receiveFd, -1,
Looper::EVENT_INPUT, nullptr, nullptr);
EXPECT_EQ(-1, result);
handler1.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
handler2.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
pipe.writeSignal();
mLooper->pollOnce(100);
EXPECT_EQ(0, handler1.callbackCount);
EXPECT_EQ(1, handler2.callbackCount);第一组输入固定非法参数的真实返回值;第二组证明同 FD 二次注册是替换,只有第二个 callback 消费之后的事件。非 callback 测试还断言 ident、fd、events 和 data 四个输出。
11. 调试查询
源码文件:system/core/libutils/Looper.cpp
相关函数:getFdStateDebug()
bool Looper::getFdStateDebug(int fd, int* ident, int* events,
sp<LooperCallback>* cb, void** data) {
std::scoped_lock _l(mLock);
// 通过 fd -> sequence -> Request 返回当前注册状态
}该入口读取当前用户态 Request,不证明内核 FD 此刻可读,也不等待事件;适合测试注册结果,不能替代 pollOnce()。
12. 动手验证
rg -n "int Looper::addFd|EPOLL_CTL_ADD|EPOLL_CTL_MOD|mNextRequestSeq|ENOENT" \
system/core/libutils/Looper.cpp
rg -n "AddFd_When|CallbackAddedTwice|NonCallbackFdIsSignalled" \
system/core/libutils/Looper_test.cpp13. 边界说明
本文不展开 pollInner() 的完整 response 遍历、Java MessageQueue FD 桥接或内核 epoll 数据结构。固定源码支持的是 addFd 参数、注册/替换、sequence、失败恢复和消费模式。
