Kilim是一個Java的actor框架,讓你可以在JVM里使用基于協程的actor模型,bluedavy曾經介紹過,這里不再贅言。這篇blog的目的在于分析下kilim實現的基本原理,看看怎么在JVM上實現協程。
在一些語言層面上支持協程的語言,如lua、ruby,都是直接在VM級別支持協程,VM幫你做context的保存和恢復。JVM沒有提供這樣的指令來保存和恢復方法棧的狀態,因此kilim的實現還是需要在bytecode級別做文章。首先,試想下,如果是你來實現協程,你會怎么做?協程的兩個基本原語resume和yield,resume運行協程,yield讓出執行權,下次resume的時候會從yield的地方重新執行,并且context保持不變。可見,你需要做這么幾個事情:
1、在yield的時候保存當前context。
2、在resume的時候恢復context,并根據pc計數來決定從哪里恢復執行。
3、半協程的實現來說,還需要一個調度器來調度所有協程。
4、為了做到用戶代碼透明,可能需要某種手段去修改用戶代碼,自動幫你做上面三個事情。
kilim的實現就是干了這么幾個事情:
1、利用字節碼增強,將普通的java代碼轉換為支持協程的代碼。
2、在調用pausable方法的時候,如果pause了就保存當前方法棧的State,停止執行當前協程,將控制權交給調度器
3、調度器負責調度就緒的協程
4、協程resume的時候,自動恢復State,根據協程的pc計數跳轉到上次執行的位置,繼續執行。
下面是來自kilim文檔的一個例子,同一段代碼,在字節碼增前前后的變化:
左邊是原始代碼,右邊是通過字節碼增強后的代碼。其中h方法是pausable的,也就是說可能被暫停阻塞的,g方法因為調用了h這個方法也變成了pausable。
首先看,原始的h方法是沒有傳入任何參數的,增強后的代碼,多了個參數Fiber,指向當前的協程,同樣,g方法本來只有一個參數n,現在在后面也多了個Fiber類型的參數,同樣是指向當前執行的協程。
其次,在原始的g方法里,一旦調用,馬上進入一個for循環。但是在增強后的代碼,多了個switch派發的過程,這就是前面提到的,根據當前的Fiber的pc計數,跳轉到上一次執行的地方執行。如果是第一次resume,也就是啟動協程,那么就將初始循環的i設置為0,進入原始代碼的循環部分。Fiber有一個pc計數,稱為程序計數器,用于指向恢復context的時候需要跳轉到位置。
第三,在g方法里調用h這個可被暫停阻塞的方法的時候,在h方法前后多了一些調用:
f.down();
h(f);
f.up();
kilim的Fiber將每個pauseable方法的調用組織成一個棧,每個pauseable方法都有一個activation frame,翻譯過來可以稱為活動棧幀,這個棧幀記錄了當前的棧的State,注意這個棧跟java本身的方法調用棧區分開來,一個是VM層面的,一個是kilim框架層面的。這里的down方法就是將棧向下延伸,表示將調用一個pauseable方法,并且設置當前State和pc計數。
調用了down之后,才是調用實際的h方法,最后還要調用一次up,顧名思義,就是說一次pauseable方法調用完成,fiber的活動棧要遞增一層,回到上一層。但是h方法調用可能出現四種情況:
1、正常的順利返回,沒有狀態需要恢復,所謂NOT_PAUSING__NO_STATE
2、也是正常返回,有狀態需要恢復,也就是NOT_PAUSING__HAS_STATE
3、h方法暫停阻塞,當前沒有保存狀態,需要保存狀態,這是第一次暫停的時候,稱為PAUSING__NO_STATE
4、h方法暫停阻塞,當前已經有狀態,不需要保存狀態,這是第一次暫停之后的resume再次暫停,稱為PAUSING__HAS_STATE,通常不需要處理什么。
第四,可以看到,在up之后,就要根據up返回的上述4種狀態執行不同的邏輯:
if (f.isPausing){
//第一次暫停,沒有狀態
if (!f.hasState){
//new一個State_I2,并保存i和n
f.state = new State_I2(i,n);
//記錄pc,還記的前面的switch嗎?
f.pc = H1;
}
return;
} else if (f.hasState)
//正常返回,有狀態需要恢復,恢復i和n
State_I2 st = (State_I2) f.state;
i = st.i1; n = st.i2;
}
這里沒有處理NOT_PAUSING__NO_STATE和PAUSING__HAS_STATE,因為這兩種情況在這里不需要處理。
通過上面的分析,我想大家對kilim的實現應該已經有一個很基本的認識。下一步,我們分析一個實際的代碼例子,查看整個運作流程。