uthread
協程的概念在coroutine源碼分析中有介紹。phxrpc默認使用ucontext作實現,同時還有boost優化版本。
UThreadContext
UThreadContext
是定義了協程接口的基類,並且有一個靜態函數對象,用來創建協程上下文,其應該是子類的DoCreate
函數,
UThreaStackMemory
UThreaStackMemory
是每個協程的私有棧,這裏並沒有實現共享棧模式,節省了拷貝,而且內存分配也沒有使用malloc
,而是使用的mmap
,這裏設置了一個標誌變量need_protect_
來選擇是否開啓保護模式,開啓保護模式會在棧兩端各多分配一頁,並將這兩頁設置PROT_NONE
屬性禁止訪問。調用mmap
時同時設置了MAP_ANONYMOUS | MAP_PRIVATE
,MAP_ANONYMOUS
表示這段內存是匿名的,不需要讀寫fd。MAP_PRIVATE
建立一個私有映射,不與其他進程共享。
UThreadContextSystem
UThreadContextSystem
是默認的使用ucontext作實現的協程上下文。每個上下文維護一個context_
表示協程的上下文,同時還有一個static __thread
修飾的main_context_
,表示每個線程只有一個,協程yield
的時候會切換到這個上下文。
Resume
函數切換到一個協程,Yield
函數切出當前協程。
UThreadFuncWrapper
包裝了協程的執行函數,協程運行時會切換到這個函數,函數的參數就是this指針,之後調用綁定的執行函數UThreadFunc_t
和回調函數UThreadDoneCallback_t
。
這裏之所以將指針拆成兩個32位是因爲setcontext
接受的是int
類型的參數。
UThreadRuntime
UThreadRuntime
封裝了協程的調度,context_list_
保存所有協程上下文,這裏把每個上下文封裝成一個ContextSlot
,其中的next_done_item
保存下一個可用的slot下標。
first_done_item_
可以看做始終保存一個已經完成的上下文的下標。Create
函數創建一個上下文,首先檢查first_done_item_
是否大於0 ,如果是說明此時有執行完的協程,更新first_done_item_
的值然後直接更換此協程的上下文。UThreadDoneCallback
回調將當前first_done_item_
保存到next_done_item
,然後將其更新爲自身。這樣實現了上下文的複用。
Yield
和Resume
封裝了UThreadContext
對應的操作。
UThreadEpollScheduler
UThreadEpollScheduler
封裝了epoll驅動的協程調度,UThreadSocket_t
封裝了socket及其他相關資源。
我們主要分析一下RunForever
函數的執行過程。
RunForever
首先會調用EpollNotifier
的Run
函數,Run
函數會將其Func
函數加入調度器的任務隊列,Func
函數會去讀管道,這樣做是爲了喚醒epoll。
接下來調用ConsumeTodoList
函數,會將任務隊列中的函數創建爲協程,並Resume
切換到協程,協程中會將fd相應的操作在epoll中註冊然後Yield
回到Run
。之後Run
函數調用epoll_wait
檢查活動的fd,並Resume
到活動fd的協程進行IO操作。這樣實現了異步操作。
處理完活動的事件後,還會不斷調用active_socket_func_()
繼續Resume
。
最後執行新建連接的回調並處理超時事件。
主要的執行流程如下:
UTreadPoll
UTreadPoll
有兩個版本,分別poll一個和一組socket,主要功能就是epoll_ctl註冊對應事件後Yield
,Resume
回來後刪除註冊。
當poll一組socket時的實現比較特殊,首先epoll_create
一個新的epollfd
,將所有的socket註冊到新的epollfd
中,然後新建一個UThreadSocket_t
將其中的socketfd
設爲剛纔新建的epollfd
,並註冊到調度器的epoll
中,這樣新建的epoll
有活動事件時會觸發調度器的epoll
Resume
回來,注意此時對新建的epollfd
執行epoll_wait
的超時參數設爲0,因爲一定有活動的事件。
UThreadAccept
,UThreadRead
等函數都是利用UThreadPoll改造的IO函數,比較簡單。
__uthread
__uthread
重載了-
操作符,來實現使用uthread_t
將協程加入調度器任務隊列。
uthread_begin
, uthread_end
, uthread_s
, uthread_t
這幾個自定義的宏,分別表示協程的準備,結束,協程調度器以及協程的創建。