読者です 読者をやめる 読者になる 読者になる

Erlangで排他ロック(のようなもの)を提供する

本来ロックなど存在しないErlangの処理系(というか言語仕様)に排他ロックを作ってみるという暴挙。registered nameがuniqueなことを利用して排他ロックを作る。どこの空間までロックを制限できるかも分からないし、どの程度の負荷に耐えられるのかも微妙というか全くの未知数。使い方:

Eshell V5.6.3  (abort with ^G)
1> c(locker).
{ok,locker}
2> locker:lock(hoge, a).
{ok,lock_aquired,<0.38.0>,<0.31.0>}
3> locker:lock(hoge, a).
{ng,already_locked}
4> locker:unlock(hoge, a).
{released,{<0.31.0>,a}}
5> locker:lock(hoge, a).  
{ok,lock_aquired,<0.43.0>,<0.31.0>}
6> 

なんで「ようなもの」かというと、ここがatomicなtest&setとかになっててほしいんだけどなっていないという悲しい現実から:

case whereis(LockName) of
    undefined ->
	register( LockName, Pid ),

(追記)
global:set_lock/1, global:del_lock/1でも十分。どうみても骨折り損です。本当にありがとうございました。

モジュールのソース全文は以下から。

-module(locker).
-author("kuenishi@gmail.com").

% simple advisory exclusive lock.
% I wish the sentence 'case whereid ~ of ~ register' were atomic...; 

-export([lock/2, unlock/2, lock_runner_/3]).

lock(LockName, LockMode)->
%    RealLockName=list_to_atom( ["lock_"|atom_to_list(LockName)]),
    Self=self(),
    spawn_link( fun()->
			Pid = self(),
			case whereis(LockName) of
			    undefined ->
				register( LockName, Pid ),
				Self ! {ok, lock_aquired, Pid, Self},
				lock_runner_(Self, LockName, LockMode);
			    _OtherPid ->
		%		io:format("other process[~p] already aquired the lock '~p'.~n", [OtherPid, LockName]),
				Self ! {ng, already_locked}
			end
		end ),
    receive
	Result-> Result
    end.
			    
unlock(LockName, LockMode)->
    case whereis(LockName) of 
	undefined ->
	    {ng, no_lock};
	_Pid ->
	    LockName ! {self(), LockName, LockMode},
	    receive
		Message ->   Message
	    after 1024 ->    failed %timeout by 1024ms
	    end
    end.

lock_runner_(LockHolder, LockName, LockMode)->
    receive 
 	{From, LockName, SomeLockMode}->
	    LockHolder ! {released, {From, SomeLockMode} },
	    {ok, released};
	_Other -> lock_runner_(LockHolder, LockName, LockMode)
    end.