lockfile 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/rlock +125 -109
- data/bin/rlock-1.3.0 +342 -0
- data/lib/lockfile-1.3.0.rb +518 -0
- data/lib/lockfile.rb +286 -205
- metadata +16 -33
- data/BUGS +0 -3
- data/README +0 -165
- data/VERSION +0 -1
- data/doc/rlock.help +0 -88
- data/install.rb +0 -143
- data/lib/lockfile-1.1.0.rb +0 -437
- data/lockfile-1.1.0.gem +0 -0
- data/lockfile.gemspec +0 -22
- data/samples/a.rb +0 -112
- data/samples/nfsstore.rb +0 -203
- data/samples/test.db +0 -0
- data/samples/test.db~ +0 -0
data/lib/lockfile.rb
CHANGED
@@ -4,8 +4,8 @@ unless defined? $__lockfile__
|
|
4
4
|
require 'fileutils'
|
5
5
|
|
6
6
|
class Lockfile
|
7
|
-
|
8
|
-
VERSION = '1.
|
7
|
+
#--{{{
|
8
|
+
VERSION = '1.3.0'
|
9
9
|
|
10
10
|
class LockError < StandardError; end
|
11
11
|
class StolenLockError < LockError; end
|
@@ -17,13 +17,13 @@ unless defined? $__lockfile__
|
|
17
17
|
class UnLockError < LockError; end
|
18
18
|
|
19
19
|
class SleepCycle < Array
|
20
|
-
|
20
|
+
#--{{{
|
21
21
|
attr :min
|
22
22
|
attr :max
|
23
23
|
attr :range
|
24
24
|
attr :inc
|
25
25
|
def initialize min, max, inc
|
26
|
-
|
26
|
+
#--{{{
|
27
27
|
@min, @max, @inc = Float(min), Float(max), Float(inc)
|
28
28
|
@range = @max - @min
|
29
29
|
raise RangeError, "max < min" if @max < @min
|
@@ -32,41 +32,42 @@ unless defined? $__lockfile__
|
|
32
32
|
push(s) and s += @inc while(s <= @max)
|
33
33
|
self[-1] = @max if self[-1] < @max
|
34
34
|
reset
|
35
|
-
|
35
|
+
#--}}}
|
36
36
|
end
|
37
37
|
def next
|
38
|
-
|
38
|
+
#--{{{
|
39
39
|
ret = self[@idx]
|
40
40
|
@idx = ((@idx + 1) % self.size)
|
41
41
|
ret
|
42
|
-
|
42
|
+
#--}}}
|
43
43
|
end
|
44
44
|
def reset
|
45
|
-
|
45
|
+
#--{{{
|
46
46
|
@idx = 0
|
47
|
-
|
47
|
+
#--}}}
|
48
48
|
end
|
49
|
-
|
49
|
+
#--}}}
|
50
50
|
end
|
51
51
|
|
52
52
|
HOSTNAME = Socket::gethostname
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
54
|
+
DEFAULT_RETRIES = nil # maximum number of attempts
|
55
|
+
DEFAULT_TIMEOUT = nil # the longest we will try
|
56
|
+
DEFAULT_MAX_AGE = 1024 # lockfiles older than this are stale
|
57
|
+
DEFAULT_SLEEP_INC = 2 # sleep cycle is this much longer each time
|
58
|
+
DEFAULT_MIN_SLEEP = 2 # shortest sleep time
|
59
|
+
DEFAULT_MAX_SLEEP = 32 # longest sleep time
|
60
|
+
DEFAULT_SUSPEND = 64 # iff we steal a lock wait this long before we go on
|
61
|
+
DEFAULT_REFRESH = 8 # how often we touch/validate the lock
|
62
|
+
DEFAULT_DONT_CLEAN = false # iff we leave lock files lying around
|
63
|
+
DEFAULT_POLL_RETRIES = 16 # this many polls makes one 'try'
|
64
|
+
DEFAULT_POLL_MAX_SLEEP = 0.08 # the longest we'll sleep between polls
|
65
|
+
DEFAULT_DONT_SWEEP = false # if we cleanup after other process on our host
|
65
66
|
|
66
|
-
|
67
|
+
DEFAULT_DEBUG = ENV['LOCKFILE_DEBUG'] || false
|
67
68
|
|
68
69
|
class << self
|
69
|
-
|
70
|
+
#--{{{
|
70
71
|
attr :retries, true
|
71
72
|
attr :max_age, true
|
72
73
|
attr :sleep_inc, true
|
@@ -79,26 +80,30 @@ unless defined? $__lockfile__
|
|
79
80
|
attr :dont_clean, true
|
80
81
|
attr :poll_retries, true
|
81
82
|
attr :poll_max_sleep, true
|
83
|
+
attr :dont_sweep, true
|
84
|
+
|
82
85
|
def init
|
83
|
-
|
84
|
-
@retries
|
85
|
-
@max_age
|
86
|
-
@sleep_inc
|
87
|
-
@min_sleep
|
88
|
-
@max_sleep
|
89
|
-
@suspend
|
90
|
-
@timeout
|
91
|
-
@refresh
|
92
|
-
@
|
93
|
-
@
|
94
|
-
@
|
95
|
-
@
|
86
|
+
#--{{{
|
87
|
+
@retries = DEFAULT_RETRIES
|
88
|
+
@max_age = DEFAULT_MAX_AGE
|
89
|
+
@sleep_inc = DEFAULT_SLEEP_INC
|
90
|
+
@min_sleep = DEFAULT_MIN_SLEEP
|
91
|
+
@max_sleep = DEFAULT_MAX_SLEEP
|
92
|
+
@suspend = DEFAULT_SUSPEND
|
93
|
+
@timeout = DEFAULT_TIMEOUT
|
94
|
+
@refresh = DEFAULT_REFRESH
|
95
|
+
@dont_clean = DEFAULT_DONT_CLEAN
|
96
|
+
@poll_retries = DEFAULT_POLL_RETRIES
|
97
|
+
@poll_max_sleep = DEFAULT_POLL_MAX_SLEEP
|
98
|
+
@dont_sweep = DEFAULT_DONT_SWEEP
|
99
|
+
|
100
|
+
@debug = DEFAULT_DEBUG
|
96
101
|
|
97
102
|
STDOUT.sync = true if @debug
|
98
103
|
STDERR.sync = true if @debug
|
99
|
-
|
104
|
+
#--}}}
|
100
105
|
end
|
101
|
-
|
106
|
+
#--}}}
|
102
107
|
end
|
103
108
|
self.init
|
104
109
|
|
@@ -110,173 +115,230 @@ unless defined? $__lockfile__
|
|
110
115
|
attr :dirname
|
111
116
|
attr :basename
|
112
117
|
attr :clean
|
113
|
-
attr :retries
|
114
|
-
attr :max_age
|
115
|
-
attr :sleep_inc
|
116
|
-
attr :min_sleep
|
117
|
-
attr :max_sleep
|
118
|
-
attr :suspend
|
119
|
-
attr :refresh
|
120
|
-
attr :timeout
|
118
|
+
attr :retries
|
119
|
+
attr :max_age
|
120
|
+
attr :sleep_inc
|
121
|
+
attr :min_sleep
|
122
|
+
attr :max_sleep
|
123
|
+
attr :suspend
|
124
|
+
attr :refresh
|
125
|
+
attr :timeout
|
126
|
+
attr :dont_clean
|
127
|
+
attr :poll_retries
|
128
|
+
attr :poll_max_sleep
|
129
|
+
attr :dont_sweep
|
130
|
+
|
121
131
|
attr :debug, true
|
122
|
-
attr :dont_clean, true
|
123
|
-
attr :poll_retries, true
|
124
|
-
attr :poll_max_sleep, true
|
125
132
|
|
126
133
|
alias thief? thief
|
127
134
|
alias locked? locked
|
128
135
|
alias debug? debug
|
129
136
|
|
130
137
|
def initialize(path, opts = {}, &block)
|
131
|
-
|
138
|
+
#--{{{
|
132
139
|
@klass = self.class
|
133
|
-
@path
|
134
|
-
@opts
|
140
|
+
@path = path
|
141
|
+
@opts = opts
|
135
142
|
|
136
|
-
@retries
|
137
|
-
@max_age
|
138
|
-
@sleep_inc
|
139
|
-
@min_sleep
|
140
|
-
@max_sleep
|
141
|
-
@suspend
|
142
|
-
@timeout
|
143
|
-
@refresh
|
144
|
-
@
|
145
|
-
@
|
146
|
-
@poll_retries = getopt('poll_retries') || @klass.poll_retries
|
143
|
+
@retries = getopt('retries') || @klass.retries
|
144
|
+
@max_age = getopt('max_age') || @klass.max_age
|
145
|
+
@sleep_inc = getopt('sleep_inc') || @klass.sleep_inc
|
146
|
+
@min_sleep = getopt('min_sleep') || @klass.min_sleep
|
147
|
+
@max_sleep = getopt('max_sleep') || @klass.max_sleep
|
148
|
+
@suspend = getopt('suspend') || @klass.suspend
|
149
|
+
@timeout = getopt('timeout') || @klass.timeout
|
150
|
+
@refresh = getopt('refresh') || @klass.refresh
|
151
|
+
@dont_clean = getopt('dont_clean') || @klass.dont_clean
|
152
|
+
@poll_retries = getopt('poll_retries') || @klass.poll_retries
|
147
153
|
@poll_max_sleep = getopt('poll_max_sleep') || @klass.poll_max_sleep
|
154
|
+
@dont_sweep = getopt('dont_sweep') || @klass.dont_sweep
|
155
|
+
|
156
|
+
@debug = getopt('debug') || @klass.debug
|
148
157
|
|
149
|
-
@sleep_cycle = SleepCycle
|
158
|
+
@sleep_cycle = SleepCycle::new @min_sleep, @max_sleep, @sleep_inc
|
150
159
|
|
151
|
-
@clean
|
152
|
-
@dirname
|
153
|
-
@basename = File
|
154
|
-
@thief
|
155
|
-
@locked
|
160
|
+
@clean = @dont_clean ? nil : lambda{ File::unlink @path rescue nil }
|
161
|
+
@dirname = File::dirname @path
|
162
|
+
@basename = File::basename @path
|
163
|
+
@thief = false
|
164
|
+
@locked = false
|
156
165
|
|
157
166
|
lock(&block) if block
|
158
|
-
|
167
|
+
#--}}}
|
159
168
|
end
|
160
169
|
def lock
|
161
|
-
|
170
|
+
#--{{{
|
162
171
|
raise StackingLockError, "<#{ @path }> is locked!" if @locked
|
163
172
|
|
164
|
-
|
173
|
+
sweep unless @dont_sweep
|
165
174
|
|
166
|
-
|
167
|
-
@sleep_cycle.reset
|
168
|
-
create_tmplock do |f|
|
169
|
-
begin
|
170
|
-
Timeout::timeout(@timeout) do
|
171
|
-
tmp_path = f.path
|
172
|
-
tmp_stat = f.lstat
|
173
|
-
n_retries = 0
|
174
|
-
#sleeptime = @sleep_inc
|
175
|
+
ret = nil
|
175
176
|
|
176
|
-
|
177
|
-
|
178
|
-
|
177
|
+
attempt do
|
178
|
+
begin
|
179
|
+
@sleep_cycle.reset
|
180
|
+
create_tmplock do |f|
|
181
|
+
begin
|
182
|
+
Timeout::timeout(@timeout) do
|
183
|
+
tmp_path = f.path
|
184
|
+
tmp_stat = f.lstat
|
185
|
+
n_retries = 0
|
186
|
+
trace{ "attempting to lock <#{ @path }>..." }
|
179
187
|
begin
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
raise StatLockError, "stat's do not agree" unless
|
184
|
-
tmp_stat.rdev == lock_stat.rdev and tmp_stat.ino == lock_stat.ino
|
185
|
-
trace{ "aquired lock <#{ @path }>" }
|
186
|
-
@locked = true
|
187
|
-
rescue => e
|
188
|
-
i += 1
|
189
|
-
unless i >= @poll_retries
|
190
|
-
t = [rand(@poll_max_sleep), @poll_max_sleep].min
|
191
|
-
trace{ "poll sleep <#{ t }>..." }
|
192
|
-
sleep t
|
193
|
-
retry
|
194
|
-
end
|
195
|
-
raise
|
196
|
-
end
|
197
|
-
|
198
|
-
rescue => e
|
199
|
-
n_retries += 1
|
200
|
-
trace{ "n_retries <#{ n_retries }>" }
|
201
|
-
raise MaxTriesLockError, "surpased retries <#{ @retries }>" if
|
202
|
-
@retries and n_retries >= @retries
|
203
|
-
|
204
|
-
valid = validlock?
|
205
|
-
|
206
|
-
case valid
|
207
|
-
when true
|
208
|
-
trace{ "found valid lock" }
|
209
|
-
sleeptime = @sleep_cycle.next
|
210
|
-
trace{ "sleep <#{ sleeptime }>..." }
|
211
|
-
sleep sleeptime
|
212
|
-
when false
|
213
|
-
trace{ "found invalid lock and removing" }
|
188
|
+
i = 0
|
189
|
+
begin
|
190
|
+
trace{ "polling attempt <#{ i }>..." }
|
214
191
|
begin
|
215
|
-
File
|
216
|
-
@thief = true
|
217
|
-
warn "<#{ @path }> stolen by <#{ Process.pid }> at <#{ timestamp }>"
|
218
|
-
trace{ "i am a thief!" }
|
219
|
-
trace{ "suspending <#{ @suspend }>" }
|
220
|
-
sleep @suspend
|
192
|
+
File::link tmp_path, @path
|
221
193
|
rescue Errno::ENOENT
|
194
|
+
try_again!
|
222
195
|
end
|
223
|
-
|
224
|
-
|
196
|
+
lock_stat = File::lstat @path
|
197
|
+
raise StatLockError, "stat's do not agree" unless
|
198
|
+
tmp_stat.rdev == lock_stat.rdev and tmp_stat.ino == lock_stat.ino
|
199
|
+
trace{ "aquired lock <#{ @path }>" }
|
200
|
+
@locked = true
|
201
|
+
rescue => e
|
202
|
+
i += 1
|
203
|
+
unless i >= @poll_retries
|
204
|
+
t = [rand(@poll_max_sleep), @poll_max_sleep].min
|
205
|
+
trace{ "poll sleep <#{ t }>..." }
|
206
|
+
sleep t
|
207
|
+
retry
|
208
|
+
end
|
209
|
+
raise
|
225
210
|
end
|
226
211
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
212
|
+
rescue => e
|
213
|
+
n_retries += 1
|
214
|
+
trace{ "n_retries <#{ n_retries }>" }
|
215
|
+
case validlock?
|
216
|
+
when true
|
217
|
+
raise MaxTriesLockError, "surpased retries <#{ @retries }>" if
|
218
|
+
@retries and n_retries >= @retries
|
219
|
+
trace{ "found valid lock" }
|
220
|
+
sleeptime = @sleep_cycle.next
|
221
|
+
trace{ "sleep <#{ sleeptime }>..." }
|
222
|
+
sleep sleeptime
|
223
|
+
when false
|
224
|
+
trace{ "found invalid lock and removing" }
|
225
|
+
begin
|
226
|
+
File::unlink @path
|
227
|
+
@thief = true
|
228
|
+
warn "<#{ @path }> stolen by <#{ Process.pid }> at <#{ timestamp }>"
|
229
|
+
trace{ "i am a thief!" }
|
230
|
+
rescue Errno::ENOENT
|
231
|
+
end
|
232
|
+
trace{ "suspending <#{ @suspend }>" }
|
233
|
+
sleep @suspend
|
234
|
+
when nil
|
235
|
+
raise MaxTriesLockError, "surpased retries <#{ @retries }>" if
|
236
|
+
@retries and n_retries >= @retries
|
237
|
+
end
|
238
|
+
retry
|
239
|
+
end # begin
|
240
|
+
end # timeout
|
241
|
+
rescue Timeout::Error
|
242
|
+
raise TimeoutLockError, "surpassed timeout <#{ @timeout }>"
|
243
|
+
end # begin
|
244
|
+
end # create_tmplock
|
234
245
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
begin
|
239
|
-
begin
|
240
|
-
ret = yield @path
|
241
|
-
rescue StolenLockError
|
242
|
-
stolen = true
|
243
|
-
raise
|
244
|
-
end
|
245
|
-
ensure
|
246
|
+
if block_given?
|
247
|
+
stolen = false
|
248
|
+
refresher = (@refresh ? new_refresher : nil)
|
246
249
|
begin
|
247
|
-
|
250
|
+
begin
|
251
|
+
ret = yield @path
|
252
|
+
rescue StolenLockError
|
253
|
+
stolen = true
|
254
|
+
raise
|
255
|
+
end
|
248
256
|
ensure
|
249
|
-
|
257
|
+
begin
|
258
|
+
refresher.kill if refresher and refresher.status
|
259
|
+
ensure
|
260
|
+
unlock unless stolen
|
261
|
+
end
|
250
262
|
end
|
263
|
+
else
|
264
|
+
ObjectSpace.define_finalizer self, @clean if @clean
|
265
|
+
ret = self
|
251
266
|
end
|
252
|
-
|
253
|
-
|
254
|
-
ret = self
|
267
|
+
rescue Errno::ESTALE, Errno::EIO => e
|
268
|
+
raise(NFSLockError, errmsg(e))
|
255
269
|
end
|
256
|
-
rescue Errno::ESTALE, Errno::EIO => e
|
257
|
-
raise(NFSLockError, errmsg(e))
|
258
270
|
end
|
259
271
|
|
260
272
|
return ret
|
261
|
-
|
273
|
+
#--}}}
|
274
|
+
end
|
275
|
+
def sweep
|
276
|
+
#----{{{
|
277
|
+
begin
|
278
|
+
glob = File::join(@dirname, ".*lck")
|
279
|
+
paths = Dir[glob]
|
280
|
+
paths.each do |path|
|
281
|
+
begin
|
282
|
+
basename = File::basename path
|
283
|
+
pat = %r/^\s*\.([^_]+)_([^_]+)/o
|
284
|
+
if pat.match(basename)
|
285
|
+
host, pid = $1, $2
|
286
|
+
else
|
287
|
+
next
|
288
|
+
end
|
289
|
+
host.gsub!(%r/^\.+|\.+$/,'')
|
290
|
+
quad = host.split %r/\./
|
291
|
+
host = quad.first
|
292
|
+
pat = %r/^\s*#{ host }/i
|
293
|
+
if pat.match(HOSTNAME) and %r/^\s*\d+\s*$/.match(pid)
|
294
|
+
unless alive?(pid)
|
295
|
+
trace{ "process <#{ pid }> on <#{ host }> is no longer alive" }
|
296
|
+
trace{ "sweeping <#{ path }>" }
|
297
|
+
FileUtils::rm_f path
|
298
|
+
else
|
299
|
+
trace{ "process <#{ pid }> on <#{ host }> is still alive" }
|
300
|
+
trace{ "ignoring <#{ path }>" }
|
301
|
+
end
|
302
|
+
else
|
303
|
+
trace{ "ignoring <#{ path }> generated by <#{ host }>" }
|
304
|
+
end
|
305
|
+
rescue
|
306
|
+
next
|
307
|
+
end
|
308
|
+
end
|
309
|
+
rescue => e
|
310
|
+
warn(errmsg(e))
|
311
|
+
end
|
312
|
+
#----}}}
|
313
|
+
end
|
314
|
+
def alive? pid
|
315
|
+
#----{{{
|
316
|
+
pid = Integer("#{ pid }")
|
317
|
+
begin
|
318
|
+
Process::kill 0, pid
|
319
|
+
true
|
320
|
+
rescue Errno::ESRCH
|
321
|
+
false
|
322
|
+
end
|
323
|
+
#----}}}
|
262
324
|
end
|
263
325
|
def unlock
|
264
|
-
|
326
|
+
#--{{{
|
265
327
|
raise UnLockError, "<#{ @path }> is not locked!" unless @locked
|
266
328
|
begin
|
267
|
-
File
|
268
|
-
@locked = false
|
269
|
-
ObjectSpace.undefine_finalizer self if @clean
|
329
|
+
File::unlink @path
|
270
330
|
rescue Errno::ENOENT
|
331
|
+
raise StolenLockError, @path
|
332
|
+
ensure
|
333
|
+
@thief = false
|
271
334
|
@locked = false
|
272
335
|
ObjectSpace.undefine_finalizer self if @clean
|
273
|
-
raise StolenLockError, @path
|
274
336
|
end
|
275
|
-
|
337
|
+
#--}}}
|
276
338
|
end
|
277
339
|
def new_refresher
|
278
|
-
|
279
|
-
Thread
|
340
|
+
#--{{{
|
341
|
+
Thread::new(Thread::current, @path, @refresh) do |thread, path, refresh|
|
280
342
|
loop do
|
281
343
|
touch path
|
282
344
|
trace{"touched <#{ path }> @ <#{ Time.now.to_f }>"}
|
@@ -287,49 +349,49 @@ unless defined? $__lockfile__
|
|
287
349
|
rescue => e
|
288
350
|
trace{errmsg e}
|
289
351
|
thread.raise StolenLockError
|
290
|
-
Thread
|
352
|
+
Thread::exit
|
291
353
|
end
|
292
354
|
sleep refresh
|
293
355
|
end
|
294
356
|
end
|
295
|
-
|
357
|
+
#--}}}
|
296
358
|
end
|
297
359
|
def validlock?
|
298
|
-
|
360
|
+
#--{{{
|
299
361
|
if @max_age
|
300
362
|
uncache @path rescue nil
|
301
363
|
begin
|
302
|
-
return((Time.now - File
|
364
|
+
return((Time.now - File::stat(@path).mtime) < @max_age)
|
303
365
|
rescue Errno::ENOENT
|
304
366
|
return nil
|
305
367
|
end
|
306
368
|
else
|
307
|
-
exist = File
|
369
|
+
exist = File::exist?(@path)
|
308
370
|
return(exist ? true : nil)
|
309
371
|
end
|
310
|
-
|
372
|
+
#--}}}
|
311
373
|
end
|
312
374
|
def uncache file
|
313
|
-
|
375
|
+
#--{{{
|
314
376
|
refresh = nil
|
315
377
|
begin
|
316
378
|
is_a_file = File === file
|
317
379
|
path = (is_a_file ? file.path : file.to_s)
|
318
|
-
stat = (is_a_file ? file.stat : File
|
319
|
-
refresh = tmpnam(File
|
320
|
-
File
|
321
|
-
File
|
322
|
-
File
|
380
|
+
stat = (is_a_file ? file.stat : File::stat(file.to_s))
|
381
|
+
refresh = tmpnam(File::dirname(path))
|
382
|
+
File::link path, refresh
|
383
|
+
File::chmod stat.mode, path
|
384
|
+
File::utime stat.atime, stat.mtime, path
|
323
385
|
ensure
|
324
386
|
begin
|
325
|
-
File
|
387
|
+
File::unlink refresh if refresh
|
326
388
|
rescue Errno::ENOENT
|
327
389
|
end
|
328
390
|
end
|
329
|
-
|
391
|
+
#--}}}
|
330
392
|
end
|
331
393
|
def create_tmplock
|
332
|
-
|
394
|
+
#--{{{
|
333
395
|
tmplock = tmpnam @dirname
|
334
396
|
begin
|
335
397
|
create(tmplock) do |f|
|
@@ -341,36 +403,36 @@ unless defined? $__lockfile__
|
|
341
403
|
yield f
|
342
404
|
end
|
343
405
|
ensure
|
344
|
-
begin; File
|
406
|
+
begin; File::unlink tmplock; rescue Errno::ENOENT; end if tmplock
|
345
407
|
end
|
346
|
-
|
408
|
+
#--}}}
|
347
409
|
end
|
348
410
|
def gen_lock_id
|
349
|
-
|
411
|
+
#--{{{
|
350
412
|
Hash[
|
351
413
|
'host' => "#{ HOSTNAME }",
|
352
414
|
'pid' => "#{ Process.pid }",
|
353
415
|
'ppid' => "#{ Process.ppid }",
|
354
416
|
'time' => timestamp,
|
355
417
|
]
|
356
|
-
|
418
|
+
#--}}}
|
357
419
|
end
|
358
420
|
def timestamp
|
359
|
-
|
421
|
+
#--{{{
|
360
422
|
time = Time.now
|
361
423
|
usec = time.usec.to_s
|
362
424
|
usec << '0' while usec.size < 6
|
363
425
|
"#{ time.strftime('%Y-%m-%d %H:%M:%S') }.#{ usec }"
|
364
|
-
|
426
|
+
#--}}}
|
365
427
|
end
|
366
428
|
def dump_lock_id lock_id = @lock_id
|
367
|
-
|
429
|
+
#--{{{
|
368
430
|
"host: %s\npid: %s\nppid: %s\ntime: %s\n" %
|
369
431
|
lock_id.values_at('host','pid','ppid','time')
|
370
|
-
|
432
|
+
#--}}}
|
371
433
|
end
|
372
434
|
def load_lock_id buf
|
373
|
-
|
435
|
+
#--{{{
|
374
436
|
lock_id = {}
|
375
437
|
kv = %r/([^:]+):(.*)/o
|
376
438
|
buf.each do |line|
|
@@ -380,58 +442,77 @@ unless defined? $__lockfile__
|
|
380
442
|
lock_id[k.strip] = v.strip
|
381
443
|
end
|
382
444
|
lock_id
|
383
|
-
|
445
|
+
#--}}}
|
384
446
|
end
|
385
|
-
def tmpnam dir, seed = File
|
386
|
-
|
447
|
+
def tmpnam dir, seed = File::basename($0)
|
448
|
+
#--{{{
|
387
449
|
pid = Process.pid
|
388
450
|
time = Time.now
|
389
451
|
sec = time.to_i
|
390
452
|
usec = time.usec
|
391
|
-
"%s%s.%s_%d_%s_%d_%d_%d" %
|
453
|
+
"%s%s.%s_%d_%s_%d_%d_%d.lck" %
|
392
454
|
[dir, File::SEPARATOR, HOSTNAME, pid, seed, sec, usec, rand(sec)]
|
393
|
-
|
455
|
+
#--}}}
|
394
456
|
end
|
395
457
|
def create path
|
396
|
-
|
458
|
+
#--{{{
|
397
459
|
umask = nil
|
398
460
|
f = nil
|
399
461
|
begin
|
400
|
-
umask = File
|
462
|
+
umask = File::umask 022
|
401
463
|
f = open path, File::WRONLY|File::CREAT|File::EXCL, 0644
|
402
464
|
ensure
|
403
|
-
File
|
465
|
+
File::umask umask if umask
|
404
466
|
end
|
405
467
|
return(block_given? ? begin; yield f; ensure; f.close; end : f)
|
406
|
-
|
468
|
+
#--}}}
|
407
469
|
end
|
408
470
|
def touch path
|
409
|
-
|
471
|
+
#--{{{
|
410
472
|
FileUtils.touch path
|
411
|
-
|
473
|
+
#--}}}
|
412
474
|
end
|
413
475
|
def getopt key
|
414
|
-
|
476
|
+
#--{{{
|
415
477
|
@opts[key] || @opts[key.to_s] || @opts[key.to_s.intern]
|
416
|
-
|
478
|
+
#--}}}
|
417
479
|
end
|
418
480
|
def to_str
|
419
|
-
|
481
|
+
#--{{{
|
420
482
|
@path
|
421
|
-
|
483
|
+
#--}}}
|
422
484
|
end
|
423
485
|
alias to_s to_str
|
424
486
|
def trace s = nil
|
425
|
-
|
487
|
+
#--{{{
|
426
488
|
STDERR.puts((s ? s : yield)) if @debug
|
427
|
-
|
489
|
+
#--}}}
|
428
490
|
end
|
429
491
|
def errmsg e
|
430
|
-
|
431
|
-
"%s
|
432
|
-
|
492
|
+
#--{{{
|
493
|
+
"%s (%s)\n%s\n" % [e.class, e.message, e.backtrace.join("\n")]
|
494
|
+
#--}}}
|
495
|
+
end
|
496
|
+
def attempt
|
497
|
+
#----{{{
|
498
|
+
ret = nil
|
499
|
+
loop{ break unless catch('attempt'){ ret = yield } == 'try_again' }
|
500
|
+
ret
|
501
|
+
#----}}}
|
433
502
|
end
|
434
|
-
|
503
|
+
def try_again!
|
504
|
+
#----{{{
|
505
|
+
throw 'attempt', 'try_again'
|
506
|
+
#----}}}
|
507
|
+
end
|
508
|
+
alias again! try_again!
|
509
|
+
def give_up!
|
510
|
+
#----{{{
|
511
|
+
throw 'attempt', 'give_up'
|
512
|
+
#----}}}
|
513
|
+
end
|
514
|
+
#--}}}
|
435
515
|
end
|
516
|
+
|
436
517
|
$__lockfile__ == __FILE__
|
437
518
|
end
|