通过前面一系列ipcz的文章分析,我们了解到ipcz是在unix domain socket上层建立起了多个逻辑通道,所以对于上层的消息事件,不能单纯依赖io event 来触发,所以在ipcz系统中加入了trap的机制。 trap 的含义是陷阱,这个名字应该是来自cpu架构的概念,其实就是一种消息机制。 在cpu中, 执行分为中断和陷阱,陷阱是安插在程序指令流里面,当执行到了陷阱指令,就相当于掉入了陷阱中,这时候要调用陷阱处理程序来从陷阱中恢复出来。 ipcz的trap 机制则不同,它既有陷阱的含义,也有中断的含义。
我们都知道ipcz将来会取代mojo,不过现在还是混合使用的状态。 所以目前有两层Trap。 第一层是ipcz 层的trap, 用于接收端口事件, 该层主要的api是/third_party/ipcz/src/api.cc的Trap函数
IpczResult Trap(IpczHandle portal_handle,
const IpczTrapConditions* conditions,
IpczTrapEventHandler handler,
uintptr_t context,
uint32_t flags,
const void* options,
IpczTrapConditionFlags* satisfied_condition_flags,
IpczPortalStatus* status)
Trap函数如果trap 关注的事件(conditions)没有满足,则会注册成功,否则返回IPCZ_RESULT_FAILED_PRECONDITION 表示前置条件不满足,满足的条件通过satisfied_condition_flags参数返回。 这使得关注portal的事件有两个路径,一个是注册成功,后续事件触发导致回调,一个是注册失败,满足的条件通过satisfied_condition_flags 来返回。 这给编程造成了一定困难,是不好的设计。
mojo层的Trap管理多个ipcz层的trap, 每个Trap叫做一个Trigger。 它主要的api是:
MojoResult MojoTrap::AddTrigger(MojoHandle handle,
MojoHandleSignals signals,
MojoTriggerCondition condition,
uintptr_t trigger_context)
由于MojoTrap管理多个Ipcz Trap, MojoTrap使用Trigger代表一个ipcz的trap。 AddTrigger为添加一个Trigger。 由于添加时Trigger 不一定能添加进去(因为IPCZ_RESULT_FAILED_PRECONDITION), 所以又增加了一个Arm函数,用于返回当前满足的状态,并注册为满足状态的trigger 到ipcz trap。
MojoResult MojoTrap::Arm(MojoTrapEvent* blocking_events,
uint32_t* num_blocking_events)
blocking_events 用于返回满足条件的Trigger对应的事件。
废话不多说我们直接通过代码分析。
我们先来看Trap对象的创建。
mojo/core/core_ipcz.cc
MojoResult MojoAddTriggerIpcz(MojoTrapEventHandler handler,
const MojoCreateTrapOptions* options,
MojoHandle* trap_handle) {
if (!handler || !trap_handle) {
return MOJO_RESULT_INVALID_ARGUMENT;
}
*trap_handle = ipcz_driver::MojoTrap::MakeBoxed(handler);
return MOJO_RESULT_OK;
}
MojoCreateTrapIpcz的作用是创建一个Trap对象,有了这个Trap对象后,就可以想这个trap对象注册一些事件观察,当事件发生的时候就会向这些观察者发出通知。
参数handler 代表触发trap时的处理函数, 这是一个回调函数。 trap_handle 是一个传输参数,是安装trap的句柄,用于向这个trap注册一下事件监听。
函数直接通过ipcz_driver::MojoTrap::MakeBoxed(handler) 创建MojoTrap, MakeBoxed我们前面已经分析过了,会调用MojoTrap的构造方法, 我们看一下MojoTrap的构造方法。
MojoTrap::MojoTrap(MojoTrapEventHandler handler) : handler_(handler) {}
也很简单,只是设置了handler_成员变量。
我们再看下一个Trap相关api
MojoResult MojoAddTriggerIpcz(MojoHandle trap_handle,
MojoHandle handle,
MojoHandleSignals signals,
MojoTriggerCondition condition,
uintptr_t context,
const MojoAddTriggerOptions* options) {
auto* trap = ipcz_driver::MojoTrap::FromBox(trap_handle);
if (!trap) {
return MOJO_RESULT_INVALID_ARGUMENT;
}
return trap->AddTrigger(handle, signals, condition, context);
}
MojoAddTriggerIpcz 函数顾名思义就是向trap添加一个触发器,用于注册trap 事件观察者,参数trap_handle 是用于指向MojoTrap的句柄,handle 是 portal的句柄,也就是添加的触发器针对与portal 上面的事件。signals则代表观察的具体事件,比如portal可读、可写等。 condition 就是表示触发器具体触发的条件,比如既可以在管道可读时触发,也可以在管道变为不可读时触发。 context参数用于回调函数的参数,是注册触发器时候给的上下文信息,这是回调场景中常用的手段。
MojoAddTriggerIpcz 函数代码逻辑也很简单,就是调用MojoTrap->AddTrigger() 方法添加触发器。在分析addTrigger之前我们先给出MojoHandleSignals 和MojoTriggerCondition的枚举值。
MojoHandleSignals:
MojoTriggerCondition:
对于信号和触发条件我们有了了解,继续分析MojoTrap->AddTrigger()函数。
195 MojoResult MojoTrap::AddTrigger(MojoHandle handle,
196 MojoHandleSignals signals,
197 MojoTriggerCondition condition,
198 uintptr_t trigger_context) {
.....
205 auto* data_pipe = DataPipe::FromBox(handle);
206 scoped_refptr<DataPipe::PortalWrapper> control_portal;
// 如果handle 是data_pipem 获取control_portal
207 if (data_pipe) {
208 control_portal = data_pipe->GetPortal();
209 if (!control_portal) {
210 return MOJO_RESULT_INVALID_ARGUMENT;
211 }
212 handle = control_portal->handle();
213 } else if (ObjectBase::FromBox(handle)) {
214 // Any other type of driver object cannot have traps installed.
215 return MOJO_RESULT_INVALID_ARGUMENT;
216 }
217
// 创建Trigger对象
218 auto trigger = base::MakeRefCounted<Trigger>(this, handle, data_pipe, signals,
219 trigger_context);
220
221 if (condition == MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED) {
// MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED 这个功能废弃了
227 } else if (data_pipe) {
// data_pipe 类型处理
228 GetConditionsForDataPipeSignals(signals, &trigger->conditions);
229 } else {
// portal 处理
230 GetConditionsForMessagePipeSignals(signals, &trigger->conditions);
231 }
232
233 base::AutoLock lock(lock_);
// 维护triggers_ 和 trigger的关系
234 auto [it, ok] = triggers_.try_emplace(trigger_context, trigger);
235 if (!ok) {
236 return MOJO_RESULT_ALREADY_EXISTS;
237 }
238
239 next_trigger_ = triggers_.begin();
240
241 // Install an ipcz trap to effectively monitor the lifetime of the watched
242 // object referenced by `handle`. Installation of the trap should always
243 // succeed, and its resulting trap event will always mark the end of this
244 // trigger's lifetime. This trap effectively owns a ref to the Trigger, as
245 // added here.
246 trigger->AddRef();
247 IpczTrapConditions removal_conditions = {
248 .size = sizeof(removal_conditions),
249 .flags = IPCZ_TRAP_REMOVED,
250 };
// 向ipcz 注册Trap, TrapRemovalEventHandler是trap被移除的回调函数
251 IpczResult result = GetIpczAPI().Trap(
252 handle, &removal_conditions, &TrapRemovalEventHandler,
253 trigger->ipcz_context(), IPCZ_NO_FLAGS, nullptr, nullptr, nullptr);
254 CHECK_EQ(result, IPCZ_RESULT_OK);
255
256 if (!armed_) {// 有其他trigger 没有安装上, 则返回MOJO_RESULT_OK,暂不安装trigger。
257 return MOJO_RESULT_OK;
258 }
259
260 // The Mojo trap is already armed, so attempt to install an ipcz trap for
261 // the new trigger immediately.
262 MojoTrapEvent event;
// 安装trigger
263 result = ArmTrigger(*trigger, event);
264 if (result == IPCZ_RESULT_OK) { // 没获取到,说明事件不属于该触发器,直接返回
265 return MOJO_RESULT_OK;
266 }
267
268 // The new trigger already needs to fire an event. OK.
// 该触发器有事件要处理,派发时间
269 armed_ = false;
270 DispatchOrQueueEvent(*trigger, event);
271 return MOJO_RESULT_OK;
272 }
Trap api分为两层。 上层是MojoTrap, 它可以管理多个ipcz trap, ipcz trap 在MojoTrap中叫做Trigger。
触发器支持DataPipe和Portal的事件触发。218行创建Trigger对象,251-253行向Ipcz层注册Trigger对象, 注意这里的IpczTrapConditions->flags 为IPCZ_TRAP_REMOVED, 这表示不关注portal其他事件,只关注ipcz trap被移除的事件 。 256-258行如果MojoTrap上有其他trigger 没有安装到ipcz trap, 则直接返回,等待一起安装。 262-266 其他Trigger 都成功安装则这里可以安装当前Trigger到ipcz trap,这里使用ArmTrigger 函数安装ipcz trap, 如果安装成功直接返回,安装失败则表示当前trigger 已经被触发,调用DispatchOrQueueEvent派发事件。
我们先来看一下事件的派发。
528 void MojoTrap::DispatchOrQueueEvent(Trigger& trigger,
529 const MojoTrapEvent& event) {
530 lock_.AssertAcquired();
531 if (dispatching_thread_ == base::PlatformThread::CurrentRef()) {
532 // This thread is already dispatching an event, so queue this one. It will
533 // be dispatched before the thread fully unwinds from its current dispatch.
// 当前线程正在派发事件,添加到pending_mojo_events_中
534 pending_mojo_events_->emplace_back(base::WrapRefCounted(&trigger), event);
535 return;
536 }
537
538 // Block as long as any other thread is dispatching.
// 同时只能有一个线程派发事件,其他线程在派发,等待
539 while (dispatching_thread_.has_value()) {
540 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
541 waiters_++;
542 dispatching_condition_.Wait();
543 waiters_--;
544 }
545
546 dispatching_thread_ = base::PlatformThread::CurrentRef();
// 派发事件
547 DispatchEvent(event);
548
549 // NOTE: This vector is only shrunk by the clear() below, but it may
550 // accumulate more events during each iteration. Hence we iterate by index.
551 for (size_t i = 0; i < pending_mojo_events_->size(); ++i) {
552 if (!pending_mojo_events_[i].trigger->removed ||
553 pending_mojo_events_[i].event.result == MOJO_RESULT_CANCELLED) {
// 派发pending_mojo_events_
554 DispatchEvent(pending_mojo_events_[i].event);
555 }
556 }
557 pending_mojo_events_->clear();
558
559 // We're done. Give other threads a chance.
560 dispatching_thread_.reset();
561 if (waiters_ > 0) {
// 通知等待者
562 dispatching_condition_.Signal();
563 }
564 }
DispatchOrQueueEvent函数代码很简单,保证同时只有一个线程派发事件,通过调用DispatchEvent函数进行事件派发。
566 void MojoTrap::DispatchEvent(const MojoTrapEvent& event) {
567 lock_.AssertAcquired();
568 DCHECK(dispatching_thread_ == base::PlatformThread::CurrentRef());
569
570 // Note that other threads may enter DispatchOrQueueEvent while this is
571 // unlocked; but they will be blocked from dispatching since we've set
572 // `dispatching_thread_` to our thread.
573 base::AutoUnlock unlock(lock_);
574 handler_(&event);
575 }
DispatchEvent 调用创建MojoTrap时候传入的回调函数。 参数为MojoTrapEvent。
再来看ipcz trap的注册。
third_party/ipcz/src/api.cc
我们继续分析向ipcz层注册Trap。
257 IpczResult Trap(IpczHandle portal_handle,
258 const IpczTrapConditions* conditions,
259 IpczTrapEventHandler handler,
260 uintptr_t context,
261 uint32_t flags,
262 const void* options,
263 IpczTrapConditionFlags* satisfied_condition_flags,
264 IpczPortalStatus* status) {
265 ipcz::Portal* portal = ipcz::Portal::FromHandle(portal_handle);
266 if (!portal || !handler || !conditions ||
267 conditions->size < sizeof(*conditions)) {
268 return IPCZ_RESULT_INVALID_ARGUMENT;
269 }
270
271 if (status && status->size < sizeof(*status)) {
272 return IPCZ_RESULT_INVALID_ARGUMENT;
273 }
274
275 return portal->router()->Trap(*conditions, handler, context,
276 satisfied_condition_flags, status);
277 }
参数portal_handle 指向portal,conditions 表示关注portal的信号条件,比如trap被移除的回调。 trap 本身关心的事件在context的成员变量中。satisfied_condition_flags 是传出参数,表示给定的conditions下当前满足的条件。status用于查询端口的状态。
函数也比较简单,调用router->Trap 方法注册ipcz层的trap。
third_party/ipcz/src/ipcz/router.cc
IpczResult Router::Trap(const IpczTrapConditions& conditions,
IpczTrapEventHandler handler,
uint64_t context,
IpczTrapConditionFlags* satisfied_condition_flags,
IpczPortalStatus* status) {
absl::MutexLock lock(&mutex_);
return traps_.Add(conditions, handler, context, status_,
satisfied_condition_flags, status);
}
Router对象使用traps_维护多个Trap, 我们来看一下它的代码.
third_party/ipcz/src/ipcz/trap_set.cc
63 IpczResult TrapSet::Add(const IpczTrapConditions& conditions,
64 IpczTrapEventHandler handler,
65 uintptr_t context,
66 const IpczPortalStatus& current_status,
67 IpczTrapConditionFlags* satisfied_condition_flags,
68 IpczPortalStatus* status) {
69 last_known_status_ = current_status;
70 IpczTrapConditionFlags flags =
71 GetSatisfiedConditions(conditions, current_status);
72 if (flags != 0) {
73 if (satisfied_condition_flags) {
74 *satisfied_condition_flags = flags;
75 }
76 if (status) {
77 // Note that we copy the minimum number of bytes between the size of our
78 // IpczPortalStatus and the size of the caller's, which may differ if
79 // coming from another version of ipcz. The `size` field is updated to
80 // reflect how many bytes are actually meaningful here.
81 const uint32_t size = std::min(status->size, sizeof(current_status));
82 memcpy(status, ¤t_status, size);
83 status->size = size;
84 }
85 return IPCZ_RESULT_FAILED_PRECONDITION;
86 }
87
88 traps_.emplace_back(conditions, handler, context);
89 return IPCZ_RESULT_OK;
90 }
参数current_status 是出入参数,为Router对象的status_成员变量。 status为传出参数。conditions则表示关注的条件。
70-71行通过router的状态和关注的条件,返回满足的条件。
73-74行给传出参数satisfied_condition_flags 赋值,设置为conditions下当前满足的条件。
76-84行给传出参数status赋值。
注意72-85行,整体上如果当前已经有满足的条件,则不注册trap,返回IPCZ_RESULT_FAILED_PRECONDITION,也就是前置条件不满足。
最后如果没有满足的条件则trap注册成功。向traps_成员变量添加一个Trap对象。
在 MojoTrap::AddTrigger中给定的条件为IPCZ_TRAP_REMOVED, 这个条件在trap被移除时触发,所以这里一定会添加trap成功。 也就是MojoTrap::AddTrigger 只关注Ipcz Trap被移除事件,也就是它的生命周期。 对应的MojoTrap::TrapRemovalEventHandler(const IpczTrapEvent* event) 函数我们就不分析了,它的作用主要是用于清理资源。
我们再看ArmTrigger函数,真正注册Trigger 到ipcz trap。
453 IpczResult MojoTrap::ArmTrigger(Trigger& trigger, MojoTrapEvent& event) {
454 lock_.AssertAcquired();
......
469 DataPipe* const data_pipe = trigger.data_pipe.get();
......
474 if (!data_pipe && (trigger.signals & MOJO_HANDLE_SIGNAL_WRITABLE)) {
// data_pipe 永远可写,直接返回IPCZ_RESULT_FAILED_PRECONDITION
475 // Message pipes are always writable, so a trap watching for writability can
476 // never be armed.
477 IpczPortalStatus status = {.size = sizeof(status)};
478 const IpczResult result = GetIpczAPI().QueryPortalStatus(
479 trigger.handle, IPCZ_NO_FLAGS, nullptr, &status);
480 if (result == IPCZ_RESULT_OK) {
481 PopulateEventForMessagePipe(trigger.signals, status, event);
482 }
483 return IPCZ_RESULT_FAILED_PRECONDITION;
484 }
485
486 // Bump the ref count on the Trigger. This ref is effectively owned by the
487 // trap if it's installed successfully.
488 trigger.AddRef();
489 IpczTrapConditionFlags satisfied_flags;
490 IpczPortalStatus status = {.size = sizeof(status)};
// 依然调用ipcz 层的Trap函数,不过这里使用的conditions为真正的trigger conditions
491 IpczResult result =
492 GetIpczAPI().Trap(trigger.handle, &trigger.conditions, &TrapEventHandler,
493 trigger.ipcz_context(), IPCZ_NO_FLAGS, nullptr,
494 &satisfied_flags, &status);
495 if (result == IPCZ_RESULT_OK) {
496 trigger.armed = true;
497 return MOJO_RESULT_OK;
498 }
499
500 // Balances the AddRef above since no trap was installed.
501 trigger.Release();
502
503 if (data_pipe) {
504 PopulateEventForDataPipe(*data_pipe, trigger.signals, event);
505 } else {
506 PopulateEventForMessagePipe(trigger.signals, status, event);
507 }
508 return result;
509 }
函数依然调用ipcz层的Trap函数去注册trigger, 不过这里的conditions已经换成了trigger的真正conditions。同样conditions如果满足则会返回IPCZ_RESULT_FAILED_PRECONDITION。函数503-507行就是处理这种情况, 我们只看PopulateEventForMessagePipe函数
void PopulateEventForMessagePipe(MojoHandleSignals trigger_signals,
const IpczPortalStatus& current_status,
MojoTrapEvent& event) {
const MojoHandleSignals kRead = MOJO_HANDLE_SIGNAL_READABLE;
const MojoHandleSignals kWrite = MOJO_HANDLE_SIGNAL_WRITABLE;
const MojoHandleSignals kPeerClosed = MOJO_HANDLE_SIGNAL_PEER_CLOSED;
MojoHandleSignals& satisfied = event.signals_state.satisfied_signals;
MojoHandleSignals& satisfiable = event.signals_state.satisfiable_signals;
satisfied = 0;
satisfiable = kPeerClosed | MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED;
if (!(current_status.flags & IPCZ_PORTAL_STATUS_DEAD)) {
satisfiable |= kRead;
}
if (current_status.flags & IPCZ_PORTAL_STATUS_PEER_CLOSED) {
satisfied |= kPeerClosed;
} else {
satisfiable |= MOJO_HANDLE_SIGNAL_PEER_REMOTE | kWrite;
satisfied |= kWrite;
}
if (current_status.num_local_parcels > 0) {
satisfied |= kRead;
}
DCHECK((satisfied & satisfiable) == satisfied);
GetEventResultForSignalsState(event.signals_state, trigger_signals,
event.result);
}
函数主要根据当前状态装填MojoTrapEvent这个传输参数。 所以在MojoTrap::AddTrigger() 函数中可以直接派发event。
假如MojoTrap.armed_为false什么时候去注册Trap呢,一般这种情况需要主动调用MojoArmTrapIpcz函数。 MojoArmTrapIpcz函数的作用是注册MojoTrap的trigger 到ipcz trap。 如果有trigger 已经满足条件,则返回满足条件的MojoTrapEvent。 把能注册上的Trigger 进行注册。
MojoResult MojoArmTrapIpcz(MojoHandle trap_handle,
const MojoArmTrapOptions* options,
uint32_t* num_blocking_events,
MojoTrapEvent* blocking_events) {
auto* trap = ipcz_driver::MojoTrap::FromBox(trap_handle);
if (!trap) {
return MOJO_RESULT_INVALID_ARGUMENT;
}
return trap->Arm(blocking_events, num_blocking_events);
}
函数参数trap_handle 为创建的MojoTrap的句柄, blocking_events 用户返回已经满足条件的Trigger 所触发的时间, num_blocking_events为blocking_events 的容量。
这个设计看起来很不统一。
mojo/core/ipcz_driver/mojo_trap.cc
289 MojoResult MojoTrap::Arm(MojoTrapEvent* blocking_events,
290 uint32_t* num_blocking_events) {
......
302 base::AutoLock lock(lock_);
303 if (armed_) { // 已经有事件被触发,并且没被处理,则没有阻塞事件,返回MOJO_RESULT_OK
304 return MOJO_RESULT_OK;
305 }
306
// 没有注册的触发器,返回MOJO_RESULT_NOT_FOUND
307 if (triggers_.empty()) {
308 return MOJO_RESULT_NOT_FOUND;
309 }
310
311 uint32_t num_events_returned = 0;
312 auto increment_wrapped = [this](TriggerMap::iterator it) {
313 lock_.AssertAcquired();
314 if (++it != triggers_.end()) {
315 return it;
316 }
317 return triggers_.begin();
318 };
319
320 TriggerMap::iterator next_trigger = next_trigger_;
321 DCHECK(next_trigger != triggers_.end());
322
323 // We iterate over all triggers, starting just beyond wherever we started last
324 // time we were armed. This guards against any single trigger being starved.
325 const TriggerMap::iterator end_trigger = next_trigger;
326 do {
327 auto& [trigger_context, trigger] = *next_trigger;
328 next_trigger = increment_wrapped(next_trigger);
329
330 MojoTrapEvent event;
// 安装trigger
331 const IpczResult result = ArmTrigger(*trigger, event);
332 if (result == IPCZ_RESULT_OK) { // 已经触发,下一个
333 // Trap successfully installed, nothing else to do for this trigger.
334 continue;
335 }
336
337 if (result != IPCZ_RESULT_FAILED_PRECONDITION) {
338 NOTREACHED();
339 return result;
340 }
341
342 // The ipcz trap failed to install, so this trigger's conditions are already
343 // met. Accumulate would-be event details if there's output space.
344 if (event_capacity == 0) {
345 return MOJO_RESULT_FAILED_PRECONDITION;
346 }
347
348 blocking_events[num_events_returned++] = event;
349 } while (next_trigger != end_trigger &&
350 (num_events_returned == 0 || num_events_returned < event_capacity));
351
352 if (next_trigger != end_trigger) {
353 next_trigger_ = next_trigger;
354 } else {
355 next_trigger_ = increment_wrapped(next_trigger);
356 }
357
358 if (num_events_returned > 0) {
359 *num_blocking_events = num_events_returned;
360 return MOJO_RESULT_FAILED_PRECONDITION;
361 }
362
363 // The whole Mojo trap is collectively armed if and only if all of the
364 // triggers managed to install an ipcz trap.
// 全部都安装上了
365 armed_ = true;
366 return MOJO_RESULT_OK;
367 }
函数主要调用ArmTrigger去安装ipcz trap, 如果安装失败则代表该Trigger 已经触发,或者其他前置条件不满足,原因会通过MojoTrapEvent传出。 如果全部Trigger 都注册成功则设置armed_=true。