2008年7月4日 星期五

Threading in Firefox 3

Firefox 3 (Gecko 1.9) 新增了許多特色 (詳見 ), 其中一項重要的特色, 便是導入了 Thread Manager 機制.

##CONTINUE##

在 Firefox 3 以前, 我們要讓 Javascript 程式在背景執行, 最簡單的方式便是利用 setTimeout / setInterval 方式:


function backgroundTask() {
// Perform a small amount of work

window.setTimeout(arguments.callee, 100);
}

window.setTimeout(backgroundTask, 100);

以上的方式雖然可以把程式丟到背景執行, 然而, 它依然只是在主執行緒(mainThread)中, 以非同步的方式執行.

Firefox 3 後, 導入了 Thread Manager , 讓我們可以撰寫平台相關的真正多執行緒程式.

要實作多執行緒 Javascript 程式,本身必須是 XPCOM Object, 且實作 nsIRunnable 介面(和 Java Thread 很像不是嗎?).
詳細範例可參考 Thread Manager nsIThreadManager .

Thread Utils:
雖然 Firefox 3 已經大大簡化了多執行緒程式的寫作方式(且很有 Java 風格), 但就 Javascript 來說, 就顯的有些麻煩, 我們要為每一個要執行的程式, 一一實作成 XPCOM Object 也是一件很煩人的事..
阿土伯在 GREUtils Rewriting 計劃中, 為 Threading 做了一些封裝, 讓您可以比較容易且直覺的使用 Firefox 3 所帶來的 Multi-Threading 這份大禮.

有關 Thread 部份的 Code 如下, 已修改過可以獨立運作於 GREUtils 整個專案之外:

/**
* Thread - is simple and easy use Thread Manager APIs libraries for GRE (Gecko Runtime Environment).
* ONLY Work with Firefox 3 or XULRunner 1.9
*
* @public
* @name GREUtils.Thread
* @namespace GREUtils.Thread
*/
var GREUtils = GREUtils || {};

GREUtils.Thread = {

_threadManager: Components.classes["@mozilla.org/thread-manager;1"]
.getService(Components.interfaces.nsIThreadManager),

_mainThread: null,

_workerThread: null,

reportError: function(err){
Components.utils.reportError(err);
}
};


/**
* getThreadManager
*
* @public
* @static
* @function
* @return {Object} nsIThreadManager
*/
GREUtils.Thread.getThreadManager = function(){
return this._threadManager;
};


/**
* getMainThread
*
* @public
* @static
* @function
* @return {Object} nsIThread main thread
*/
GREUtils.Thread.getMainThread = function(){
if (this._mainThread == null) {
this._mainThread = GREUtils.Thread.getThreadManager().mainThread;

// extends magical method to worker thread
// this._workerThread.dispatchMainThread = GREUtils.Thread.dispatchMainThread;
}
return this._mainThread;
};


/**
* dispatchMainThread
*
* @public
* @static
* @function
* @return {Object} nsIThreadManager
* @param {Object} aRunnable
* @param {Object} aType
*/
GREUtils.Thread.dispatchMainThread = function(aRunnable, aType) {
var mainThread = GREUtils.Thread.getMainThread();
var aType = aType || mainThread.DISPATCH_NORMAL;
try {
mainThread.dispatch(aRunnable, aType);
}catch (err) {
GREUtils.Thread.reportError(err);
}
};


/**
* dispatchWorkerThread
*
* @public
* @static
* @function
* @return {Object} nsIThreadManager
* @param {Object} workerThread
* @param {Object} aRunnable
* @param {Object} aType
*/
GREUtils.Thread.dispatchWorkerThread = function(workerThread, aRunnable, aType) {
var aType = aType || workerThread.DISPATCH_NORMAL;
try {
workerThread.dispatch(aRunnable, aType);
}catch (err) {
GREUtils.Thread.reportError(err);
}
};

/**
* getWorkerThread in pool
*
* @public
* @static
* @function
* @return {Object} nsIThread worker thread
*/
GREUtils.Thread.getWorkerThread = function(){
// get presist work thread
// will not create new worker thread
if (this._workerThread == null) {
this._workerThread = GREUtils.Thread.getThreadManager().newThread(0);

// extends magical method to worker thread
// this._workerThread.dispatchMainThread = GREUtils.Thread.dispatchMainThread;
}
return this._workerThread;
};


