> 文档中心 > 软总线源码分析5: 组网状态机

软总线源码分析5: 组网状态机

组网过程首先需要经过设备认证,认证成功后又要交换设备状态信息,成功后设备间才算组完成工。为了管理组网过程中状态的迁移过程和对应处理,组网模块使用状态机来完成整体的业务逻辑。

一.状态机机制

状态机由函数LnnFsmInit完成初始化,其源码如下:

int32_t LnnFsmInit(FsmStateMachine *fsm, char *name, FsmDinitCallback cb){    if (fsm == NULL || name == NULL) { return SOFTBUS_INVALID_PARAM;    }    (void)memset_s(fsm, sizeof(*fsm), 0, sizeof(*fsm));    ListInit(&fsm->stateList);    fsm->looper = GetLooper(LOOP_TYPE_DEFAULT);    if (fsm->looper == NULL) { LOG_ERR("get looper fail"); return SOFTBUS_ERR;    }    fsm->handler.name = name;    fsm->handler.HandleMessage = FsmStateMsgHandler;    fsm->handler.looper = fsm->looper;    fsm->deinitCallback = cb;    return SOFTBUS_OK;}

我们可以看到,状态机的结构体为FsmStateMachine,其内部有一个指向Default Looper的指针和对应的handler,为什么状态机要维护Looper和handler呢?

我们可以看一下另外两个接口:1.CreateFsmHandleMsg生成一条提交给状态机的消息;2.PostMessageToFsm将消息提交给状态机。

CreateFsmHandleMsg的源码如下:

static SoftBusMessage *CreateFsmHandleMsg(FsmStateMachine *fsm,    int32_t what, uint64_t arg1, uint64_t arg2, void *obj){    SoftBusMessage *msg = NULL;    FsmCtrlMsgObj *ctrlMsgObj = NULL;    msg = SoftBusCalloc(sizeof(*msg));    if (msg == NULL) { return NULL;    }    msg->what = what;    msg->arg1 = arg1;    msg->arg2 = arg2;    msg->handler = &fsm->handler;    msg->FreeMessage = FreeFsmHandleMsg;    ctrlMsgObj = SoftBusMalloc(sizeof(*ctrlMsgObj));    if (ctrlMsgObj == NULL) { SoftBusFree(msg); return NULL;    }    ctrlMsgObj->fsm = fsm;    ctrlMsgObj->obj = obj;    msg->obj = ctrlMsgObj;    return msg;}

CreateFsmHandleMsg生成一条消息,其结构体为SoftBusMessage,其handler即为状态机维护的handler,关键的HandleMessage函数指针指向的函数为FsmStateMsgHandler。

PostMessageToFsm的源码如下:

static int32_t PostMessageToFsm(FsmStateMachine *fsm, int32_t what, uint64_t arg1, uint64_t arg2, void *obj){    SoftBusMessage *msg = NULL;    msg = CreateFsmHandleMsg(fsm, what, arg1, arg2, obj);    if (msg == NULL) { LOG_ERR("create fsm handle msg fail"); return SOFTBUS_ERR;    }    fsm->looper->PostMessage(fsm->looper, msg);    return SOFTBUS_OK;}

即调用looper的PostMessage方法,将消息提交给状态机的Looper。

关于Looper的机制,我们在前面的章节介绍过,每当有消息提交到Looper内部的消息队列后,LoopTask函数会根据当前的时间和消息中的时间戳,来判断是否处理该消息。如果消息中的时间戳早于或等于当前时间的话,则调用消息中的HandleMessage函数指针处理该消息。

如前面所说,我们状态机的HandleMessage函数指针指向的函数为FsmStateMsgHandler,其源码如下所示:

static void FsmStateMsgHandler(SoftBusMessage *msg){    if (msg == NULL) { return;    }    if (msg->what != FSM_CTRL_MSG_DATA) { LOG_INFO("process fsm ctrl msg: %d", msg->what);    }    switch (msg->what) { case FSM_CTRL_MSG_START:     ProcessStartMessage(msg);     break; case FSM_CTRL_MSG_CHANGE_STATE:     ProcessChangeStateMessage(msg);     break; case FSM_CTRL_MSG_DATA:     ProcessDataMessage(msg);     break; case FSM_CTRL_MSG_STOP:     ProcessStopMessage(msg);     break; case FSM_CTRL_MSG_DEINIT:     ProcessDeinitMessage(msg);     break; default:     break;    }}

即根据消息中的what字段,选择相应的处理。主要分为五种类型:状态启动,状态迁移,消息处理,消息停止,卸载:

下面依次介绍这几种类型对应的函数处理:

1.状态启动

状态启动的函数为ProcessStartMessage,其源码如下:

static void ProcessStartMessage(SoftBusMessage *msg){    FsmCtrlMsgObj *ctrlMsgObj = msg->obj;    FsmStateMachine *fsm = NULL;    FsmState *state = NULL;    if (ctrlMsgObj == NULL) { return;    }    fsm = ctrlMsgObj->fsm;    state = (FsmState *)ctrlMsgObj->obj;    if (fsm == NULL || state == NULL) { return;    }    if (fsm->curState != NULL || (fsm->flag & FSM_FLAG_RUNNING) != 0) { LOG_ERR("unexpected state"); return;    }    if (IsDuplicateState(fsm, state) == true) { fsm->curState = state; if (fsm->curState->enter != NULL) {     fsm->curState->enter(); } fsm->flag |= FSM_FLAG_RUNNING;    }}

