/[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 - (show annotations) (download)
Tue Jul 29 09:16:06 2003 UTC (23 years, 1 month ago) by wakaba
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +2 -2 lines
FILE REMOVED
*** empty log message ***

1
2 package Yuki::YukiWikiDB_Lock;
3 ($VERSION) = q($Revision: 1.1 $) =~ m/\x20([\d.]+)\x20/;
4 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 <[email protected]>
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 1; # $Date: 2003/07/28 08:49:13 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24