singleExecutionで強制終了した場合でも大丈夫なように修正
昨日作ったsingleExcutionですが、思いのほか好評なようでよかった。^o^
昨日作ったバージョンだと通常通りにスクリプトが終了すれば問題ないのですが、何らかの原因でプロセスが終了した場合にロックが残ってしまい、手動でロックを削除しないといけなかったのですが、これでは使いづらくてしょうがないので、ロックも自動で削除するように修正しました。
シグナルハンドラを登録するようにした
次のようにしてシグナルハンドラを登録して、強制終了させられたタイミングでunlockするようにしてみました。
でもなぜか動かない...
なんでやねん!!
if (singleExecution::loadExtension('pcntl')) {
$this->key = $key;
pcntl_signal(SIGTERM, array($this, 'unlockExit'));
pcntl_signal(SIGHUP, array($this, 'unlockExit'));
}
ロックしたプロセスが存在するか調べるようにした
ロックディレクトリにプロセスIDを保存するようにしました。
このプロセスIDを使ってプロセスが存在するどうか調べて、存在しなければunlockするようにしました。
プロセスが存在するかどうか調べる
プロセスIDからプロセスが存在するかどうかは、シグナル番号0を送信してみれば分かります。
phpの場合はposix拡張がないとシグナルが送れないので、その場合はpsコマンドを使って調べます。
このやり方だとpsコマンドの出力結果が変わると動かなくなるので、あまりよくないんですが...
function processExists($pid)
{
if (!$this->loadExtension('posix')) {
return posix_kill($pid, 0);
} else {
return intval(exec("ps -e|grep '^ *{$pid} '"));
}
}
ロックが不正な場合はunlock
これでロックが不正かどうか調べる準備が整ったので、不正な場合はunlockするようにしました。
不正なロックのunlock処理は不整合が起きないようにクリティカルセクションに入れました。
function unlockIfInvalid($key)
{
$common_key = 'common';
// spin lock
$common_lock = $this->lockDirectory($common_key);
while (!$this->mkdir($common_lock)) {
usleep(10000);
}
// critical section
if (!$this->isInvalidLock($this->lockDirectory($key))) {
$this->unlock($key);
}
// unlock
$this->unlock($common_key);
}
かなり堅牢になった
想定されるケースはつぶしたので、かなり堅牢になったはず。
何かうまく動かない場合がありましたら、ご連絡ください。