├── ActorBase.cpp ├── ActorBase.h ├── ActorContext.cpp ├── ActorContext.h ├── ActorGroup.cpp ├── ActorGroup.h ├── ActorLogicBase.h ├── README.md ├── SpinLock.h ├── TaskAsync.cpp ├── TaskAsync.h ├── TaskBase.cpp ├── TaskBase.h ├── TaskClone.h ├── TaskPull.h ├── TaskWithCallback.h ├── TestBase.h ├── TimerExcuteQue.cpp ├── TimerExcuteQue.h ├── lc-addrlabels.h ├── lc-switch.h ├── lc.h ├── pt-sem.h ├── pt.h └── sample ├── TestPull.cpp └── TestPush.cpp /ActorBase.cpp: -------------------------------------------------------------------------------- 1 | #include "ActorBase.h" 2 | #include "TaskAsync.h" 3 | #include "ActorGroup.h" 4 | #include "TimerExcuteQue.h" 5 | #include 6 | 7 | extern TimerExcuteQue* g_pGlobalTimer; 8 | 9 | void ActorBase::_PushReq(TaskBase* msg) 10 | { 11 | m_queue[m_tail] = msg; 12 | if (++m_tail >= m_cap) { 13 | m_tail = 0; 14 | } 15 | if (m_head == m_tail) { 16 | ExpandQueue(); 17 | } 18 | 19 | m_group->FreeToRun(this); 20 | } 21 | 22 | bool ActorBase::PushReq(TaskBase* msg) 23 | { 24 | m_spinlock.Lock(); 25 | _PushReq(msg); 26 | m_spinlock.UnLock(); 27 | return true; 28 | } 29 | 30 | bool ActorBase::PushReqAndSetRelease(TaskBase* msg, bool release) 31 | { 32 | m_spinlock.Lock(); 33 | m_release = release; 34 | _PushReq(msg); 35 | m_spinlock.UnLock(); 36 | return true; 37 | } 38 | 39 | bool ActorBase::PushReqArrayAndSetRelease(TaskBase** msg, int count, bool release) 40 | { 41 | m_spinlock.Lock(); 42 | m_release = release; 43 | for (int i = 0; i < count; ++i) { 44 | _PushReq(msg[i]); 45 | } 46 | m_spinlock.UnLock(); 47 | return true; 48 | } 49 | 50 | bool ActorBase::PushAck(TaskPullBase* msg) 51 | { 52 | m_spinlock.Lock(); 53 | if (m_tail != m_head) { 54 | TaskBase* head = m_queue[m_head]; 55 | assert(head->m_taskType == task_async); 56 | TaskAsync* asyncTask = static_cast(head); 57 | if (asyncTask->m_session == msg->m_session) { 58 | if (asyncTask->m_srcAckSeq == msg->m_dstAckSeq) { 59 | if (asyncTask->AppendAck(msg)) { 60 | m_group->BlockToRunOnAckAll(this); 61 | } 62 | m_spinlock.UnLock(); 63 | return true; 64 | } 65 | } 66 | } 67 | m_spinlock.UnLock(); 68 | return false; 69 | } 70 | 71 | //0 : maybe need block 1 : have task after PoolOne 2 : no task after PollOne 72 | int ActorBase::PollOne(void) 73 | { 74 | int ret = 2; 75 | TaskPullBase* ackList = NULL; 76 | int ackNum = 0; 77 | int pullnum = 0; 78 | 79 | TaskAsync* taskAsync = NULL; 80 | m_spinlock.Lock(); 81 | if (m_head != m_tail) { 82 | TaskBase* message = m_queue[m_head]; 83 | if (message->m_taskType == task_async) { 84 | taskAsync = static_cast(message); 85 | ackList = taskAsync->m_ackHead; 86 | ackNum = taskAsync->m_ackNum; 87 | pullnum = taskAsync->m_pullNum; 88 | taskAsync->ResetAckData(); 89 | m_spinlock.UnLock(); 90 | 91 | if (pullnum > ackNum) { 92 | taskAsync->AsyncCall(m_logic, ackList, ackNum, true); 93 | } 94 | else if (ackList) { 95 | taskAsync->AsyncCall(m_logic, ackList, ackNum, false); 96 | } 97 | else { 98 | taskAsync->AsyncCall(m_logic, NULL, 0, false); 99 | } 100 | while (ackList) { 101 | TaskPullBase* p = ackList; 102 | ackList = ackList->m_ackNext; 103 | delete p; 104 | } 105 | } 106 | else { 107 | m_spinlock.UnLock(); 108 | message->Call(m_logic); 109 | } 110 | 111 | 112 | m_spinlock.Lock(); 113 | if (taskAsync) { 114 | if (taskAsync->m_blockMS <= 0) { 115 | delete message; 116 | m_queue[m_head] = NULL; 117 | if (++m_head >= m_cap) 118 | m_head = 0; 119 | 120 | if (m_head != m_tail) { 121 | ret = 1; 122 | } 123 | } 124 | else { 125 | if (taskAsync->m_ackNum < taskAsync->m_pullNum) 126 | ret = 0; 127 | else 128 | ret = 1; 129 | } 130 | } 131 | else { 132 | delete message; 133 | m_queue[m_head] = NULL; 134 | if (++m_head >= m_cap) 135 | m_head = 0; 136 | 137 | if (m_head != m_tail) { 138 | ret = 1; 139 | } 140 | } 141 | } 142 | m_spinlock.UnLock(); 143 | return ret; 144 | } 145 | 146 | bool ActorBase::CheckNeedDel(void) 147 | { 148 | m_spinlock.Lock(); 149 | if (m_head == m_tail) { 150 | if (m_release) { 151 | m_spinlock.UnLock(); 152 | return true; 153 | } 154 | else { 155 | m_group->RunToFree(this); 156 | } 157 | } 158 | else { 159 | m_group->PushBackToList(this); 160 | } 161 | m_spinlock.UnLock(); 162 | return false; 163 | } 164 | 165 | void ActorBase::ProcessAckTask(void) 166 | { 167 | TaskAsync* message = static_cast(m_queue[m_head]); 168 | m_spinlock.Lock(); 169 | if (message->m_ackNum >= message->m_pullNum) { 170 | m_group->PushBackToList(this); 171 | } 172 | else { 173 | m_group->RunToBlock(this, message->m_blockMS); 174 | } 175 | m_spinlock.UnLock(); 176 | } 177 | 178 | void ActorBase::Release(void) 179 | { 180 | m_spinlock.Lock(); 181 | m_release = true; 182 | m_group->FreeToRun(this); 183 | m_spinlock.UnLock(); 184 | } 185 | -------------------------------------------------------------------------------- /ActorBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "TaskBase.h" 7 | #include "TaskPull.h" 8 | #include "SpinLock.h" 9 | #include "ActorLogicBase.h" 10 | #include 11 | 12 | 13 | 14 | enum actor_state 15 | { 16 | actor_free, 17 | actor_run, 18 | actor_block 19 | }; 20 | 21 | class ActorGroup; 22 | class ActorBase 23 | { 24 | public: 25 | friend class ActorGroup; 26 | ActorBase(const ActorBase&) = delete; 27 | ActorBase& operator=(const ActorBase&) = delete; 28 | 29 | private: 30 | ActorBase(ActorGroup* group, ActorLogicBase* logic) 31 | : m_group(group) 32 | , m_logic(logic) 33 | , m_queue(NULL) 34 | , m_release(false) 35 | , m_head(0) 36 | , m_tail(0) 37 | , m_cap(DEFAULT_QUEUE_SIZE) 38 | , m_next(NULL) 39 | , m_actorState(actor_free) 40 | , m_blockPos(-1) 41 | , m_srcAckSeq(0) 42 | { 43 | m_id = logic->GetId(); 44 | m_queue = (TaskBase**)malloc(sizeof(TaskBase*)* m_cap); // new ActorMessageBase[DEFAULT_QUEUE_SIZE]; 45 | memset(m_queue, 0, sizeof(TaskBase*)* m_cap); 46 | logic->SetGroup(group); 47 | } 48 | 49 | void _PushReq(TaskBase* msg); 50 | bool PushReq(TaskBase* msg); 51 | 52 | template 53 | bool PushReqList(T*... task) 54 | { 55 | m_spinlock.Lock(); 56 | std::initializer_list({ (_PushReq(task), 0)... }); 57 | m_spinlock.UnLock(); 58 | return true; 59 | } 60 | bool PushReqArray(TaskBase** task, int count) 61 | { 62 | m_spinlock.Lock(); 63 | for (int i = 0; i < count; ++i) { 64 | _PushReq(task[i]); 65 | } 66 | m_spinlock.UnLock(); 67 | return true; 68 | } 69 | 70 | bool PushReqAndSetRelease(TaskBase* msg, bool release); 71 | bool PushReqArrayAndSetRelease(TaskBase** msg, int count, bool release); 72 | bool PushAck(TaskPullBase* msg); 73 | int PollOne(void); //0 : need block 1 : have task after PoolOne 2 : no task after PollOne 74 | bool CheckNeedDel(void); 75 | void ProcessAckTask(void); 76 | void Release(void); 77 | inline void UnRelease(void) 78 | { 79 | m_spinlock.Lock(); 80 | m_release = false; 81 | m_spinlock.UnLock(); 82 | } 83 | 84 | virtual ~ActorBase(void) 85 | { 86 | // assert(m_blockPos == -1); 87 | m_group = NULL; 88 | if (m_queue) 89 | { 90 | if (m_head < m_tail) 91 | { 92 | for (int i = m_head; i < m_tail; i++) 93 | { 94 | TaskBase* p = m_queue[i]; 95 | m_queue[i] = NULL; 96 | delete p; 97 | } 98 | } 99 | else if (m_head > m_tail) 100 | { 101 | for (int i = m_head; i < m_cap; i++) 102 | { 103 | TaskBase* p = m_queue[i]; 104 | m_queue[i] = NULL; 105 | delete p; 106 | } 107 | for (int i = 0; i < m_tail; i++) 108 | { 109 | TaskBase* p = m_queue[i]; 110 | m_queue[i] = NULL; 111 | delete p; 112 | } 113 | } 114 | 115 | free(m_queue); 116 | m_queue = NULL; 117 | } 118 | 119 | if (m_logic) { 120 | m_logic->DelRef(); 121 | m_logic = NULL; 122 | } 123 | 124 | m_next = NULL; 125 | m_group = NULL; 126 | } 127 | 128 | inline void ExpandQueue(void) 129 | { 130 | TaskBase** new_queue = (TaskBase**)malloc(sizeof(TaskBase*)* m_cap * 2); 131 | memset(new_queue, 0, sizeof(TaskBase*)* m_cap * 2); 132 | for (int i = 0; i m_actorState; 158 | int64_t m_srcAckSeq; 159 | }; 160 | -------------------------------------------------------------------------------- /ActorContext.cpp: -------------------------------------------------------------------------------- 1 | #include "ActorContext.h" 2 | #include 3 | 4 | void ActorContext::Run() 5 | { 6 | ActorGroup* pGroup; 7 | if (m_groupNum <= 0){ 8 | assert(0); 9 | return; 10 | } 11 | for (uint32_t i = 0; i < m_groupNum; i++){ 12 | m_group[i]->InitTick(); 13 | } 14 | while (!m_gracefullExit) { 15 | uint32_t pos = m_currentIndex.fetch_add(1, std::memory_order_relaxed) % m_groupNum; 16 | pGroup = m_group[pos]; 17 | if (pGroup->RunOne()) { 18 | m_busyFlag |= (((uint64_t)1) << pos); 19 | } 20 | else { 21 | m_busyFlag &= ~(((uint64_t)1) << pos); 22 | } 23 | if ((m_busyFlag & m_busyFlagMask) == 0) { 24 | m_busyFlag = m_busyFlagMask; 25 | std::unique_lock lock(m_mutex); 26 | if (m_gracefullExit) 27 | return; 28 | 29 | m_waitingThread++; 30 | m_condition.wait(lock); 31 | m_waitingThread--; 32 | } 33 | } 34 | } 35 | 36 | bool ActorContext::ReqAndEnsureActorExist(ActorLogicBase* newLogicObj, int64_t dstGroup, TaskBase* task, 37 | e_release_flag_proc oldActorReleaseFlagProc, e_release_flag_proc newActorReleaseFlagProc) { 38 | if (dstGroup <= 0 || dstGroup > m_groupNum) { 39 | fprintf(stderr, "ReqAndEnsureActorExist dstGroup <= 0 || dstGroup > m_groupNum!!!!!!!\n dstGroup = %lld", dstGroup); 40 | assert(0); 41 | newLogicObj->DelRef(); 42 | delete task; 43 | return false; 44 | } 45 | 46 | ActorGroup* group = m_group[dstGroup - 1]; 47 | return group->ReqAndEnsureActorExist(newLogicObj, task, oldActorReleaseFlagProc, newActorReleaseFlagProc); 48 | } 49 | 50 | bool ActorContext::ReqArrayAndEnsureActorExist(ActorLogicBase* newLogicObj, int64_t dstGroup, TaskBase** task, int count, 51 | e_release_flag_proc oldActorReleaseFlagProc, e_release_flag_proc newActorReleaseFlagProc) { 52 | if (dstGroup <= 0 || dstGroup > m_groupNum) { 53 | fprintf(stderr, "ReqAndEnsureActorExist dstGroup <= 0 || dstGroup > m_groupNum!!!!!!!\n dstGroup = %lld", dstGroup); 54 | assert(0); 55 | newLogicObj->DelRef(); 56 | delete task; 57 | return false; 58 | } 59 | 60 | ActorGroup* group = m_group[dstGroup - 1]; 61 | return group->ReqArrayAndEnsureActorExist(newLogicObj, task, count, oldActorReleaseFlagProc, newActorReleaseFlagProc); 62 | } -------------------------------------------------------------------------------- /ActorContext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ActorGroup.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #define MAX_GROUP_NUM 64 8 | class ActorContext 9 | { 10 | public: 11 | ActorContext(int threadnum) 12 | : m_currentIndex(0) 13 | , m_gracefullExit(0) 14 | , m_groupNum(0) 15 | , m_busyFlagMask(0) 16 | , m_busyFlag(0) 17 | , m_waitingThread(0) 18 | , m_workThreadNum(threadnum) 19 | { 20 | memset(m_group, 0, sizeof(m_group)); 21 | } 22 | 23 | ~ActorContext(void) 24 | { 25 | for (uint32_t i = 0; i < m_groupNum; i++) 26 | { 27 | delete m_group[i]; 28 | m_group[i] = NULL; 29 | } 30 | m_workThreads.clear(); 31 | } 32 | 33 | void Start(void) 34 | { 35 | for (int i = 0; i < m_workThreadNum; i++) 36 | { 37 | std::shared_ptr pt(new std::thread(&ActorContext::Run, this)); 38 | m_workThreads.push_back(pt); 39 | } 40 | } 41 | 42 | void WaitExit(void) 43 | { 44 | auto size = m_workThreads.size(); 45 | for (size_t i = 0; i < size; i++) 46 | { 47 | std::shared_ptr pthread = m_workThreads[i]; 48 | pthread->join(); 49 | } 50 | } 51 | 52 | void AddGroup(int64_t groupId) // groupId master continuous and from 1 53 | { 54 | if (groupId <= 0 && groupId > MAX_GROUP_NUM) 55 | { 56 | assert(0); 57 | return; 58 | } 59 | if (m_group[groupId - 1] != 0) 60 | { 61 | assert(0); 62 | return; 63 | } 64 | m_group[groupId - 1] = new ActorGroup(groupId); 65 | m_groupNum++; 66 | m_busyFlagMask = ((((int64_t)1) << (groupId - 1)) | m_busyFlagMask); 67 | m_busyFlag = m_busyFlagMask; 68 | } 69 | 70 | void GracefullExit(void) 71 | { 72 | std::lock_guard lock(m_mutex); 73 | m_gracefullExit = 1; 74 | m_condition.notify_all(); 75 | } 76 | 77 | void WakeWork(void) 78 | { 79 | if (m_waitingThread > 0) 80 | { 81 | m_condition.notify_one(); 82 | } 83 | } 84 | 85 | bool AddActor(ActorLogicBase* logic) 86 | { 87 | int64_t groupId = logic->GetGroupId(); 88 | if (groupId <= 0 || groupId > m_groupNum) 89 | { 90 | assert(0); 91 | return false; 92 | } 93 | return m_group[groupId - 1]->Add(logic); 94 | } 95 | 96 | // true : actor created with param logic false : new actor deleted, anyway will send the task 97 | bool ReqAndEnsureActorExist(ActorLogicBase* newLogicObj, int64_t dstGroup, TaskBase* task, 98 | e_release_flag_proc oldActorReleaseFlagProc, e_release_flag_proc newActorReleaseFlagProc); 99 | 100 | bool ReqArrayAndEnsureActorExist(ActorLogicBase* newLogicObj, int64_t dstGroup, TaskBase**task, int count, 101 | e_release_flag_proc oldActorReleaseFlagProc, e_release_flag_proc newActorReleaseFlagProc); 102 | 103 | void SoftErase(int64_t groupId, int64_t _id) 104 | { 105 | if (groupId <= 0 || groupId > m_groupNum) 106 | { 107 | assert(0); 108 | return; 109 | } 110 | return m_group[groupId - 1]->SoftErase(_id); 111 | } 112 | 113 | void ReqAndSoftErase(int64_t groupId, int64_t _id, TaskBase* task) 114 | { 115 | if (groupId <= 0 || groupId > m_groupNum) 116 | { 117 | assert(0); 118 | return; 119 | } 120 | return m_group[groupId - 1]->ReqAndSoftErase(_id, task); 121 | } 122 | 123 | bool Req(int64_t groupId, int64_t dst, TaskBase* task, bool newSession) 124 | { 125 | if (groupId <= 0 || groupId > m_groupNum) 126 | { 127 | assert(0); 128 | delete task; 129 | return false; 130 | } 131 | bool ret = m_group[groupId - 1]->Req(dst, task, newSession); 132 | if (!ret) 133 | delete task; 134 | 135 | return ret; 136 | } 137 | 138 | bool TryReq(int64_t groupId, int64_t dst, TaskBase* task, bool newSession) 139 | { 140 | if (groupId <= 0 || groupId > m_groupNum) 141 | { 142 | assert(0); 143 | delete task; 144 | return false; 145 | } 146 | bool ret = m_group[groupId - 1]->Req(dst, task, newSession); 147 | 148 | return ret; 149 | } 150 | 151 | template 152 | bool ReqMoreOne(int64_t groupId, int64_t dst, bool newSession, T*... task) 153 | { 154 | if (groupId <= 0 || groupId > m_groupNum) { 155 | assert(0); 156 | return false; 157 | } 158 | 159 | bool ret = m_group[groupId - 1]->ReqMoreOne(dst, newSession, task...); 160 | if (!ret) 161 | std::initializer_list{(delete task, 0)...}; 162 | 163 | return ret; 164 | } 165 | bool ReqArray(int64_t groupId, int64_t dst, bool newSession, TaskBase** task, int count) { 166 | if (groupId <= 0 || groupId > m_groupNum) { 167 | assert(0); 168 | return false; 169 | } 170 | bool ret = m_group[groupId - 1]->ReqArray(dst, newSession, task, count); 171 | if (!ret) { 172 | for (int i = 0; i < count; ++i) { 173 | delete task[i]; 174 | } 175 | } 176 | return ret; 177 | } 178 | bool Ack(int64_t groupId, TaskPullBase* pull) 179 | { 180 | if (groupId <= 0 || groupId > m_groupNum) 181 | { 182 | assert(0); 183 | return false; 184 | } 185 | return m_group[groupId - 1]->Ack(pull); 186 | } 187 | private: 188 | void Run(void); 189 | int m_gracefullExit; 190 | std::atomic m_currentIndex; 191 | int64_t m_groupNum; 192 | uint64_t m_busyFlagMask; 193 | uint64_t m_busyFlag; 194 | 195 | std::mutex m_mutex; 196 | std::condition_variable m_condition; 197 | int m_waitingThread; 198 | std::vector > m_workThreads; 199 | int m_workThreadNum; 200 | ActorGroup* m_group[MAX_GROUP_NUM]; 201 | }; 202 | 203 | extern ActorContext* g_context; -------------------------------------------------------------------------------- /ActorGroup.cpp: -------------------------------------------------------------------------------- 1 | #include "ActorGroup.h" 2 | #include 3 | #include 4 | 5 | std::atomic g_sessionId(1); 6 | ActorGroup::~ActorGroup() 7 | { 8 | for (auto pr : m_mpActors) 9 | { 10 | delete pr.second; 11 | pr.second = NULL; 12 | } 13 | 14 | m_mpActors.clear(); 15 | for (int i = 0; i < TASK_MAX_WAIT_MS; ++i) 16 | { 17 | if (m_blockActorHead[i]) 18 | { 19 | delete m_blockActorHead[i]; 20 | m_blockActorHead[i] = NULL; 21 | } 22 | } 23 | } 24 | 25 | bool ActorGroup::Add(ActorLogicBase* Logic) 26 | { 27 | int64_t _id = Logic->GetId(); 28 | ActorBase* actor = new ActorBase(this, Logic); 29 | m_mapSpinLock.Lock(); 30 | auto it = m_mpActors.find(_id); 31 | if (it != m_mpActors.end()) 32 | { 33 | m_mapSpinLock.UnLock(); 34 | delete actor; 35 | assert(0); 36 | return false; 37 | } 38 | else 39 | { 40 | m_mpActors[_id] = actor; 41 | m_mapSpinLock.UnLock(); 42 | return true; 43 | } 44 | } 45 | 46 | void ActorGroup::SoftErase(int64_t _id) 47 | { 48 | m_mapSpinLock.Lock(); 49 | auto it = m_mpActors.find(_id); 50 | if (it != m_mpActors.end()) 51 | { 52 | auto actor = it->second; 53 | actor->Release(); 54 | } 55 | m_mapSpinLock.UnLock(); 56 | } 57 | 58 | void ActorGroup::ReqAndSoftErase(int64_t id, TaskBase* task) 59 | { 60 | bool reqAdded = false; 61 | m_mapSpinLock.Lock(); 62 | auto it = m_mpActors.find(id); 63 | if (it != m_mpActors.end()) 64 | { 65 | auto actor = it->second; 66 | reqAdded = actor->PushReqAndSetRelease(task, true); 67 | } 68 | m_mapSpinLock.UnLock(); 69 | if (!reqAdded) { 70 | delete task; 71 | } 72 | } 73 | 74 | bool ActorGroup::Req(int64_t dst, TaskBase* task, bool newSession) 75 | { 76 | if (newSession) 77 | task->m_session = g_sessionId.fetch_add(1, std::memory_order_relaxed); 78 | 79 | m_mapSpinLock.Lock(); 80 | auto it = m_mpActors.find(dst); 81 | if (it != m_mpActors.end()) 82 | { 83 | auto actor = it->second; 84 | bool ret = actor->PushReq(task); 85 | m_mapSpinLock.UnLock(); 86 | return ret; 87 | } 88 | else 89 | { 90 | m_mapSpinLock.UnLock(); 91 | return false; 92 | } 93 | } 94 | 95 | bool ActorGroup::ReqAll(TaskClone* task, bool newSession) 96 | { 97 | if (newSession) 98 | task->m_session = g_sessionId.fetch_add(1, std::memory_order_relaxed); 99 | 100 | m_mapSpinLock.Lock(); 101 | auto it = m_mpActors.begin(); 102 | for (; it != m_mpActors.end(); it++) { 103 | auto actor = it->second; 104 | TaskClone* clone = task->Clone(); 105 | actor->PushReq(clone); 106 | } 107 | m_mapSpinLock.UnLock(); 108 | return true; 109 | } 110 | 111 | bool ActorGroup::ReqAll(TaskClone* task, bool newSession, bool(*check_fun)(ActorBase* actor)) 112 | { 113 | if (newSession) 114 | task->m_session = g_sessionId.fetch_add(1, std::memory_order_relaxed); 115 | 116 | m_mapSpinLock.Lock(); 117 | auto it = m_mpActors.begin(); 118 | for (; it != m_mpActors.end(); it++) { 119 | auto actor = it->second; 120 | if (check_fun(actor)) { 121 | TaskClone* clone = task->Clone(); 122 | actor->PushReq(clone); 123 | } 124 | } 125 | m_mapSpinLock.UnLock(); 126 | return true; 127 | } 128 | 129 | bool ActorGroup::Ack(TaskPullBase* pull) 130 | { 131 | int64_t dst = (pull->m_src & ((((int64_t)1) << ACTOR_ID_BIT_NUM) - 1)); 132 | m_mapSpinLock.Lock(); 133 | auto it = m_mpActors.find(dst); 134 | if (it != m_mpActors.end()) 135 | { 136 | auto actor = it->second; 137 | bool ret = actor->PushAck(pull); 138 | m_mapSpinLock.UnLock(); 139 | return ret; 140 | } 141 | else 142 | { 143 | m_mapSpinLock.UnLock(); 144 | return false; 145 | } 146 | } 147 | 148 | void ActorGroup::InitTick(void) 149 | { 150 | m_updateSpinLock.Lock(); 151 | if (0 == m_lastUpdateTick) 152 | m_lastUpdateTick = g_pGlobalTimer->GetTickMS(); 153 | m_updateSpinLock.UnLock(); 154 | } 155 | 156 | bool ActorGroup::ReqAndEnsureActorExist(ActorLogicBase* logic, TaskBase* task, 157 | e_release_flag_proc oldActorReleaseFlagProc, e_release_flag_proc newActorReleaseFlagProc) 158 | { 159 | int64_t _id = logic->GetId(); 160 | ActorBase* actor = new ActorBase(this, logic); 161 | if (newActorReleaseFlagProc != erfp_no_change) { 162 | actor->m_release = (newActorReleaseFlagProc == erfp_set_true ? true : false); 163 | } 164 | m_mapSpinLock.Lock(); 165 | auto it = m_mpActors.find(_id); 166 | if (it != m_mpActors.end()) { 167 | if (oldActorReleaseFlagProc != erfp_no_change) 168 | it->second->PushReqAndSetRelease(task, oldActorReleaseFlagProc == erfp_set_true ? true : false); 169 | else 170 | it->second->PushReq(task); 171 | 172 | m_mapSpinLock.UnLock(); 173 | delete actor; 174 | return false; 175 | } 176 | else { 177 | m_mpActors[_id] = actor; 178 | actor->PushReq(task); 179 | m_mapSpinLock.UnLock(); 180 | return true; 181 | } 182 | } 183 | 184 | bool ActorGroup::ReqArrayAndEnsureActorExist(ActorLogicBase* logic, TaskBase** task, int count, 185 | e_release_flag_proc oldActorReleaseFlagProc, e_release_flag_proc newActorReleaseFlagProc) 186 | { 187 | int64_t _id = logic->GetId(); 188 | ActorBase* actor = new ActorBase(this, logic); 189 | if (newActorReleaseFlagProc != erfp_no_change) { 190 | actor->m_release = (newActorReleaseFlagProc == erfp_set_true ? true : false); 191 | } 192 | m_mapSpinLock.Lock(); 193 | auto it = m_mpActors.find(_id); 194 | if (it != m_mpActors.end()) { 195 | if (oldActorReleaseFlagProc != erfp_no_change) 196 | it->second->PushReqArrayAndSetRelease(task, count, oldActorReleaseFlagProc == erfp_set_true ? true : false); 197 | else 198 | it->second->PushReqArray(task, count); 199 | 200 | m_mapSpinLock.UnLock(); 201 | delete actor; 202 | return false; 203 | } 204 | else { 205 | m_mpActors[_id] = actor; 206 | actor->PushReqArray(task, count); 207 | m_mapSpinLock.UnLock(); 208 | return true; 209 | } 210 | } 211 | 212 | bool ActorGroup::RunOne() 213 | { 214 | UpdateBlockActor(g_pGlobalTimer->GetTickMS()); 215 | ActorBase* actor = PopFromList(); 216 | if (NULL == actor) 217 | return false; 218 | 219 | //0 : maybe need block 1 : have task after PoolOne 2 : no task after PollOne 220 | int ret = actor->PollOne(); 221 | if (0 == ret) { 222 | actor->ProcessAckTask(); 223 | } 224 | else if (1 == ret) { 225 | PushBackToList(actor); 226 | } 227 | else { 228 | bool bDel = false; 229 | m_mapSpinLock.Lock(); 230 | int64_t _id = actor->m_id; 231 | if (actor->CheckNeedDel()) { 232 | auto it = m_mpActors.find(_id); 233 | if (it != m_mpActors.end()) { 234 | m_mpActors.erase(it); 235 | bDel = true; 236 | } 237 | } 238 | m_mapSpinLock.UnLock(); 239 | if (bDel) { 240 | delete actor; 241 | } 242 | } 243 | return true; 244 | } -------------------------------------------------------------------------------- /ActorGroup.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xinqiangren/TinyActor/174933a7863d765a64f543bfb8caaae76ea2f14d/ActorGroup.h -------------------------------------------------------------------------------- /ActorLogicBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "TaskBase.h" 4 | #include "TaskPull.h" 5 | #include 6 | 7 | class ActorGroup; 8 | class ActorLogicBase 9 | { 10 | public: 11 | ActorLogicBase(int64_t groupId, int64_t _id) 12 | : m_groupId(groupId) 13 | , m_id(_id) 14 | , m_group(NULL) 15 | { 16 | m_refCount.store(1, std::memory_order_relaxed); 17 | } 18 | 19 | ActorLogicBase(const ActorLogicBase& ) = delete; 20 | ActorLogicBase& operator=(const ActorLogicBase& ) = delete; 21 | 22 | inline bool DelRef(void) { 23 | int priRef = m_refCount.fetch_sub(1, std::memory_order_relaxed); 24 | if (priRef == 1) { 25 | delete this; 26 | return true; 27 | } 28 | return false; 29 | } 30 | 31 | inline void IncRef(void) { 32 | m_refCount.fetch_add(1, std::memory_order_relaxed); 33 | } 34 | 35 | inline int64_t GetId(void) { return m_id; } 36 | inline int64_t GetGroupId(void) { return m_groupId; } 37 | 38 | friend class ActorBase; 39 | inline void SetGroup(ActorGroup* context) { m_group = context; } 40 | 41 | std::atomic m_refCount; 42 | int64_t m_groupId; 43 | int64_t m_id; 44 | ActorGroup* m_group; 45 | 46 | protected: 47 | virtual ~ActorLogicBase(void) {} 48 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 本项目是基于c++11实现的简单实用的多线的跨平台多线程actor框架,支持push和pull两种基本接口,目前不支持分布式actor之间的交互 2 | push: 发送回调任务给其他actor,不等待回复 3 | pull: 发送回调任务给其他actor,等待回复(通过协程异步等待),回复后仍然回到回调函数继续执行 4 | 5 | 基本类简介: 6 | ActorContext: 提供actor多线程运行环境,提供对外接口 7 | 8 | ActorGroup: 对actor进行分组,同一类型actor放在同一group.工作线程对group进行轮叫调度,保证各group之间cpu占用的公平性, 9 | 避免某一类型actor过多而饿死其他类型的actor,ActorGroup内的actor通过队列链接在一起 10 | 11 | ActorBase: actor框架核心类,actor内有一个task队列,轮到该actor执行逻辑时,工作线程从队列中取出任务调用任务的回调函数 12 | 13 | ActorLogicBase: 业务逻辑接口,客户端代码通过扩展该接口实现业务逻辑。ActorBase保存该类对象指针,任务回调调用时该指针会作为输入参数 14 | 15 | TaskBase: 任务类的基本类,提供call函数接口供工作线程调用 16 | 17 | TaskAsync: 继承自ActorBase,提供AyncCall函数接口替代call函数,在该函数内可以使用上述pull接口 18 | 19 | TaskPullBase: pull接口被调用时作为信使在同actor之间承载和拉取数据(其有两个子类:TaskPullPod和TaskPullObj) 20 | 21 | TimerExcuteQue: 当定时器用。框架内部也使用它来唤醒空闲线程,处理任务超时 22 | 23 | 实例代码: 24 | 见sample文件夹 25 | 26 | 编译与安装: 27 | 过于简单,略 28 | 29 | 本框架的核心数据结构(两级队列)参考了云风skynet开源框架 30 | 本框架使用蝇级stackless协程库 protothreads, 作者 Adam Dunkels 31 | 32 | 联系:email xinqiangren11@126.com 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /SpinLock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class SpinLock 6 | { 7 | public: 8 | SpinLock(void) 9 | { 10 | m_flag.clear(); 11 | } 12 | 13 | void Lock() 14 | { 15 | int poolnum = 0; 16 | while (m_flag.test_and_set(std::memory_order_acquire)) 17 | { 18 | if ((++poolnum & 8191) == 0) 19 | { 20 | std::this_thread::yield(); 21 | } 22 | } 23 | } 24 | 25 | bool TryLock() 26 | { 27 | return !m_flag.test_and_set(std::memory_order_acquire); 28 | } 29 | 30 | void UnLock() 31 | { 32 | m_flag.clear(std::memory_order_release); 33 | } 34 | 35 | private: 36 | std::atomic_flag m_flag; 37 | }; -------------------------------------------------------------------------------- /TaskAsync.cpp: -------------------------------------------------------------------------------- 1 | #include "TaskAsync.h" 2 | #include "ActorGroup.h" 3 | #include "ActorContext.h" 4 | 5 | extern ActorContext* g_context; 6 | bool TaskAsync::_ReqContext(int64_t group, int64_t dst, TaskBase* task, bool newSession) 7 | { 8 | return g_context->Req(group, dst, task, false); 9 | } 10 | -------------------------------------------------------------------------------- /TaskAsync.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef _WIN32 3 | #ifndef LC_INCLUDE 4 | #define LC_INCLUDE "lc-addrlabels.h" 5 | #endif 6 | #endif 7 | 8 | #include "TaskBase.h" 9 | #include "TaskPull.h" 10 | #include "pt.h" 11 | #include "ActorLogicBase.h" 12 | 13 | struct TaskAsync : public TaskBase 14 | { 15 | TaskAsync(void) 16 | : TaskBase(task_async) 17 | , m_ackNum(0) 18 | , m_pullNum(0) 19 | , m_blockMS(0) 20 | , m_srcAckSeq(0) 21 | , m_ackHead(NULL) 22 | { 23 | PT_INIT(&m_pt); 24 | } 25 | 26 | void Call(ActorLogicBase* logic) final {} 27 | template 28 | bool Pull(ActorLogicBase* logic, int64_t dstGroup, int64_t dst, int waitTime, task_params_type... args) 29 | { 30 | #ifdef __DISABLE_PULL_IN_THE_SAME_GROUP__ 31 | if (logic->GetGroupId() == dstGroup) 32 | { 33 | fprintf(stderr, "!!!!! Pull error logic groupid[%lld] dstGroup[%lld] \n", logic->GetGroupId(), dstGroup); 34 | assert(0); 35 | return false; 36 | } 37 | #endif 38 | task_type* task = task_type::CreateWithPull(args...); 39 | TaskPullBase* pull = task->m_pull; 40 | assert(pull); 41 | pull->m_src = task->m_src = (logic->GetId() | (logic->GetGroupId() << ACTOR_ID_BIT_NUM)); 42 | pull->m_session = task->m_session = m_session; 43 | pull->m_dstAckSeq = m_srcAckSeq; 44 | if (_ReqContext(dstGroup, dst, task, false)) { 45 | if (waitTime == 0) 46 | waitTime = DEFAULT_WAIT_MS; 47 | else if (waitTime < 0 || waitTime > TASK_MAX_WAIT_MS) 48 | waitTime = TASK_MAX_WAIT_MS; 49 | 50 | if (m_blockMS < waitTime) 51 | m_blockMS = waitTime; 52 | 53 | m_pullNum++; 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | bool _ReqContext(int64_t group, int64_t dst, TaskBase* task, bool newSession); 60 | virtual int AsyncCall(ActorLogicBase* logic, TaskPullBase* ack, int num, bool timeout) = 0; 61 | 62 | virtual ~TaskAsync(void) 63 | { 64 | while (m_ackHead) 65 | { 66 | TaskBase* p = m_ackHead; 67 | m_ackHead = m_ackHead->m_ackNext; 68 | delete p; 69 | } 70 | } 71 | 72 | 73 | inline void ResetAckData(void) 74 | { 75 | m_ackHead = NULL; 76 | m_ackNum = 0; 77 | m_blockMS = 0; 78 | m_pullNum = 0; 79 | m_srcAckSeq++; 80 | } 81 | 82 | inline bool AppendAck(TaskPullBase* task) 83 | { 84 | task->m_ackNext = m_ackHead; 85 | m_ackHead = task; 86 | if (++m_ackNum >= m_pullNum) 87 | { 88 | return true; 89 | } 90 | return false; 91 | } 92 | 93 | int m_ackNum; 94 | int m_pullNum; 95 | int m_blockMS; 96 | int64_t m_srcAckSeq; 97 | TaskPullBase* m_ackHead; 98 | struct pt m_pt; 99 | }; -------------------------------------------------------------------------------- /TaskBase.cpp: -------------------------------------------------------------------------------- 1 | #include "TaskBase.h" 2 | #include "ActorGroup.h" 3 | #include "ActorContext.h" 4 | 5 | extern ActorContext* g_context; 6 | extern std::atomic g_sessionId; 7 | 8 | bool TaskBase::PushInGroup(ActorLogicBase* logic, int64_t dst, TaskBase* task) 9 | { 10 | assert(logic->GetId() != dst); 11 | int64_t session = g_sessionId.fetch_add(1); 12 | task->m_src = (logic->GetId() | (logic->GetGroupId() << ACTOR_ID_BIT_NUM)); //logic->GetId(); 13 | task->m_session = session; 14 | return logic->m_group->Req(dst, task, false); 15 | } 16 | 17 | bool TaskBase::PushInContext(ActorLogicBase* logic, int64_t dstGroup, int64_t dst, TaskBase* task) 18 | { 19 | if (logic->GetGroupId() == dstGroup) 20 | { 21 | assert(logic->GetId() != dst); 22 | } 23 | 24 | int64_t session = g_sessionId.fetch_add(1); 25 | task->m_src = (logic->GetId() | (logic->GetGroupId() << ACTOR_ID_BIT_NUM)); //logic->GetId(); 26 | task->m_session = session; 27 | return g_context->Req(dstGroup, dst, task, false); 28 | } 29 | 30 | bool TaskBase::AckPull(ActorLogicBase* logic) 31 | { 32 | assert(m_pull); 33 | 34 | int64_t dstGroup = (m_pull->m_src >> ACTOR_ID_BIT_NUM); 35 | bool ackSucess = true; 36 | if (logic->m_groupId == dstGroup) { 37 | ackSucess = logic->m_group->Ack(m_pull); 38 | } 39 | else { 40 | ackSucess = g_context->Ack(dstGroup, m_pull); 41 | } 42 | if (ackSucess) { 43 | m_pull = NULL; 44 | } 45 | return ackSucess; 46 | } 47 | 48 | TaskBase::~TaskBase(void) 49 | { 50 | if (m_pull) 51 | { 52 | delete m_pull; 53 | m_pull = NULL; 54 | } 55 | } -------------------------------------------------------------------------------- /TaskBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "TimerExcuteQue.h" 8 | #include 9 | 10 | extern TimerExcuteQue* g_pGlobalTimer; 11 | 12 | class ActorLogicBase; 13 | struct TaskBase; 14 | struct TaskPullBase; 15 | 16 | enum TaskType 17 | { 18 | task_base = 1, 19 | task_async, 20 | task_pull, 21 | }; 22 | 23 | #define DEFAULT_WAIT_MS 5000 24 | #define TASK_MAX_WAIT_MS (1 << 15) 25 | #define DEFAULT_QUEUE_SIZE 32 26 | #define ACTOR_ID_BIT_NUM 46 27 | 28 | struct TaskBase 29 | { 30 | TaskBase(void) 31 | : m_taskType(task_base) 32 | , m_session(0) 33 | , m_src(0) 34 | , m_pull(NULL) 35 | { 36 | } 37 | 38 | explicit TaskBase(int taskType) 39 | : m_taskType(taskType) 40 | , m_session(0) 41 | , m_src(0) 42 | , m_pull(NULL) 43 | { 44 | } 45 | 46 | virtual ~TaskBase(void); 47 | 48 | TaskBase(const TaskBase&) = delete; 49 | TaskBase& operator=(const TaskBase&) = delete; 50 | 51 | virtual void Call(ActorLogicBase* logic) = 0; 52 | bool PushInGroup(ActorLogicBase* logic, int64_t dst, TaskBase* task); 53 | bool PushInContext(ActorLogicBase* logic, int64_t dstGroup, int64_t dst, TaskBase* task); 54 | bool AckPull(ActorLogicBase* logic); 55 | 56 | int m_taskType; 57 | int64_t m_src; 58 | int64_t m_session; 59 | TaskPullBase* m_pull; 60 | }; 61 | -------------------------------------------------------------------------------- /TaskClone.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TaskBase.h" 3 | struct TaskClone : public TaskBase 4 | { 5 | virtual TaskClone* Clone(void) = 0; 6 | }; -------------------------------------------------------------------------------- /TaskPull.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TaskBase.h" 3 | #include 4 | 5 | struct TaskPullBase : public TaskBase 6 | { 7 | TaskPullBase(void) 8 | : TaskBase(task_pull) 9 | , m_dstAckSeq(0) 10 | , m_ackNext(NULL) 11 | { 12 | } 13 | void Call(ActorLogicBase* logic) {} 14 | int64_t m_dstAckSeq; 15 | TaskPullBase* m_ackNext; 16 | }; 17 | 18 | 19 | 20 | template 21 | struct TaskPullPod final : public TaskPullBase 22 | { 23 | explicit TaskPullPod(void) 24 | { 25 | static_assert(std::is_pod::value, "TaskPullPod template param must be pod type"); 26 | } 27 | 28 | void SetReturnVal(const T& t) 29 | { 30 | m_returnVal = t; 31 | } 32 | T GetReturnVal(void) 33 | { 34 | return m_returnVal; 35 | } 36 | 37 | void Call(ActorLogicBase* logic) {} 38 | T m_returnVal; 39 | }; 40 | 41 | 42 | template 43 | struct TaskPullObj final : public TaskPullBase 44 | { 45 | TaskPullObj(void) 46 | : m_returnObj(NULL) 47 | { 48 | static_assert(std::is_class::value, "TaskPullPod template param must be class type"); 49 | } 50 | ~TaskPullObj(void) 51 | { 52 | if (m_returnObj) 53 | { 54 | delete m_returnObj; 55 | m_returnObj = NULL; 56 | } 57 | } 58 | 59 | void SetReturnObjPtr(T* ptr) 60 | { 61 | m_returnObj = ptr; 62 | } 63 | 64 | void Call(ActorLogicBase* logic) {} 65 | 66 | T* FetchObjPtr(void) 67 | { 68 | T* ret = m_returnObj; 69 | m_returnObj = NULL; 70 | return ret; 71 | } 72 | 73 | protected: 74 | T* m_returnObj; 75 | }; -------------------------------------------------------------------------------- /TaskWithCallback.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TaskBase.h" 3 | #include 4 | 5 | struct TaskWithCallback final : public TaskBase 6 | { 7 | std::function m_call; 8 | 9 | TaskWithCallback(std::function&& call) 10 | : m_call(call) 11 | { 12 | } 13 | void Call(ActorLogicBase* logic) 14 | { 15 | m_call(logic); 16 | } 17 | }; -------------------------------------------------------------------------------- /TestBase.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xinqiangren/TinyActor/174933a7863d765a64f543bfb8caaae76ea2f14d/TestBase.h -------------------------------------------------------------------------------- /TimerExcuteQue.cpp: -------------------------------------------------------------------------------- 1 | #include "TimerExcuteQue.h" 2 | #include 3 | #include 4 | #include 5 | 6 | TimerExcuteQue::TimerExcuteQue(void) 7 | : m_tickUpdated(0) 8 | , m_tickStart(0) 9 | , m_tickCount(0) 10 | , m_timeStamp(0) 11 | { 12 | } 13 | 14 | void TimerExcuteQue::Init(void) 15 | { 16 | uint32_t current = 0; 17 | GetSecAndLeft10MS(m_tickStart, current); 18 | m_tickCount = current; 19 | m_timeStamp = Get10MSNum(); 20 | } 21 | 22 | void TimerExcuteQue::Update(void) 23 | { 24 | uint64_t currentStamp = Get10MSNum(); 25 | if (currentStamp < m_timeStamp) 26 | { 27 | fprintf(stderr, "current timestamp less than last time stamp last[%lld] current[%lld]\n", m_timeStamp, currentStamp); 28 | m_timeStamp = currentStamp; 29 | } 30 | else if (currentStamp != m_timeStamp) 31 | { 32 | uint32_t diff = (uint32_t)(currentStamp - m_timeStamp); 33 | m_timeStamp = currentStamp; 34 | m_tickCount += diff; 35 | for (uint32_t i = 0; inext; 73 | AddTimerNode(current); 74 | current = temp; 75 | } 76 | } 77 | else 78 | { 79 | uint32_t time = ct >> TIME_NEAR_SHIFT; 80 | int i = 0; 81 | 82 | while ((ct & (mask - 1)) == 0) 83 | { 84 | int idx = time & TIME_LEVEL_MASK; 85 | if (idx != 0) { 86 | 87 | TimerNode* current = m_levelTimer[i][idx].Clear(); 88 | while (current) 89 | { 90 | TimerNode *temp = current->next; 91 | AddTimerNode(current); 92 | current = temp; 93 | } 94 | break; 95 | } 96 | mask <<= TIME_LEVEL_SHIFT; 97 | time >>= TIME_LEVEL_SHIFT; 98 | ++i; 99 | } 100 | } 101 | } 102 | 103 | void TimerExcuteQue::DispatchTimerList(TimerNode *current) 104 | { 105 | while (current) 106 | { 107 | current->fun(); 108 | TimerNode * temp = current; 109 | current = current->next; 110 | delete temp; 111 | } 112 | } 113 | 114 | 115 | void TimerExcuteQue::GetSecAndLeft10MS(uint32_t & sec, uint32_t & left) const 116 | { 117 | int64_t milliseconds = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); 118 | sec = (uint32_t)(milliseconds / 1000); 119 | left = (uint32_t)((milliseconds % 1000) / 10); 120 | } 121 | 122 | uint64_t TimerExcuteQue::Get10MSNum(void) const 123 | { 124 | int64_t milliseconds = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); 125 | return (milliseconds / 10); 126 | } 127 | 128 | void TimerExcuteQue::AddTimer(int time, std::function f) 129 | { 130 | TimerNode *node = new TimerNode(f); 131 | m_lock.Lock(); 132 | node->SetExpire(time + m_tickUpdated); 133 | AddTimerNode(node); 134 | m_lock.UnLock(); 135 | } 136 | 137 | uint64_t TimerExcuteQue::GetTickMS(void) const 138 | { 139 | return m_tickCount * 10; 140 | } 141 | time_t TimerExcuteQue::GetCurTime(void) const 142 | { 143 | return m_tickStart + m_tickCount / 100; 144 | } 145 | 146 | void TimerExcuteQue::AddTimerNode(TimerNode *node) 147 | { 148 | uint32_t time = node->expire; 149 | if ((time | TIME_NEAR_MASK) == (m_tickUpdated | TIME_NEAR_MASK)) 150 | { 151 | m_nearTimer[time&TIME_NEAR_MASK].PushBack(node); 152 | } 153 | else 154 | { 155 | uint32_t mask = TIME_NEAR << TIME_LEVEL_SHIFT; 156 | int i = 0; 157 | for (; i<3; i++) 158 | { 159 | if ((time | (mask - 1)) == (m_tickUpdated | (mask - 1))) { 160 | break; 161 | } 162 | mask <<= TIME_LEVEL_SHIFT; 163 | } 164 | int idx = (time >> (TIME_NEAR_SHIFT + i*TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK; 165 | m_levelTimer[i][idx].PushBack(node); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /TimerExcuteQue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "SpinLock.h" 4 | #include 5 | #include 6 | #include 7 | 8 | #define TIME_NEAR_SHIFT 8 9 | #define TIME_NEAR (1 << TIME_NEAR_SHIFT) 10 | #define TIME_LEVEL_SHIFT 6 11 | #define TIME_LEVEL (1 << TIME_LEVEL_SHIFT) 12 | #define TIME_NEAR_MASK (TIME_NEAR-1) 13 | #define TIME_LEVEL_MASK (TIME_LEVEL-1) 14 | 15 | struct TimerNode { 16 | TimerNode *next; 17 | uint32_t expire; 18 | std::function fun; 19 | 20 | explicit TimerNode(const std::function& _fun) 21 | : next(0) 22 | , expire(0) 23 | , fun(_fun) 24 | { 25 | } 26 | 27 | TimerNode(void) 28 | : next(NULL) 29 | , expire(0) 30 | { 31 | } 32 | 33 | void SetExpire(uint32_t _expire) 34 | { 35 | expire = _expire; 36 | } 37 | }; 38 | 39 | struct TimerList 40 | { 41 | TimerNode head; 42 | TimerNode *tail; 43 | 44 | TimerList(void) 45 | : tail(0) 46 | { 47 | Clear(); 48 | } 49 | 50 | void PushBack(TimerNode *node) 51 | { 52 | tail->next = node; 53 | tail = node; 54 | node->next = 0; 55 | } 56 | 57 | TimerNode* Clear(void) 58 | { 59 | TimerNode* ret = head.next; 60 | head.next = 0; 61 | tail = &head; 62 | return ret; 63 | } 64 | }; 65 | 66 | class TimerExcuteQue 67 | { 68 | public: 69 | TimerExcuteQue(void); 70 | void Init(void); 71 | void Update(void); 72 | void AddTimer(int time, std::function f); 73 | uint64_t GetTickMS(void) const; 74 | time_t GetCurTime(void) const; 75 | private: 76 | void AddTimerNode(TimerNode *node); 77 | void EventDispatch(); 78 | void ExcuteTimeoutEvent(); 79 | void TimerListUpdate(); 80 | 81 | void DispatchTimerList(TimerNode *current); 82 | void GetSecAndLeft10MS(uint32_t & sec, uint32_t & left) const; 83 | uint64_t Get10MSNum(void) const; 84 | 85 | TimerList m_nearTimer[TIME_NEAR]; 86 | TimerList m_levelTimer[4][TIME_LEVEL]; 87 | SpinLock m_lock; 88 | uint32_t m_tickUpdated; 89 | uint32_t m_tickStart; 90 | 91 | uint64_t m_tickCount; 92 | uint64_t m_timeStamp; 93 | }; -------------------------------------------------------------------------------- /lc-addrlabels.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004-2005, Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Contiki operating system. 30 | * 31 | * Author: Adam Dunkels 32 | * 33 | * $Id: lc-addrlabels.h,v 1.4 2006/06/03 11:29:43 adam Exp $ 34 | */ 35 | 36 | /** 37 | * \addtogroup lc 38 | * @{ 39 | */ 40 | 41 | /** 42 | * \file 43 | * Implementation of local continuations based on the "Labels as 44 | * values" feature of gcc 45 | * \author 46 | * Adam Dunkels 47 | * 48 | * This implementation of local continuations is based on a special 49 | * feature of the GCC C compiler called "labels as values". This 50 | * feature allows assigning pointers with the address of the code 51 | * corresponding to a particular C label. 52 | * 53 | * For more information, see the GCC documentation: 54 | * http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html 55 | * 56 | */ 57 | 58 | #ifndef __LC_ADDRLABELS_H__ 59 | #define __LC_ADDRLABELS_H__ 60 | 61 | /** \hideinitializer */ 62 | typedef void * lc_t; 63 | 64 | #define LC_INIT(s) s = NULL 65 | 66 | #define LC_RESUME(s) \ 67 | do { \ 68 | if(s != NULL) { \ 69 | goto *s; \ 70 | } \ 71 | } while(0) 72 | 73 | #define LC_CONCAT2(s1, s2) s1##s2 74 | #define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2) 75 | 76 | #define LC_SET(s) \ 77 | do { \ 78 | LC_CONCAT(LC_LABEL, __LINE__): \ 79 | (s) = &&LC_CONCAT(LC_LABEL, __LINE__); \ 80 | } while(0) 81 | 82 | #define LC_END(s) 83 | 84 | #endif /* __LC_ADDRLABELS_H__ */ 85 | /** @} */ 86 | -------------------------------------------------------------------------------- /lc-switch.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004-2005, Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Contiki operating system. 30 | * 31 | * Author: Adam Dunkels 32 | * 33 | * $Id: lc-switch.h,v 1.4 2006/06/03 11:29:43 adam Exp $ 34 | */ 35 | 36 | /** 37 | * \addtogroup lc 38 | * @{ 39 | */ 40 | 41 | /** 42 | * \file 43 | * Implementation of local continuations based on switch() statment 44 | * \author Adam Dunkels 45 | * 46 | * This implementation of local continuations uses the C switch() 47 | * statement to resume execution of a function somewhere inside the 48 | * function's body. The implementation is based on the fact that 49 | * switch() statements are able to jump directly into the bodies of 50 | * control structures such as if() or while() statmenets. 51 | * 52 | * This implementation borrows heavily from Simon Tatham's coroutines 53 | * implementation in C: 54 | * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html 55 | */ 56 | 57 | #ifndef __LC_SWITCH_H__ 58 | #define __LC_SWITCH_H__ 59 | 60 | /* WARNING! lc implementation using switch() does not work if an 61 | LC_SET() is done within another switch() statement! */ 62 | 63 | /** \hideinitializer */ 64 | typedef unsigned short lc_t; 65 | 66 | #define LC_INIT(s) s = 0; 67 | 68 | #define LC_RESUME(s) switch(s) { case 0: 69 | 70 | #define LC_SET(s) s = __LINE__; case __LINE__: 71 | 72 | #define LC_END(s) } 73 | 74 | #endif /* __LC_SWITCH_H__ */ 75 | 76 | /** @} */ 77 | -------------------------------------------------------------------------------- /lc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004-2005, Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the protothreads library. 30 | * 31 | * Author: Adam Dunkels 32 | * 33 | * $Id: lc.h,v 1.2 2005/02/24 10:36:59 adam Exp $ 34 | */ 35 | 36 | /** 37 | * \addtogroup pt 38 | * @{ 39 | */ 40 | 41 | /** 42 | * \defgroup lc Local continuations 43 | * @{ 44 | * 45 | * Local continuations form the basis for implementing protothreads. A 46 | * local continuation can be set in a specific function to 47 | * capture the state of the function. After a local continuation has 48 | * been set can be resumed in order to restore the state of the 49 | * function at the point where the local continuation was set. 50 | * 51 | * 52 | */ 53 | 54 | /** 55 | * \file lc.h 56 | * Local continuations 57 | * \author 58 | * Adam Dunkels 59 | * 60 | */ 61 | 62 | #ifdef DOXYGEN 63 | /** 64 | * Initialize a local continuation. 65 | * 66 | * This operation initializes the local continuation, thereby 67 | * unsetting any previously set continuation state. 68 | * 69 | * \hideinitializer 70 | */ 71 | #define LC_INIT(lc) 72 | 73 | /** 74 | * Set a local continuation. 75 | * 76 | * The set operation saves the state of the function at the point 77 | * where the operation is executed. As far as the set operation is 78 | * concerned, the state of the function does not include the 79 | * call-stack or local (automatic) variables, but only the program 80 | * counter and such CPU registers that needs to be saved. 81 | * 82 | * \hideinitializer 83 | */ 84 | #define LC_SET(lc) 85 | 86 | /** 87 | * Resume a local continuation. 88 | * 89 | * The resume operation resumes a previously set local continuation, thus 90 | * restoring the state in which the function was when the local 91 | * continuation was set. If the local continuation has not been 92 | * previously set, the resume operation does nothing. 93 | * 94 | * \hideinitializer 95 | */ 96 | #define LC_RESUME(lc) 97 | 98 | /** 99 | * Mark the end of local continuation usage. 100 | * 101 | * The end operation signifies that local continuations should not be 102 | * used any more in the function. This operation is not needed for 103 | * most implementations of local continuation, but is required by a 104 | * few implementations. 105 | * 106 | * \hideinitializer 107 | */ 108 | #define LC_END(lc) 109 | 110 | /** 111 | * \var typedef lc_t; 112 | * 113 | * The local continuation type. 114 | * 115 | * \hideinitializer 116 | */ 117 | #endif /* DOXYGEN */ 118 | 119 | #ifndef __LC_H__ 120 | #define __LC_H__ 121 | 122 | 123 | #ifdef LC_INCLUDE 124 | #include LC_INCLUDE 125 | #else 126 | #include "lc-switch.h" 127 | #endif /* LC_INCLUDE */ 128 | 129 | #endif /* __LC_H__ */ 130 | 131 | /** @} */ 132 | /** @} */ 133 | -------------------------------------------------------------------------------- /pt-sem.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the protothreads library. 30 | * 31 | * Author: Adam Dunkels 32 | * 33 | * $Id: pt-sem.h,v 1.2 2005/02/24 10:36:59 adam Exp $ 34 | */ 35 | 36 | /** 37 | * \addtogroup pt 38 | * @{ 39 | */ 40 | 41 | /** 42 | * \defgroup ptsem Protothread semaphores 43 | * @{ 44 | * 45 | * This module implements counting semaphores on top of 46 | * protothreads. Semaphores are a synchronization primitive that 47 | * provide two operations: "wait" and "signal". The "wait" operation 48 | * checks the semaphore counter and blocks the thread if the counter 49 | * is zero. The "signal" operation increases the semaphore counter but 50 | * does not block. If another thread has blocked waiting for the 51 | * semaphore that is signalled, the blocked thread will become 52 | * runnable again. 53 | * 54 | * Semaphores can be used to implement other, more structured, 55 | * synchronization primitives such as monitors and message 56 | * queues/bounded buffers (see below). 57 | * 58 | * The following example shows how the producer-consumer problem, also 59 | * known as the bounded buffer problem, can be solved using 60 | * protothreads and semaphores. Notes on the program follow after the 61 | * example. 62 | * 63 | \code 64 | #include "pt-sem.h" 65 | 66 | #define NUM_ITEMS 32 67 | #define BUFSIZE 8 68 | 69 | static struct pt_sem mutex, full, empty; 70 | 71 | PT_THREAD(producer(struct pt *pt)) 72 | { 73 | static int produced; 74 | 75 | PT_BEGIN(pt); 76 | 77 | for(produced = 0; produced < NUM_ITEMS; ++produced) { 78 | 79 | PT_SEM_WAIT(pt, &full); 80 | 81 | PT_SEM_WAIT(pt, &mutex); 82 | add_to_buffer(produce_item()); 83 | PT_SEM_SIGNAL(pt, &mutex); 84 | 85 | PT_SEM_SIGNAL(pt, &empty); 86 | } 87 | 88 | PT_END(pt); 89 | } 90 | 91 | PT_THREAD(consumer(struct pt *pt)) 92 | { 93 | static int consumed; 94 | 95 | PT_BEGIN(pt); 96 | 97 | for(consumed = 0; consumed < NUM_ITEMS; ++consumed) { 98 | 99 | PT_SEM_WAIT(pt, &empty); 100 | 101 | PT_SEM_WAIT(pt, &mutex); 102 | consume_item(get_from_buffer()); 103 | PT_SEM_SIGNAL(pt, &mutex); 104 | 105 | PT_SEM_SIGNAL(pt, &full); 106 | } 107 | 108 | PT_END(pt); 109 | } 110 | 111 | PT_THREAD(driver_thread(struct pt *pt)) 112 | { 113 | static struct pt pt_producer, pt_consumer; 114 | 115 | PT_BEGIN(pt); 116 | 117 | PT_SEM_INIT(&empty, 0); 118 | PT_SEM_INIT(&full, BUFSIZE); 119 | PT_SEM_INIT(&mutex, 1); 120 | 121 | PT_INIT(&pt_producer); 122 | PT_INIT(&pt_consumer); 123 | 124 | PT_WAIT_THREAD(pt, producer(&pt_producer) & 125 | consumer(&pt_consumer)); 126 | 127 | PT_END(pt); 128 | } 129 | \endcode 130 | * 131 | * The program uses three protothreads: one protothread that 132 | * implements the consumer, one thread that implements the producer, 133 | * and one protothread that drives the two other protothreads. The 134 | * program uses three semaphores: "full", "empty" and "mutex". The 135 | * "mutex" semaphore is used to provide mutual exclusion for the 136 | * buffer, the "empty" semaphore is used to block the consumer is the 137 | * buffer is empty, and the "full" semaphore is used to block the 138 | * producer is the buffer is full. 139 | * 140 | * The "driver_thread" holds two protothread state variables, 141 | * "pt_producer" and "pt_consumer". It is important to note that both 142 | * these variables are declared as static. If the static 143 | * keyword is not used, both variables are stored on the stack. Since 144 | * protothreads do not store the stack, these variables may be 145 | * overwritten during a protothread wait operation. Similarly, both 146 | * the "consumer" and "producer" protothreads declare their local 147 | * variables as static, to avoid them being stored on the stack. 148 | * 149 | * 150 | */ 151 | 152 | /** 153 | * \file 154 | * Couting semaphores implemented on protothreads 155 | * \author 156 | * Adam Dunkels 157 | * 158 | */ 159 | 160 | #ifndef __PT_SEM_H__ 161 | #define __PT_SEM_H__ 162 | 163 | #include "pt.h" 164 | 165 | struct pt_sem { 166 | unsigned int count; 167 | }; 168 | 169 | /** 170 | * Initialize a semaphore 171 | * 172 | * This macro initializes a semaphore with a value for the 173 | * counter. Internally, the semaphores use an "unsigned int" to 174 | * represent the counter, and therefore the "count" argument should be 175 | * within range of an unsigned int. 176 | * 177 | * \param s (struct pt_sem *) A pointer to the pt_sem struct 178 | * representing the semaphore 179 | * 180 | * \param c (unsigned int) The initial count of the semaphore. 181 | * \hideinitializer 182 | */ 183 | #define PT_SEM_INIT(s, c) (s)->count = c 184 | 185 | /** 186 | * Wait for a semaphore 187 | * 188 | * This macro carries out the "wait" operation on the semaphore. The 189 | * wait operation causes the protothread to block while the counter is 190 | * zero. When the counter reaches a value larger than zero, the 191 | * protothread will continue. 192 | * 193 | * \param pt (struct pt *) A pointer to the protothread (struct pt) in 194 | * which the operation is executed. 195 | * 196 | * \param s (struct pt_sem *) A pointer to the pt_sem struct 197 | * representing the semaphore 198 | * 199 | * \hideinitializer 200 | */ 201 | #define PT_SEM_WAIT(pt, s) \ 202 | do { \ 203 | PT_WAIT_UNTIL(pt, (s)->count > 0); \ 204 | --(s)->count; \ 205 | } while(0) 206 | 207 | /** 208 | * Signal a semaphore 209 | * 210 | * This macro carries out the "signal" operation on the semaphore. The 211 | * signal operation increments the counter inside the semaphore, which 212 | * eventually will cause waiting protothreads to continue executing. 213 | * 214 | * \param pt (struct pt *) A pointer to the protothread (struct pt) in 215 | * which the operation is executed. 216 | * 217 | * \param s (struct pt_sem *) A pointer to the pt_sem struct 218 | * representing the semaphore 219 | * 220 | * \hideinitializer 221 | */ 222 | #define PT_SEM_SIGNAL(pt, s) ++(s)->count 223 | 224 | #endif /* __PT_SEM_H__ */ 225 | 226 | /** @} */ 227 | /** @} */ 228 | 229 | -------------------------------------------------------------------------------- /pt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004-2005, Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Contiki operating system. 30 | * 31 | * Author: Adam Dunkels 32 | * 33 | * $Id: pt.h,v 1.7 2006/10/02 07:52:56 adam Exp $ 34 | */ 35 | 36 | /** 37 | * \addtogroup pt 38 | * @{ 39 | */ 40 | 41 | /** 42 | * \file 43 | * Protothreads implementation. 44 | * \author 45 | * Adam Dunkels 46 | * 47 | */ 48 | 49 | #ifndef __PT_H__ 50 | #define __PT_H__ 51 | 52 | #include "lc.h" 53 | 54 | struct pt { 55 | lc_t lc; 56 | }; 57 | 58 | #define PT_WAITING 0 59 | #define PT_YIELDED 1 60 | #define PT_EXITED 2 61 | #define PT_ENDED 3 62 | 63 | /** 64 | * \name Initialization 65 | * @{ 66 | */ 67 | 68 | /** 69 | * Initialize a protothread. 70 | * 71 | * Initializes a protothread. Initialization must be done prior to 72 | * starting to execute the protothread. 73 | * 74 | * \param pt A pointer to the protothread control structure. 75 | * 76 | * \sa PT_SPAWN() 77 | * 78 | * \hideinitializer 79 | */ 80 | #define PT_INIT(pt) LC_INIT((pt)->lc) 81 | 82 | /** @} */ 83 | 84 | /** 85 | * \name Declaration and definition 86 | * @{ 87 | */ 88 | 89 | /** 90 | * Declaration of a protothread. 91 | * 92 | * This macro is used to declare a protothread. All protothreads must 93 | * be declared with this macro. 94 | * 95 | * \param name_args The name and arguments of the C function 96 | * implementing the protothread. 97 | * 98 | * \hideinitializer 99 | */ 100 | #define PT_THREAD(name_args) char name_args 101 | 102 | /** 103 | * Declare the start of a protothread inside the C function 104 | * implementing the protothread. 105 | * 106 | * This macro is used to declare the starting point of a 107 | * protothread. It should be placed at the start of the function in 108 | * which the protothread runs. All C statements above the PT_BEGIN() 109 | * invokation will be executed each time the protothread is scheduled. 110 | * 111 | * \param pt A pointer to the protothread control structure. 112 | * 113 | * \hideinitializer 114 | */ 115 | #define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc) 116 | 117 | /** 118 | * Declare the end of a protothread. 119 | * 120 | * This macro is used for declaring that a protothread ends. It must 121 | * always be used together with a matching PT_BEGIN() macro. 122 | * 123 | * \param pt A pointer to the protothread control structure. 124 | * 125 | * \hideinitializer 126 | */ 127 | #define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \ 128 | PT_INIT(pt); return PT_ENDED; } 129 | 130 | /** @} */ 131 | 132 | /** 133 | * \name Blocked wait 134 | * @{ 135 | */ 136 | 137 | /** 138 | * Block and wait until condition is true. 139 | * 140 | * This macro blocks the protothread until the specified condition is 141 | * true. 142 | * 143 | * \param pt A pointer to the protothread control structure. 144 | * \param condition The condition. 145 | * 146 | * \hideinitializer 147 | */ 148 | #define PT_WAIT_UNTIL(pt, condition) \ 149 | do { \ 150 | LC_SET((pt)->lc); \ 151 | if(!(condition)) { \ 152 | return PT_WAITING; \ 153 | } \ 154 | } while(0) 155 | 156 | /** 157 | * Block and wait while condition is true. 158 | * 159 | * This function blocks and waits while condition is true. See 160 | * PT_WAIT_UNTIL(). 161 | * 162 | * \param pt A pointer to the protothread control structure. 163 | * \param cond The condition. 164 | * 165 | * \hideinitializer 166 | */ 167 | #define PT_WAIT_WHILE(pt, cond) PT_WAIT_UNTIL((pt), !(cond)) 168 | 169 | /** @} */ 170 | 171 | /** 172 | * \name Hierarchical protothreads 173 | * @{ 174 | */ 175 | 176 | /** 177 | * Block and wait until a child protothread completes. 178 | * 179 | * This macro schedules a child protothread. The current protothread 180 | * will block until the child protothread completes. 181 | * 182 | * \note The child protothread must be manually initialized with the 183 | * PT_INIT() function before this function is used. 184 | * 185 | * \param pt A pointer to the protothread control structure. 186 | * \param thread The child protothread with arguments 187 | * 188 | * \sa PT_SPAWN() 189 | * 190 | * \hideinitializer 191 | */ 192 | #define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread)) 193 | 194 | /** 195 | * Spawn a child protothread and wait until it exits. 196 | * 197 | * This macro spawns a child protothread and waits until it exits. The 198 | * macro can only be used within a protothread. 199 | * 200 | * \param pt A pointer to the protothread control structure. 201 | * \param child A pointer to the child protothread's control structure. 202 | * \param thread The child protothread with arguments 203 | * 204 | * \hideinitializer 205 | */ 206 | #define PT_SPAWN(pt, child, thread) \ 207 | do { \ 208 | PT_INIT((child)); \ 209 | PT_WAIT_THREAD((pt), (thread)); \ 210 | } while(0) 211 | 212 | /** @} */ 213 | 214 | /** 215 | * \name Exiting and restarting 216 | * @{ 217 | */ 218 | 219 | /** 220 | * Restart the protothread. 221 | * 222 | * This macro will block and cause the running protothread to restart 223 | * its execution at the place of the PT_BEGIN() call. 224 | * 225 | * \param pt A pointer to the protothread control structure. 226 | * 227 | * \hideinitializer 228 | */ 229 | #define PT_RESTART(pt) \ 230 | do { \ 231 | PT_INIT(pt); \ 232 | return PT_WAITING; \ 233 | } while(0) 234 | 235 | /** 236 | * Exit the protothread. 237 | * 238 | * This macro causes the protothread to exit. If the protothread was 239 | * spawned by another protothread, the parent protothread will become 240 | * unblocked and can continue to run. 241 | * 242 | * \param pt A pointer to the protothread control structure. 243 | * 244 | * \hideinitializer 245 | */ 246 | #define PT_EXIT(pt) \ 247 | do { \ 248 | PT_INIT(pt); \ 249 | return PT_EXITED; \ 250 | } while(0) 251 | 252 | /** @} */ 253 | 254 | /** 255 | * \name Calling a protothread 256 | * @{ 257 | */ 258 | 259 | /** 260 | * Schedule a protothread. 261 | * 262 | * This function shedules a protothread. The return value of the 263 | * function is non-zero if the protothread is running or zero if the 264 | * protothread has exited. 265 | * 266 | * \param f The call to the C function implementing the protothread to 267 | * be scheduled 268 | * 269 | * \hideinitializer 270 | */ 271 | #define PT_SCHEDULE(f) ((f) < PT_EXITED) 272 | 273 | /** @} */ 274 | 275 | /** 276 | * \name Yielding from a protothread 277 | * @{ 278 | */ 279 | 280 | /** 281 | * Yield from the current protothread. 282 | * 283 | * This function will yield the protothread, thereby allowing other 284 | * processing to take place in the system. 285 | * 286 | * \param pt A pointer to the protothread control structure. 287 | * 288 | * \hideinitializer 289 | */ 290 | #define PT_YIELD(pt) \ 291 | do { \ 292 | PT_YIELD_FLAG = 0; \ 293 | LC_SET((pt)->lc); \ 294 | if(PT_YIELD_FLAG == 0) { \ 295 | return PT_YIELDED; \ 296 | } \ 297 | } while(0) 298 | 299 | /** 300 | * \brief Yield from the protothread until a condition occurs. 301 | * \param pt A pointer to the protothread control structure. 302 | * \param cond The condition. 303 | * 304 | * This function will yield the protothread, until the 305 | * specified condition evaluates to true. 306 | * 307 | * 308 | * \hideinitializer 309 | */ 310 | #define PT_YIELD_UNTIL(pt, cond) \ 311 | do { \ 312 | PT_YIELD_FLAG = 0; \ 313 | LC_SET((pt)->lc); \ 314 | if((PT_YIELD_FLAG == 0) || !(cond)) { \ 315 | return PT_YIELDED; \ 316 | } \ 317 | } while(0) 318 | 319 | /** @} */ 320 | 321 | #endif /* __PT_H__ */ 322 | 323 | /** @} */ 324 | -------------------------------------------------------------------------------- /sample/TestPull.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xinqiangren/TinyActor/174933a7863d765a64f543bfb8caaae76ea2f14d/sample/TestPull.cpp -------------------------------------------------------------------------------- /sample/TestPush.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xinqiangren/TinyActor/174933a7863d765a64f543bfb8caaae76ea2f14d/sample/TestPush.cpp --------------------------------------------------------------------------------