/[pub]/suikawiki/script/lib/Yuki/YukiWikiDB_Lock.pm
Suika

Contents of /suikawiki/script/lib/Yuki/YukiWikiDB_Lock.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (hide annotations) (download)
Tue Jul 29 09:16:06 2003 UTC (22 years, 5 months ago) by wakaba
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +2 -2 lines
FILE REMOVED
*** empty log message ***

1 wakaba 1.1
2     package Yuki::YukiWikiDB_Lock;
3 wakaba 1.2 ($VERSION) = q($Revision: 1.1 $) =~ m/\x20([\d.]+)\x20/;
4 wakaba 1.1 use strict;
5    
6     sub _make_lock_file_name ($$) {
7     my ($self, $o) = @_;
8     return sprintf '%s.%d.%d.%d.lock', $o->{prefix}, $o->{seq},
9     $o->{time}, $o->{creation};
10     }
11    
12     sub _match_lock_file_name ($$%) {
13     my ($self, $fname, %o) = @_;
14     if ($fname =~ /^\Q$o{prefix}\E\.([0-9]+)\.([0-9]+)\.([0-9]+)\.lock$/) {
15     return {prefix => $o{prefix}, seq => $1, time => $2,
16     creation => $3};
17     }
18     return undef;
19     }
20    
21     ## Lock
22     ## Returns 1 if lock successed and 0 otherwise.
23     sub _lock ($$$;%) {
24     my ($self, $old_lock_name, $status, %opt) = @_;
25    
26     ## Share lock
27     if ($self->{-share}) {
28     if ($opt{force}) { # force lock
29     $status->{seq} = 0;
30     $status->{time} = time;
31     $status->{creation} = time;
32     } elsif ( # normal lock
33     ($status->{seq} == 0 && $status->{time} == 0) # not locked
34     || ($status->{seq} && $status->{time})) { # share locked
35     $status->{seq}++; ## Increase lock count
36     $status->{time} = time; ## Update locked date
37     $status->{creation} = time if $status->{seq} == 1;
38     } else { # Exclusively locked or unknown error
39     return 0;
40     }
41     ## Exclusive lock
42     } else {
43     if ($opt{force} # force lock
44     || ($status->{seq} == 0 && $status->{time} == 0)) { # not locked
45     $status->{seq} = 0;
46     $status->{time} = time; ## Locked date
47     $status->{creation} = time;
48     } else { # Already locked
49     return 0;
50     }
51     }
52    
53     my $newname = $self->{-directory}.$self->_make_lock_file_name
54     ($status);
55     if (rename $self->{-directory}.$old_lock_name => $newname) {
56     &{$self->{-error_handler}} ($self, level => 'detaillog',
57     msg => qq(_lock: $self->{-directory}$old_lock_name => $newname: locking successd));
58     return 1;
59     } else {
60     &{$self->{-error_handler}} ($self, level => 'log',
61     msg => qq(_lock: $self->{-directory}$old_lock_name => $newname: $!));
62     return 0;
63     }
64     }
65    
66     sub _unlock ($$$) {
67     my ($self, $old_lock_name, $status) = @_;
68    
69     ## Share lock
70     if ($self->{-share}) {
71     if ($status->{seq} && $status->{time}) {
72     ## I'm the last lock owner
73     if ($status->{seq} == 1) {
74     $status->{seq} = 0;
75     $status->{time} = 0;
76     $status->{creation} = 0;
77     } else {
78     $status->{seq}--;
79     #$status->{time} unchanged
80     }
81     } else {
82     return 0;
83     }
84     ## Exclusive lock
85     } else {
86     if ($status->{seq} == 0 && $status->{time}) {
87     $status->{time} = 0;
88     $status->{creation} = 0;
89     } else {
90     return 0;
91     }
92     }
93    
94     my $newname = $self->{-directory}.$self->_make_lock_file_name
95     ($status);
96     if (rename $self->{-directory}.$old_lock_name => $newname) {
97     &{$self->{-error_handler}} ($self, level => 'detaillog',
98     msg => qq(_unlock: $self->{-directory}$old_lock_name => $newname: unlocking successed));
99     return 1;
100     } else {
101     &{$self->{-error_handler}} ($self, level => 'log',
102     msg => qq(_unlock: $self->{-directory}$old_lock_name => $newname: $!));
103     return 0;
104     }
105     }
106    
107     ## -directory => path Lock files' directory
108     ## -name => 1*filename-char Base name of lock file
109     ## -share => 0/1
110     ## - 1: Share lock
111     ## - 0: Exclusive lock (default)
112     ## -retry => -1/0/1..
113     ## - -1: No retry
114     ## - 0: Infinite times of retries
115     ## - n (n > 0): n times of retries (default 8)
116     ## -timeout => n (second) (default 20)
117     ## -error_handler => CODE : Error handler (default: die)
118     ##
119     sub new ($%) {
120     my $class = shift;
121     my $self = bless {@_}, $class;
122     $self->{-retry} = 8 unless defined $self->{-retry};
123     $self->{-timeout} ||= 20;
124     $self->{-error_handler} ||= sub {
125     my (undef, %o) = @_;
126     if ($o{level} eq 'fatal') {
127     die sprintf '%d: %s', scalar time, $o{msg};
128     } else {
129     warn sprintf '%d: %s', scalar time, $o{msg};
130     }
131     };
132     $self->{-directory} .= '/'
133     unless substr ($self->{-directory}, -1) eq '/';
134     return $self;
135     }
136    
137     sub lock ($) {
138     my $self = shift;
139     return if $self->{locked};
140    
141     ## Get lock file
142     my ($lockfile, $status) = $self->_make_new_lock ();
143    
144     ## Try locking
145     if ($self->_lock ($lockfile, $status)) {
146     ## Success
147     $self->{creation} = $status->{creation};
148     $self->{locked} = 1;
149     return 1;
150     } else { ## Failure
151     return 0 if $self->{-retry} == -1; # No retry
152    
153     ## How many times retry?
154     my $trytime = $self->{-retry} || $self->{-timeout};
155     $trytime = $self->{-timeout} if $self->{-retry} > $trytime;
156    
157     for (my $try = 0; $try < $trytime; $try++) {
158     my ($lockfile, $status) = $self->_make_new_lock ();
159     my $duration = time - $status->{time};
160    
161     ## Expired lock
162     if ($self->{-timeout} < $duration) {
163     &{$self->{-error_handler}} ($self, level => 'log',
164     msg => qq(lock: $lockfile: lockfile already expired));
165    
166     if ($self->_lock ($lockfile, $status, force => 1)) {
167     $self->{creation} = $status->{creation};
168     $self->{locked} = 1;
169     return 1;
170     } else {
171     &{$self->{-error_handler}} ($self, level => 'warn',
172     msg => qq(lock: force lock failed));
173     # -> retry
174     }
175     ## Locked
176     } else {
177     if ($self->_lock ($lockfile, $status)) {
178     $self->{creation} = $status->{creation};
179     $self->{locked} = 1;
180     return 1;
181     } else {
182     # -> retry
183     }
184     }
185    
186     ## Wait
187     &{$self->{-error_handler}} ($self, level => 'log',
188     msg => qq(lock: retry ($try/$trytime) failed));
189     sleep 1; # 1 second
190     } # for
191     }
192    
193     return 0;
194     }
195    
196     sub unlock ($) {
197     my $self = shift;
198     return unless $self->{locked};
199    
200     ## Get lock file
201     my ($lockfile, $status) = $self->_make_new_lock ();
202    
203     ## Current lock file is not same as what was used to lock
204     if ($self->{creation} != $status->{creation}) {
205     &{$self->{-error_handler}} ($self, level => 'log',
206     msg => qq(unlock: $lockfile: original lock ($self->{creation}) was lost));
207     $self->{creation} = undef;
208     $self->{locked} = 0;
209     return -1;
210     }
211    
212     ## Try unlocking
213     if ($self->_unlock ($lockfile, $status)) {
214     ## Success
215     $self->{creation} = undef;
216     $self->{locked} = 0;
217     return 1;
218     } else { ## Failure
219     if ($self->{-retry} == -1) { # No retry
220     $self->{creation} = undef;
221     return 0;
222     }
223    
224     ## How many times retry?
225     my $trytime = $self->{-retry} || $self->{-timeout};
226     $trytime = $self->{-timeout} if $self->{-retry} > $trytime;
227    
228     for (my $try = 0; $try < $trytime; $try++) {
229     my ($lockfile, $status) = $self->_make_new_lock ();
230    
231     ## Current lock file is not same as what was used to lock
232     if ($self->{creation} != $status->{creation}) {
233     &{$self->{-error_handler}} ($self, level => 'log',
234     msg => qq(unlock: $lockfile: original lock ($self->{creation}) was lost));
235     $self->{creation} = undef;
236     $self->{locked} = 0;
237     return -1;
238     } elsif ($self->_unlock ($lockfile, $status)) {
239     $self->{creation} = undef;
240     $self->{locked} = 0;
241     return 1;
242     }
243    
244     ## Wait
245     &{$self->{-error_handler}} ($self, level => 'log',
246     msg => qq(unlock: retry ($try/$trytime) failed));
247     #sleep 1; # 1 second
248     } # for
249     }
250    
251     $self->{creation} = undef;
252     return 0;
253     }
254    
255     sub DESTROY ($) {
256     my $self = shift;
257     $self->unlock if $self->{locked};
258     &{$self->{-error_handler}} ($self, level => 'log', msg => 'DESTROY');
259     }
260    
261     ## Makes new lock file and returns its name.
262     ## If already exists, returns its name.
263     sub _make_new_lock ($) {
264     my $self = shift;
265     my @lockfile;
266     opendir LOCKDIR, $self->{-directory}
267     or &{$self->{-error_handler}}
268     ($self, msg => qq(_make_new_lock: $self->{-directory}: $!),
269     level => 'fatal'); {
270     my $pfx_length = length $self->{-name};
271     @lockfile = grep {substr ($_, 0, $pfx_length)
272     eq $self->{-name}}
273     readdir LOCKDIR;
274     } closedir LOCKDIR;
275    
276     my $lockfile;
277     my $status;
278     for (@lockfile) {
279     $status = $self->_match_lock_file_name
280     ($_, prefix => $self->{-name});
281     if ($status) {
282     $lockfile = $_;
283     last;
284     }
285     }
286     ## Lock file not exist -- Make new lock file
287     unless ($lockfile) {
288     $status = {prefix => $self->{-name}, seq => 0, time => 0,
289     creation => 0};
290     $lockfile = $self->_make_lock_file_name ($status);
291     open LOCKFILE, '>', $self->{-directory}.$lockfile
292     or &{$self->{-error_handler}}
293     ($self, msg => qq(_make_new_lock: $self->{-directory}.$lockfile: $!),
294     level => 'fatal');
295     print LOCKFILE scalar localtime; # dummy data
296     close LOCKFILE;
297     }
298     return ($lockfile, $status);
299     }
300    
301     =head1 LICENSE
302    
303     Copyright 2003 Wakaba <w@suika.fam.cx>
304    
305     This program is free software; you can redistribute it and/or
306     modify it under the same terms as Perl itself.
307    
308     =cut
309    
310 wakaba 1.2 1; # $Date: 2003/07/28 08:49:13 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24