From: matz@... (Yukihiro Matsumoto) Date: 1998-12-15T09:54:47+09:00 Subject: [ruby-list:11326] Forward: ANNOUNCEMENT: filelock.rb (long) まつもと ゆきひろです ruby-talkでClemens氏がfilelock.rbを発表しました. 本人に希望により ruby-list にもフォワードします. ------- Start of forwarded message ------- Date: Mon, 14 Dec 1998 23:24:48 +0000 (Local time zone must be set--see zic manual page) From: Clemens Hintze Subject: [ruby-talk:00095] ANNOUNCEMENT: filelock.rb (long) To: ruby-talk@netlab.co.jp (ruby-talk ML) Hello all, with help from matz and all of you who'ved answered my newbie questions, I am now able to announce my filelock.rb module. It should already be located at: ftp://ftp.netlab.co.jp/pub/lang/ruby/contrib If you find any error, or if you have any improvement proposal, please let me know. My email address is: mailto:c.hintze@gmx.net Following I have inserted the filelock.README to enable you to figure out the area for that filelock.rb could be used. Much fun, Cle. ____________________________filelock.README________________________________ filelock.rb 0.1 (c) C.Hintze 22nov1998 This module provides two classes to deal with lockfiles. Instances of LockFile represents real lockfiles. Mostly these file have the extension ".lock". If the instance is not any longer needed, the instance will delete the lockfile during finalization. Instances of LockedFile provides a possibility to open a certain file and handle the lockfile mechanism transparent for the user. If a LockedFile is not any longer needed, it will be closed, and the corresponding LockFile instance will be removed during finalization. What is it? =========== Sometimes it is not possible to use fctl mechanism to lock/unlock files. May it be, that fctl doesn't properly work on a given UNIX, may it be that your file resides on a mounted filesystem. UNIX mail delivery system itself use a lock mechanism via lockfiles. For every file I want to lock, a corresponding .lock file is created. If such a lockfile already exists, than the process have to wait until the lock was released (lockfile deleted) or have to steal the lock to have access on that certain file. Why you may need it? ==================== If, for example, you want to process your system mailbox, it may happen that while you are reading your mailbox, a new mail arrives. While you reading your mailbox, there is probably no problem. But if you also want to write your mailbox this may create a big desaster. Here you can use the LockedFile wrapper object. If you obtain the lock and then process your mailbox, you can be sure that in this very moment no write access from your mail delivery system will occur, until you close that file and therefore release the lock. See the function test2 at the bottom of the file, to see, how you COULD do it, if the module mailread.rb would allow other objects than derived from IO to serve as input. Algorithm: ========== If there is no another lock, the lockfile will be created. Otherwise, it must be decided whether the lock should be stolen or not. If the lock should not be stolen, the process will sleep for sleep seconds and then check, whether the existant lock is vanished or not. If not it sleeps again for sleep seconds, ... etc. This all would be done for retry times or forever if retry is nil. If the lock should be stolen, then the process will first sleeps for steal seconds. Then the lockfile of the other process will be removed. After that, the process again sleeps suspend seconds, to give the process from which the lock was stolen, the possibility to steal it back. If the other process has not stolen back its lock, the lockfile will be created. That all would be done for retry times or forever if retry is nil. If the lock could not be obtained after retry retries, an AcquireLockError will be thrown. Additonal Features: =================== The methods LockedFile::open and LockFile::create_for are able to deal with blocks like File::open does. LockedFile::open will hands over the reference to the LockedFile instance as parameter to the block and execute it. After executing, the LockedFile will be closed and the lock will be released. WARNING: You should not reopen the file within the block. Doing so will raise a FrozenLockError. LockFile::create_for will hands over a stolen lock checker lambda. You can invoke it with .call. It will return true, if the lock was stolen in the meantime; false otherwise. After the block is finished, the lock will be released if it was not stolen in the meantime. WARNING: You may unlock the lock explicity within the block, but you cannot obtain it again. Trying to do so will result in a FrozenLockError exception raised. Examples: ========= 1. Read in a whole UNIX mailbox. During the read-in, no new mail can be delivered by the UNIX mailsystem. contents = LockedFile::open(mailbox) { |mb| mb.read() } 2. Another way to safety deal with your mailbox: LockFile::create_for(mailbox) do |not_ok| fd = open(mailbox, "r+") ... # Do what you want with it. Filter mails, delete them ... # It's up to you! But don't forget to call the checker ... # regulary with not_ok.call fd.close end Here is a concrete but useless example: require "filelock" require "mailread" FileLock::LockFile::create_for(ARGV[0]) do |not_ok| fp = open(ARGV[0]) until fp.eof? or not_ok.call mail = Mail.new(fp) print "Subj=#{mail.header['Subject']}\n" sleep 1 end print "Lock was stolen!\n" if not_ok.call fp.close end But much more safety would be the following (NOT POSSIBLE USING Ruby 1.1c9!): require "filelock" require "mailread" fp = FileLock::LockedFile::open(ARGV[0]) begin until fp.eof? mail = Mail(fp) print "Subj=#{mail.header['Subject']}\n" sleep 1 end rescue FileLock::StolenLockError print "Lock was stolen!\n" ensure fp.close end Here the fact, that the lock was stolen, will be recognized on the soonest time possible, whereas in the first example, it will be only recognized during execution of until! That perhaps could be too late!!! Copyright notice ================ This source is copyrighted, but you can freely use and copy it as long as you don't change or remove the copyright notice: Now coming to the warranty. There is no warranty! Never and in no case ;-) I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. To-do: - Improve the speed, although I don't know how :-} Bugs: - Slow compared with Ruby's file objects, as on every access it has to been checked, whether the lock is still valid yet. I have used the Python version of that module for some time without encoutering any problem. As Ruby is very new for me, and I have to adapt certain things to Ruby's way of doing, I don't know, if I have all done correct. Furthermore I have added some new features not contained in the Python's version. So certainly there should be some errors. Please let me know, if you find one. mailto:c.hintze@gmx.net Describe what you have tried to do, and the contents of the version string contained in the variable version on top of the file. Please do not forget to attach an example, where the error occurs to enable me to reproduce the error. ------- End of forwarded message -------