即查看消息中指示的状态是否属于状态机,若属于则将状态机的状态设置为消息指示的状态,并调用该状态下的enter()函数。

2.状态迁移

状态迁移的函数为ProcessChangeStateMessage,其源码如下:

static void ProcessChangeStateMessage(SoftBusMessage *msg){    FsmCtrlMsgObj *ctrlMsgObj = msg->obj;    FsmStateMachine *fsm = NULL;    FsmState *state = NULL;    if (ctrlMsgObj == NULL) { return;    }    fsm = ctrlMsgObj->fsm;    state = (FsmState *)ctrlMsgObj->obj;    if (fsm == NULL || state == NULL) { return;    }    if (fsm->curState == NULL || (fsm->flag & FSM_FLAG_RUNNING) == 0) { LOG_ERR("unexpected state"); return;    }    if (IsDuplicateState(fsm, state)) { if (fsm->curState->exit != NULL) {     fsm->curState->exit(); } fsm->curState = state; if (fsm->curState->enter != NULL) {     fsm->curState->enter(); }    }}

即消息中的状态属于状态机,则调用当前状态的exit()函数,然后将状态机的状态设置为消息指示的状态,并调用该状态下的enter()函数。

3.消息处理

消息处理的函数为ProcessDataMessage,其源码如下:

static void ProcessDataMessage(SoftBusMessage *msg){    FsmCtrlMsgObj *ctrlMsgObj = msg->obj;    FsmStateMachine *fsm = NULL;    if (ctrlMsgObj == NULL) { return;    }    fsm = ctrlMsgObj->fsm;    if (fsm == NULL) { return;    }    if (fsm->curState == NULL || (fsm->flag & FSM_FLAG_RUNNING) == 0) { LOG_ERR("unexpected state"); return;    }    if (fsm->curState->process != NULL) { fsm->curState->process((int32_t)msg->arg1, ctrlMsgObj->obj);    }}

即调用当前状态下的process()函数。

4.消息停止

消息停止的函数为ProcessStopMessage,其源码如下:

static void ProcessStopMessage(SoftBusMessage *msg){    FsmCtrlMsgObj *ctrlMsgObj = msg->obj;    FsmStateMachine *fsm = NULL;    if (ctrlMsgObj == NULL) { return;    }    fsm = ctrlMsgObj->fsm;    if (fsm == NULL) { return;    }    if (fsm->curState == NULL || (fsm->flag & FSM_FLAG_RUNNING) == 0) { LOG_ERR("unexpected state"); return;    }    fsm->curState = NULL;    fsm->flag &= ~FSM_FLAG_RUNNING;}

该函数将状态机的状态置为NULL,并取消RUNNING标志。

5.卸载

卸载的函数为ProcessDeinitMessage,其源码如下所示:

static void ProcessDeinitMessage(SoftBusMessage *msg){    FsmCtrlMsgObj *ctrlMsgObj = msg->obj;    FsmStateMachine *fsm = NULL;    if (ctrlMsgObj == NULL) { return;    }    fsm = ctrlMsgObj->fsm;    if (fsm == NULL) { return;    }    if (fsm->deinitCallback != NULL) { fsm->deinitCallback(fsm);    }}

该函数调用状态机的deinitCallback回调。

那么,为什么状态机需要使用Looper,在LooperTask中完成状态机的种种处理函数呢,其实原因也很简单:因为LooperTask运行在Looper的工作线程中,通过将状态机的种种处理从进程的主线程挪到工作线程中,从而不阻塞主线程的,保证软总线的服务进程的性能。

组网状态机提供了几个常用的接口,分别如下:

//状态机初始化int32_t LnnFsmInit(FsmStateMachine *fsm, char *name, FsmDinitCallback cb);//状态机卸载int32_t LnnFsmDeinit(FsmStateMachine *fsm);//向状态机中添加状态int32_t LnnFsmAddState(FsmStateMachine *fsm, FsmState *state);//启动状态机int32_t LnnFsmStart(FsmStateMachine *fsm, FsmState *initialState);//停止状态机int32_t LnnFsmStop(FsmStateMachine *fsm);//向状态机发送消息int32_t LnnFsmPostMessage(FsmStateMachine *fsm, int32_t msgType, void *data);//向状态机发送消息,该消息延迟至少delayMillis毫秒后,进行处理int32_t LnnFsmPostMessageDelay(FsmStateMachine *fsm, int32_t msgType,void *data, uint64_t delayMillis);//删除状态机中已发送的某条消息int32_t LnnFsmRemoveMessage(FsmStateMachine *fsm, int32_t msgType);//将状态机迁移到目标状态int32_t LnnFsmTransactState(FsmStateMachine *fsm, FsmState *state);

其接口代码比较简单,因此比展开介绍,感兴趣的读者可以自行研究。

二.组网状态机流程

二.组网状态机流程