該系列文章主要記錄閱讀理解ceph代碼時可能遇到的一些難點,可能跳躍比較大。如果有描述錯誤或任何疑問歡迎交流討論。
狀態機本身機制是比較好理解的,看狀態機代碼時只要理解幾個特殊的地方就很容易。
默認子狀態
狀態機的部分狀態有默認子狀態,從該狀態到其默認子狀態是不需要事件觸發的。比如:
struct Primary : boost::statechart::state< Primary, Started, Peering >, NamedState
{
Primary(my_context ctx);
void exit();
typedef boost::mpl::list <
boost::statechart::custom_reaction< ActMap >,
boost::statechart::custom_reaction< MNotifyRec >,
boost::statechart::transition< NeedActingChange, WaitActingChange >
> reactions;
boost::statechart::result react(const ActMap&);
boost::statechart::result react(const MNotifyRec&);
};
這里定義了Primary狀態,其父狀態是Started,默認子狀態是Peering。因而從Primary進入Peering狀態不需要任何事件觸發。
狀態跳轉
狀態機是由事件驅動的,定義事件的響應方法有2種方式。
- 注冊事件處理函數,比如上面Primary在處理ActMap事件就注冊了boost::statechart::result react(const ActMap&)函數。
- 跳轉到另一個狀態。比如上面Primary處理NeedActingChange事件。
在事件處理中直接跳轉狀態是一種方式。
還有一種狀態跳轉方式,就是顯式的調用transit。比如:
boost::statechart::result PG::RecoveryState::Reset::react(const ActMap&)
{
PG *pg = context< RecoveryMachine >().pg;
...
...
pg->update_heartbeat_peers();
pg->take_waiters();
return transit< Started >();
}
事件處理
注意事件不一定都是在當前狀態下處理,如果當前狀態處理不了,可以在其父狀態處理,相當于子狀態繼承了父狀態的事件處理方法。
這里還有一個小疑問,我們看到有些地方執行post_event, 這些event是什么時候由誰執行的呢? 可以想象應該是狀態機在處理完當前事件之后就開始執行了。查看boost源碼也確實如此。
post_event的實現:
void post_event_impl( const event_base_ptr_type & pEvent )
{
BOOST_ASSERT( get_pointer( pEvent ) != 0 );
eventQueue_.push_back( pEvent );
}
eventQueue_是在process_event中循環處理的。
void process_event( const event_base_type & evt )
{
if ( send_event( evt ) == detail::do_defer_event )
{
deferredEventQueue_.push_back( evt.intrusive_from_this() );
}
process_queued_events();
}
void process_queued_events()
{
while ( !eventQueue_.empty() )
{
event_base_ptr_type pEvent = eventQueue_.front();
eventQueue_.pop_front();
if ( send_event( *pEvent ) == detail::do_defer_event )
{
deferredEventQueue_.push_back( pEvent );
}
}
}
有了上面的基礎之后,再去找各種狀態是怎么變化的就非常簡單了。
狀態機的事務
還有一點對于理解ceph狀態機也比較重要,我們經常看到狀態內部執行事務,但是沒看到事務在哪里提交。答案在這里:
if (!advance_pg(curmap->get_epoch(), pg, handle, &rctx, &split_pgs))
{
// we need to requeue the PG explicitly since we didn't actually
// handle an event
peering_wq.queue(pg);
}
else
{
assert(!pg->peering_queue.empty());
PG::CephPeeringEvtRef evt = pg->peering_queue.front();
pg->peering_queue.pop_front();
pg->handle_peering_event(evt, &rctx);
}
need_up_thru = pg->need_up_thru || need_up_thru;
same_interval_since = MAX(pg->info.history.same_interval_since,
same_interval_since);
pg->write_if_dirty(*rctx.transaction);
if (!split_pgs.empty())
{
rctx.on_applied->add(new C_CompleteSplits(this, split_pgs));
split_pgs.clear();
}
dispatch_context_transaction(rctx, pg, &handle);
就在最后一行。
然后,好像沒了,自己對著狀態機那個圖研究吧 : )