/**
* createWorkerThread - create new worker thread.
*
* @public
* @static
* @function
* @return {Object} nsIThread worker thread
*/
GREUtils.Thread.createWorkerThread = function(){
// create new worker thread
var worker = GREUtils.Thread.getThreadManager().newThread(0);

// extends magical method to worker thread
//worker.dispatchMainThread = GREUtils.Thread.dispatchMainThread;

return worker;
};


/**
* CallbackRunnableAdapter
*
* @public
* @class
* @param {Object} func
* @param {Object} data
*/
GREUtils.Thread.CallbackRunnableAdapter = function(func, data) {
this._func = func;
this._data = data;
};

GREUtils.Thread.CallbackRunnableAdapter.prototype = {

get func() {
return this._func;
},

set func(func){
this._func = func || null;
},

get data() {
return this._data;
},

set data(data){
this._data = data || null;
},

run: function() {
try {
if (this.func) {
if(this.data) this.func(this.data);
else this.func();
}
} catch (err) {
Components.utils.reportError(err);
}
},

QueryInterface: function(iid) {
if (iid.equals(Components.Interfaces.nsIRunnable) || iid.equals(Components.Interfaces.nsISupports)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
};


/**
* WorkerRunnableAdapter
*
* @public
* @class
* @param {Object} func
* @param {Object} callback
* @param {Object} data
*/
GREUtils.Thread.WorkerRunnableAdapter = function(func, callback, data) {
this._func = func;
this._callback = callback;
this._data = data;

if(arguments.length == 2 ) {
this._data = callback;
this._callback = null;
}
};

GREUtils.Thread.WorkerRunnableAdapter.prototype = {

get func() {
return this._func;
},

set func(func){
this._func = func || null;
},

get callback() {
return this._callback;
},

set callback(callback){
this._callback = callback || null;
},

get data() {
return this._data;
},

set data(data){
this._data = data || null;
},

run: function() {
try {
var result = null;
if (this.func) {
if(this.data) result = this.func(this.data);
else result = this.func();
}

if (this.callback) {
GREUtils.Thread.dispatchMainThread(new GREUtils.Thread.CallbackRunnableAdapter(this.callback, result));
}
} catch (err) {
Components.utils.reportError(err);
}

},

QueryInterface: function(iid) {
if (iid.equals(Components.Interfaces.nsIRunnable) || iid.equals(Components.Interfaces.nsISupports)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
};


/**
* createWorkerThreadAdapter
*
* @public
* @static
* @function
* @param {Object} workerFunc
* @param {Object} callbackFunc
* @param {Object} data
*/
GREUtils.Thread.createWorkerThreadAdapter = function(workerFunc, callbackFunc, data) {

return new GREUtils.Thread.WorkerRunnableAdapter(workerFunc, callbackFunc, data);
};


使用方式:
利用 GREUtils.Thread.getMainThread() 可以取得主執行緒.
利用 GREUtils.Thread.createWorkerThread() 產生一個新的執行緒.

WorkerThreadAdapter:
則是用以簡化自行撰寫 worker / callback 二個 XPCOM Object, 取而代之的是二個 Function handle 以及要傳入 worker thread 的資料.


var worker = GREUtils.Thread.createWorkerThread();
var execute = GREUtils.Thread.createWorkerThreadAdapter(
/* worker function */
function(data) {
var result = 0;
var i = 0;

while(i <= data) {
result += i;
i++;
}
return result;
},
/* callback function */
function(result) {
alert('sum = ' + result);
},
/* data to worker function */
10000
);

GREUtils.Thread.dispatchWorkerThread(worker, execute);



註1:
Firefox3 的 Thread 由於是以 XPCOM 實作, 所以您只能用於 extension addons 寫作或是 XULRunner 專案中, 如果您需要在一般 Web Application 中使用多執行緒, 可以參考 Google Gears 的 WorkerPool 實作.

註2:
請特別注意 The Thread Manager 那篇的這句重要的警告.
Note: The DOM is not thread safe. You must not access the DOM or user interface from background threads. Doing so will likely crash.
其中要再注意的是, alert() 也是 DOM global scope 哦, 如果您在 background threads 用了 alert(), Firefox 3 也是會當給你看地.


忙裡偷閒寫下本篇, 內容有點雜亂簡單, 但還是希望對大家有幫助.

2008年5月11日 星期日

Processing.js

Processing.js. jQuery 作者 John Resig’s 將 processing.org Java implementation's Processing Language port to JavaScript and Canvas.

而且實測, 在 Firefox 3 執行的很完美, 真是有夠強.

有興趣的可以玩玩 Processing.js 的 Basic Demo.
和 processing.org java 版的 Basic Demo.
只能說..太酷了..

其中有幾樣和 Processing Language 無關的 javascript 實作, 也很值得研究.

  • Types and type casting
  • Classes
  • Method overloading and multiple constructors
  • Inheritance

2008年5月2日 星期五

Nimbus Theme on Ubuntu 8.04


Nimbus ( 各組件詳細 Nimbus SPEC ) 是 Sun Solaris Desktop 預設的就相當有質感的 Theme . 在未來的 JDK 6u10 中也將加入 Nimbus LookAndFeel.
所以利用這次重裝 Ubuntu 8.04 之餘, 順便把 Nimbus GTK2 engine 以及 Icons-theme 加入 Ubuntu 8.04 中.

阿土伯目前的 Ubuntu ScreenShot:


Compile and Install:
首先請先確認您有安裝打包 dpkg 相關的開發環境

sudo apt-get install fakeroot dpkg-dev devscripts icon-naming-utils

接下來就是下載 Nimbus 最新的 0.0.9 Source, 以及 deb 的 patch 包.

wget -c http://dlc.sun.com/osol/jds/downloads/extras/nimbus-0.0.9.tar.bz2
wget -c http://zap.tartarus.org/~ds/debian/dists/sid/main/source/nimbus_0.0.6-1.diff.gz


Compile:

tar xf nimbus-0.0.9.tar.bz2
cd nimbus-0.0.9
zcat ../nimbus_0.0.6-1.diff.gz | patch -p1
chmod +x debian/rules
debchange -v 0.0.9-1
sudo apt-get build-dep
fakeroot dpkg-buildpackage -us -uc

在一陣等待後, 您應該會在您的家目錄得到二個deb, (gtk2-engines-nimbus_0.0.6-1_i386.deb / nimbus-icon-theme_0.0.6-1_all.deb )

安裝 Nimbus dpkg:

sudo dpkg -i nimbus-icon-theme_0.0.6-1_all.deb
sudo dpkg -i gtk2-engines-nimbus_0.0.6-1_i386.deb

您應該可以在外觀設定看到 Nimbus 嘍.

懶人包:
OK!OK! 阿土伯了解大家的痛苦, 懶人包在此, 請直接下載這二個 deb , 然後依照安裝方式安裝即可.
gtk2-engines-nimbus_0.0.6-1_i386.deb(45K)
nimbus-icon-theme_0.0.6-1_all.deb(9M)

2008年4月29日 星期二

安裝 Ubuntu 8.04 於 Thinkpad T61 雜記

前幾天把 Ubuntu 8.04 Desktop Edition 版本裝在 lenovo Thinkpad T61 上面.
安裝上算是例來版本最順暢的版本, 不用調整 acpi / sata 設定, 一路安裝到底就可以完成安裝嘍.
基本上所有 T61 上的硬體都能正確抓到, 而 nvidia driver 也只需透過 restricted driver 確認的動作便完成了.
當然, 尚許多美中不足的地方需要調效,本篇並不是安裝教學,請參考官方或網路上為數眾多之教學文件.

Wireless LED 不會亮:
雖說 Intel 4965AGN 的 wireless driver 正確掛上, 且亦可以正確使用, 但是主機上那個 wireless led 不會亮, 就是那麼讓人爽度下降.
所以我們需要自己更新 intel compat wireless driver. (當然, 除了 T61 其它品牌 notebook 亦可照作).
首先先到 http://linuxwireless.org/ 下載最新的 driver, link here.
解壓後, 請修改 config.mk 加入 LED 的 options.

CONFIG_IWL3945_LEDS=y
CONFIG_IWLWIFI_LEDS=y

然後 sudo make install 即可完成更新,由於網路 module 使用中, 通常需要重開.
您就會看到您主機上的 wireless LED 開始可愛的一閃一閃了.

Trackpoint 中間鍵
加入以下幾行在 /etc/X11/xorg.conf 的 mouse 設定中:

Option "Device" "/dev/input/mice"
Option "Protocol" "ImPS/2"
Option "ZAxisMapping" "4 5"
Option "Emulate3Buttons" "true"
Option "EmulateWheel" "true"
Option "EmulateWheelButton" "2"


中文輸入法的更換:
雖然我對 SCIM 沒有好惡問題, 但是經過半天的使用, 發現 pidgin 在輸入字時,
會始整個視窗 hang 住一下, 然後久久才取得 focus .
一開始以為是 compiz 特效問題, 後來發現是 scim 怪怪的, 沒有去深究為什麼, 換成 gcin 試試.
於是改裝套件庫中的 gcin gcin-qt3-immodule, pidgin 果然不會卡卡的.
但是另一個問題接著而來, 那就是部份程式在結束時會有 segment fault 問題發生, Firefox / MySQL Query Browser 都會.
所以,又要展開昇級的動作, 由於 hardy 套件庫內的版本只到 1.3.7 版本. 新的穩定版是 1.3.9 可以解決這個問題.
於是我們使用 debian etch 套件庫來更新, 請參考 http://www.calno.com/moto/gcin/

deb http://www.calno.com etch-backports main

加入您 Synaptic 第三方套件列表中->更新套件.
即可完成 1.3.9 版本的安裝, 現在 firefox / MySQL Query Browser 可以正常使用了.

中文字體 AA 小字不好看:
由於 Ubuntu 8.04 內建已經使用文泉驛正黑體, 由於 wqy-zenhei 內建含了點陣的明體,所以小字會使用到點陣字.
所以我們需要關掉它. (詳見官網).
請下載官網中的 [44-wqy-zenhei.conf] , 並換掉系統中的 /etc/fonts/conf.d/44-wqy-zenhei.conf .
在此, 由於官網的 match 只用了 "WenQuanYi Zen Hei" 如果在選字時用了 "文泉驛正黑" 的中文 family name 將不會生效, 建議您將
44-wqy-zenhei.conf test 部份改成如下:

<test qual="any" name="family">
<string>WenQuanYi Zen Hei</string>
<string>文泉驿正黑</string>
<string>文泉驛正黑</string>
</test>


CPU 時脈調整
預設的 CPU freq 模式是 Ondemand, 會自動依 CPU 使用情況調整 CPU 時脈, 但是常常會造成程式 delay 或切換過慢的問題. 所以我們需要讓 cpufreq-selector 可以調整時脈.

sudo dpkg-reconfigure gnome-applets

回答 YES , 讓 cpufreq-selector 擁有 SUID root 權限. 將 cpufreq-selector 加入面版,現在可以很容易依需要調整 CPU 時脈.


Suspend / Hibernate Support
讓 fn+f4 suspend 以及 fn+f12 能正常作業.
修改 /etc/default/acpi-support

ACPI_SLEEP=true
ACPI_HIBERNATE=true
ACPI_SLEEP_MODE=mem
MODULES="e1000 iwl4965 nvidia"
HIBERNATE_MODE=platform

其中的 modules 則是 "removed before suspend and reloaded on resume" 所以可以有效解決 resume 畫面一片白/無線網路不能連等問題(至少阿土伯電腦沒再出現過了).

讓 Ubuntu 更好用:
當然, 不能忘了 lazybuntu 最新的 0.1.5 版也支援 hardy 了.

以上, 是目前阿土伯安裝的心得, 陸續調適, 再行增加整理.

2008年3月10日 星期一

另人遺憾的 Google AuthSub API 有感

身為一個 Google Fans 及 Programmer, 對於參與開發的 Web Application , 總是極力希望能直接整合 Google Services. 甚而連 User Singup 都不用了, 直接使用 Google Account 或 OpenID 進行 Authenticate , 然後直接使用我們的服務..

然而 Google Account API 中的 AuthSub API 似乎不能把它拿來當 Account Identification 用. 因為它無法傳回 UID (username / hash or anythings).

如果我們要拿來識別自己的 local user, 看來只能使用 ClientLogin, 要使用者填入 username (gmail) / password ,.....
這...是怎樣, 在現在釣魚網站這麼多, 而又是一個新創網站而言, 要使用者填入它的 Google username / password 是幾乎不可能的吧...


建議不用改變太多, 至少擴充一下 AuthSubTokenInfo 多傳一行 user_hash or UID 應該沒有安全性上考量吧.. 查了一下 Groups , 很多開發者有和阿土伯相同的困擾, 就差最後一點功夫, 很遺憾的, Google 缺少了它....

現在我們的 Service 放棄了 Google Login 的功能, 阿土伯身為 GFans , 好不甘心呀~~~~

建議:
一. 不改變現有任何 API , 但對於同一個 Username 對於同一個 next 同一個 scope 操作, 回傳同一個
session token , 因為目前 session token 並不會失效, 所以我們可以 "暫時" 拿來當同一個使用者的識別之用.

二. 只改變 AuthSubTokenInfo , 目前有的三行資訊, 多加入一行 user_hash or anything 可以反映相同使用者的資訊都可...

Google Groups: http://tinyurl.com/2yr8hx