//进入读函数,返回当前的读计数器值 int MRSWLock_AddRead(STonyXiaoMultiReadSingleWriteLock* pLock) { while (1) { //这里是死循环等待,不过,即使是其他线程在进行写操作, //操作总会完成,因此,总有机会碰到写标志为“假”,最终跳出循环 MUTEXLOCK(&(pLock->m_Lock)); //加内部锁,进入锁域 { if (!pLock->m_bWriteFlag) //检测写标志是否为“假” { //如果为“假”,表示可以开始读 //此时,一定要先累加,再释放内部锁,避免由于空隙, //导致别的线程错误切入 pLock->m_nReadCount++; MUTEXUNLOCK(&(pLock->m_Lock)); //返回读计数器的值, //注意:这个值可能不一定是刚刚累加的值 //由于内部锁已经解除,别的读线程完全可能切进来 //将这个值增加好几次 return MRSWLock_GetRead(pLock); //这是本函数唯一跳出点 } } //如果写标志为“真”只能循环等待 MUTEXUNLOCK(&(pLock->m_Lock)); //使用特殊睡眠,后文有解释 TonyXiaoMinSleep(); } }
void MRSWLock_Read2Write(STonyXiaoMultiReadSingleWriteLock* pLock) { while (1) //死循环,和进入写中的死循环一个道理 { MUTEXLOCK(&(pLock->m_Lock)); { if (!pLock->m_bWriteFlag) { //注意这里,一旦检测到可以抢夺写权利 //先把写标志置为“真” pLock->m_bWriteFlag = true; //切记,由于是读转写,以前进入读的时候,已经把计数器+1 //这里一定要-1,否则会导致计数器永远不为0,系统挂死 if (0 < (pLock->m_nReadCount)) pLock->m_nReadCount--; //如前,所有状态设置完成,解除内部锁,跳到下一步 MUTEXUNLOCK(&(pLock->m_Lock)); goto MRSWLock_Read2Write_Wait_Read_Clean; } } //解除内部锁等待,前文已经说明 MUTEXUNLOCK(&(pLock->m_Lock)); TonyXiaoMinSleep(); } //此处开始等待所有的读退出,同“进入写”的逻辑 MRSWLock_Read2Write_Wait_Read_Clean: while (MRSWLock_GetRead(pLock)) { TonyXiaoMinSleep(); } }
void MRSWLock_DisableWrite(STonyXiaoMultiReadSingleWriteLock* pLock) { MUTEXLOCK(&(pLock->m_Lock)); { pLock->m_bWriteFlag = false; } MUTEXUNLOCK(&(pLock->m_Lock)); }
//进入写操作函数 void MRSWLock_EnableWrite(STonyXiaoMultiReadSingleWriteLock* pLock) { while (1) //死循环等待 { //第一重循环,检测“写”标志是否为“假”,尝试抢夺“写”权 //请大家放心,即使有其他线程正在写,操作总会完成 //因此,这个死循环不会永远死等,一般很快就会因为条件满足而跳出 MUTEXLOCK(&(pLock->m_Lock)); //内部锁加锁,进入锁域 { //在此域内,其他访问本单写多读锁的线程,会因为内部锁的作用被挂住 //判断是否可以抢夺“写”权利 if (!pLock->m_bWriteFlag) { //如果写标志为“假”,即可以抢夺“写”权利 //注意,此时一定不要解内部锁,应该立即将写标志置为“真” pLock->m_bWriteFlag = true; //抢到“写”权后,本轮逻辑完毕,解除内部锁后,可以退出 //关键:请注意,这里多一句解锁命令,为安全跳出使用 MUTEXUNLOCK(&(pLock->m_Lock)); //注意退出方式,使用goto 精确定位 goto MRSWLock_EnableWrite_Wait_Read_Clean; } //这是if 域结束 //此处,“写”标志为真,表示其他线程已经抢到“写”权, //本线程必须悬挂等待。 } //这是锁域结束 //等待睡眠时,应该及时释放内部锁,避免其他线程被连锁挂死 MUTEXUNLOCK(&(pLock->m_Lock)); //这是一个特殊的Sleep,下面会讲到 TonyXiaoMinSleep(); } //这是第一阶段完成标志,程序运行到此,表示“写”权已经被本程序抢到手 MRSWLock_EnableWrite_Wait_Read_Clean: //下面,开始等待其他的“读”操作完毕 while (MRSWLock_GetRead(pLock)) //请务必关注这个调用 //这是利用公有的取读状态方法来获取信息 //前文已经说明,这个函数内部,是“资源锁”模型, //即函数内进行了内部锁加锁,是线程安全的, //退出函数,内部锁自动解锁 //因此,本循环Sleep 期间,本函数没有挂住内部锁, //其他线程访问的其他公有方法,可以自由修改读计数器的值。 //这个规避逻辑异常重要! { //第二重循环,等待所有的“读”操作退出 //请放心,一旦“写”标志被置为“真”,新的“读”操作会被悬挂,不会进来 //而老的读操作,迟早会工作完毕 //因此,这个死循环,总能等到退出的时候,并不是死循环 TonyXiaoMinSleep(); } }
//获取读计数器 int MRSWLock_GetRead(STonyXiaoMultiReadSingleWriteLock* pLock) { int nRet = 0; MUTEXLOCK(&(pLock->m_Lock)); { nRet = pLock->m_nReadCount; } MUTEXUNLOCK(&(pLock->m_Lock)); return nRet; }
//获取写状态 bool MRSWLock_GetWrite(STonyXiaoMultiReadSingleWriteLock* pLock) { bool bRet = false; MUTEXLOCK(&(pLock->m_Lock)); { bRet = pLock->m_bWriteFlag; } MUTEXUNLOCK(&(pLock->m_Lock)); return bRet; }
// Unlock the library and post any events that were queued in the meantime void put_iaxc_lock() { iaxc_event *prev, *event; MUTEXLOCK(&event_queue_lock); event = event_queue; event_queue = NULL; MUTEXUNLOCK(&event_queue_lock); MUTEXUNLOCK(&iaxc_lock); while (event) { iaxci_post_event(*event); prev = event; event = event->next; free(prev); } }
//返回读计数器变化后的结果 int MRSWLock_DecRead(STonyXiaoMultiReadSingleWriteLock* pLock) { int nRet = 0; MUTEXLOCK(&(pLock->m_Lock)); { //这是一种习惯性保护,递减计算时,如果最小值是0,总是加个判断 if (0 < (pLock->m_nReadCount)) pLock->m_nReadCount--; //注意,这里是直接获得读计数器的值,看起来,比进入读要准确一点 nRet = pLock->m_nReadCount; } MUTEXUNLOCK(&(pLock->m_Lock)); return nRet; }
// Post Events back to clients void iaxci_post_event(iaxc_event e) { if ( e.type == 0 ) { iaxci_usermsg(IAXC_ERROR, "Error: something posted to us an invalid event"); return; } if ( MUTEXTRYLOCK(&iaxc_lock) ) { iaxc_event **tail; /* We could not obtain the lock. Queue the event. */ MUTEXLOCK(&event_queue_lock); tail = &event_queue; e.next = NULL; while ( *tail ) tail = &(*tail)->next; *tail = (iaxc_event *)malloc(sizeof(iaxc_event)); memcpy(*tail, &e, sizeof(iaxc_event)); MUTEXUNLOCK(&event_queue_lock); return; } /* TODO: This is not the best. Since we were able to get the * lock, we decide that it is okay to go ahead and do the * callback to the application. This is really nasty because * it gives the appearance of serialized callbacks, but in * reality, we could callback an application multiple times * simultaneously. So, as things stand, an application must * do some locking in their callback function to make it * reentrant. Barf. More ideally, iaxclient would guarantee * serialized callbacks to the application. */ MUTEXUNLOCK(&iaxc_lock); if ( iaxc_event_callback ) { int rv; rv = iaxc_event_callback(e); if ( e.type == IAXC_EVENT_VIDEO ) { /* We can free the frame data once it is off the * event queue and has been processed by the client. */ free(e.ev.video.data); } else if ( e.type == IAXC_EVENT_AUDIO ) { free(e.ev.audio.data); } if ( rv < 0 ) default_message_callback( "IAXCLIENT: BIG PROBLEM, event callback returned failure!"); // > 0 means processed if ( rv > 0 ) return; // else, fall through to "defaults" } switch ( e.type ) { case IAXC_EVENT_TEXT: default_message_callback(e.ev.text.message); // others we just ignore too return; } }
int callback(iaxc_event e) { Tcl_Obj *elm_list = NULL, *val[8]; switch (e.type) { case IAXC_EVENT_TEXT: /* * int type * int callNo * char message[IAXC_EVENT_BUFSIZ] */ val[0] = Tcl_NewStringObj("text", -1); val[1] = Tcl_NewIntObj(e.ev.text.type); val[2] = Tcl_NewIntObj(e.ev.text.callNo); val[3] = Tcl_NewStringObj(e.ev.text.message, -1); elm_list = Tcl_NewListObj(4, val); break; case IAXC_EVENT_LEVELS: /* * float input; * float output; */ val[0] = Tcl_NewStringObj("levels", -1); val[1] = Tcl_NewDoubleObj(e.ev.levels.input); val[2] = Tcl_NewDoubleObj(e.ev.levels.output); elm_list = Tcl_NewListObj(3, val); break; case IAXC_EVENT_STATE: /* * int callNo; * int state; * int format; * char remote[IAXC_EVENT_BUFSIZ]; * char remote_name[IAXC_EVENT_BUFSIZ]; * char local[IAXC_EVENT_BUFSIZ]; * char local_context[IAXC_EVENT_BUFSIZ]; */ val[0] = Tcl_NewStringObj("state", -1); val[1] = Tcl_NewIntObj(e.ev.call.callNo); val[2] = Tcl_NewIntObj(e.ev.call.state); val[3] = Tcl_NewIntObj(e.ev.call.format); val[4] = Tcl_NewStringObj(e.ev.call.remote, -1); val[5] = Tcl_NewStringObj(e.ev.call.remote_name, -1); val[6] = Tcl_NewStringObj(e.ev.call.local, -1); val[7] = Tcl_NewStringObj(e.ev.call.local_context, -1); elm_list = Tcl_NewListObj(8, val); break; case IAXC_EVENT_NETSTAT: /* * int callNo; * int rtt; * struct iaxc_netstat local; * struct iaxc_netstat remote; */ val[0] = Tcl_NewStringObj("netstat", -1); val[1] = Tcl_NewIntObj(e.ev.netstats.callNo); val[2] = Tcl_NewIntObj(e.ev.netstats.rtt); /* how can I report local and remote?? TODO */ elm_list = Tcl_NewListObj(3, val); break; case IAXC_EVENT_URL: /* * int callNo; * int type; * char url[IAXC_EVENT_BUFSIZ]; */ val[0] = Tcl_NewStringObj("url", -1); val[1] = Tcl_NewIntObj(e.ev.url.callNo); val[2] = Tcl_NewIntObj(e.ev.url.type); val[3] = Tcl_NewStringObj(e.ev.url.url, -1); elm_list = Tcl_NewListObj(4, val); break; case IAXC_EVENT_VIDEO: /* * int callNo; * int format; * int width; * int height; * unsigned char *data; */ val[0] = Tcl_NewStringObj("video", -1); val[1] = Tcl_NewIntObj(e.ev.video.callNo); val[2] = Tcl_NewIntObj(e.ev.video.format); val[3] = Tcl_NewIntObj(e.ev.video.width); val[4] = Tcl_NewIntObj(e.ev.video.height); val[5] = Tcl_NewStringObj((char *)(e.ev.video.data), -1); elm_list = Tcl_NewListObj(6, val); break; case IAXC_EVENT_REGISTRATION: /* * int id; * int reply; * int msgcount; */ val[0] = Tcl_NewStringObj("registration", -1); val[1] = Tcl_NewIntObj(e.ev.reg.id); val[2] = Tcl_NewIntObj(e.ev.reg.reply); val[3] = Tcl_NewIntObj(e.ev.reg.msgcount); elm_list = Tcl_NewListObj(4, val); break; } if (elm_list != NULL) { MUTEXLOCK(&head_mutex); Tcl_ListObjAppendElement(NULL, evt_list, elm_list); MUTEXUNLOCK(&head_mutex); } return 1; }
void MRSWLock_Destroy(STonyXiaoMultiReadSingleWriteLock* pLock) { MUTEXLOCK(&pLock->m_Lock); //还记得前文的技巧吗? MUTEXUNLOCK(&pLock->m_Lock); //利用一次空加锁解锁动作,规避风险 MUTEXDESTROY(&(pLock->m_Lock)); //摧毁内部锁 }