bio-gemma-wrapper 0.99.2 → 0.99.6
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.
- checksums.yaml +4 -4
- data/README.md +26 -10
- data/VERSION +1 -1
- data/bin/gemma-wrapper +116 -47
- data/gemma-wrapper.gemspec +1 -0
- data/lib/lock.rb +95 -0
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69d74ac5f1a705132d7ddc86bd1182c11fc37cf32062dbb28909f16684a827cb
|
4
|
+
data.tar.gz: 6f912b3c03474c1334a105d6c9471c02d39c205e94b0d07e5281718beec65ee7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35fbdf4ccfc482f6898e35b1e46e840db87f34efb35a096206ffef4a131ac8b21a4e6b7893be6ee053a59837eadba6547258d2360d796794c71a60670458943f
|
7
|
+
data.tar.gz: 43f7a7438f475583930e6cf1f50d34206fba7674b3df836791cf7064bd7ca55dddc2cc101f7c5a89d8042645b6dd06245b84d3b38501bc1eab7d18774703a01a
|
data/README.md
CHANGED
@@ -8,11 +8,12 @@ Nat. Genet., 2016)](cfw.gif)
|
|
8
8
|
## Introduction
|
9
9
|
|
10
10
|
Gemma-wrapper allows running GEMMA with LOCO, GEMMA with caching,
|
11
|
-
GEMMA in parallel (now the default), and GEMMA on
|
12
|
-
is used to run GEMMA as part of the
|
13
|
-
environment.
|
11
|
+
GEMMA in parallel (now the default with LOCO), and GEMMA on
|
12
|
+
PBS. Gemma-wrapper is used to run GEMMA as part of the
|
13
|
+
https://genenetwork.org/ environment.
|
14
14
|
|
15
|
-
Note that gemma-wrapper is projected to be integrated
|
15
|
+
Note that a version of gemma-wrapper is projected to be integrated
|
16
|
+
into gemma itself.
|
16
17
|
|
17
18
|
GEMMA is a software toolkit for fast application of linear mixed
|
18
19
|
models (LMMs) and related models to genome-wide association studies
|
@@ -29,6 +30,14 @@ does a pass-through of all standard GEMMA invocation switches. On
|
|
29
30
|
return gemma-wrapper can return a JSON object (--json) which is
|
30
31
|
useful for web-services.
|
31
32
|
|
33
|
+
## Performance
|
34
|
+
|
35
|
+
LOCO runs in parallel by default which is at least a 5x performance
|
36
|
+
improvement on a machine with enough cores. GEMMA without LOCO,
|
37
|
+
however, does not run in parallel by default. Performance
|
38
|
+
improvements with the parallel implementation for LOCO and non-LOCO
|
39
|
+
can be viewed [here](./test/performance/releases.gmi).
|
40
|
+
|
32
41
|
## Installation
|
33
42
|
|
34
43
|
Prerequisites are
|
@@ -53,15 +62,19 @@ and it will render something like
|
|
53
62
|
Usage: gemma-wrapper [options] -- [gemma-options]
|
54
63
|
--permutate n Permutate # times by shuffling phenotypes
|
55
64
|
--permute-phenotypes filen Phenotypes to be shuffled in permutations
|
56
|
-
--loco
|
65
|
+
--loco Run full leave-one-chromosome-out (LOCO)
|
66
|
+
--chromosomes [1,2,3] Run specific chromosomes
|
57
67
|
--input filen JSON input variables (used for LOCO)
|
58
68
|
--cache-dir path Use a cache directory
|
59
69
|
--json Create output file in JSON format
|
60
|
-
--force Force computation
|
61
|
-
--
|
70
|
+
--force Force computation (override cache)
|
71
|
+
--parallel Run jobs in parallel
|
72
|
+
--no-parallel Do not run jobs in parallel
|
73
|
+
--slurm[=opts] Use slurm PBS for submitting jobs
|
62
74
|
--q, --quiet Run quietly
|
63
75
|
-v, --verbose Run verbosely
|
64
|
-
|
76
|
+
-d, --debug Show debug messages and keep intermediate output
|
77
|
+
--dry-run Show commands, but don't execute
|
65
78
|
-- Anything after gets passed to GEMMA
|
66
79
|
|
67
80
|
-h, --help display this help and exit
|
@@ -99,6 +112,7 @@ the data files are found):
|
|
99
112
|
gemma-wrapper -- \
|
100
113
|
-g test/data/input/BXD_geno.txt.gz \
|
101
114
|
-p test/data/input/BXD_pheno.txt \
|
115
|
+
-a test/data/input/BXD_snps.txt \
|
102
116
|
-gk \
|
103
117
|
-debug
|
104
118
|
|
@@ -116,6 +130,7 @@ You can also get JSON output on STDOUT by providing the --json switch
|
|
116
130
|
gemma-wrapper --json -- \
|
117
131
|
-g test/data/input/BXD_geno.txt.gz \
|
118
132
|
-p test/data/input/BXD_pheno.txt \
|
133
|
+
-a test/data/input/BXD_snps.txt \
|
119
134
|
-gk \
|
120
135
|
-debug > K.json
|
121
136
|
|
@@ -133,6 +148,7 @@ default. If you want something else provide a --cache-dir, e.g.
|
|
133
148
|
gemma-wrapper --cache-dir ~/.gemma-cache -- \
|
134
149
|
-g test/data/input/BXD_geno.txt.gz \
|
135
150
|
-p test/data/input/BXD_pheno.txt \
|
151
|
+
-a test/data/input/BXD_snps.txt \
|
136
152
|
-gk \
|
137
153
|
-debug
|
138
154
|
|
@@ -143,7 +159,7 @@ will store K in ~/.gemma-cache.
|
|
143
159
|
Run the LMM using the K's captured earlier in K.json using the --input
|
144
160
|
switch
|
145
161
|
|
146
|
-
gemma-wrapper --json --
|
162
|
+
gemma-wrapper --json --input K.json -- \
|
147
163
|
-g test/data/input/BXD_geno.txt.gz \
|
148
164
|
-p test/data/input/BXD_pheno.txt \
|
149
165
|
-c test/data/input/BXD_covariates2.txt \
|
@@ -163,7 +179,7 @@ https://github.com/genetics-statistics/GEMMA/issues/46). To loop all
|
|
163
179
|
chromosomes first create all K's with
|
164
180
|
|
165
181
|
gemma-wrapper --json \
|
166
|
-
--loco
|
182
|
+
--loco -- \
|
167
183
|
-g test/data/input/BXD_geno.txt.gz \
|
168
184
|
-p test/data/input/BXD_pheno.txt \
|
169
185
|
-a test/data/input/BXD_snps.txt \
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.99.
|
1
|
+
0.99.6
|
data/bin/gemma-wrapper
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
# Author:: Pjotr Prins
|
5
5
|
# License:: GPL3
|
6
6
|
#
|
7
|
-
# Copyright (C) 2017-
|
7
|
+
# Copyright (C) 2017-2022 Pjotr Prins <pjotr.prins@thebird.nl>
|
8
8
|
|
9
9
|
USAGE = "
|
10
10
|
GEMMA wrapper example:
|
@@ -14,12 +14,12 @@ GEMMA wrapper example:
|
|
14
14
|
gemma-wrapper -- \\
|
15
15
|
-g test/data/input/BXD_geno.txt.gz \\
|
16
16
|
-p test/data/input/BXD_pheno.txt \\
|
17
|
+
-a test/data/input/BXD_snps.txt \
|
17
18
|
-gk
|
18
19
|
|
19
20
|
LOCO K computation with caching and JSON output
|
20
21
|
|
21
|
-
gemma-wrapper --json \\
|
22
|
-
--loco 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,X -- \\
|
22
|
+
gemma-wrapper --json --loco -- \\
|
23
23
|
-g test/data/input/BXD_geno.txt.gz \\
|
24
24
|
-p test/data/input/BXD_pheno.txt \\
|
25
25
|
-a test/data/input/BXD_snps.txt \\
|
@@ -64,6 +64,7 @@ if not gemma_command
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
+
hashme = nil
|
67
68
|
|
68
69
|
require 'digest/sha1'
|
69
70
|
require 'fileutils'
|
@@ -71,12 +72,15 @@ require 'optparse'
|
|
71
72
|
require 'tempfile'
|
72
73
|
require 'tmpdir'
|
73
74
|
|
75
|
+
require 'lock'
|
76
|
+
|
74
77
|
split_at = ARGV.index('--')
|
78
|
+
|
75
79
|
if split_at
|
76
80
|
gemma_args = ARGV[split_at+1..-1]
|
77
81
|
end
|
78
82
|
|
79
|
-
options = { show_help: false, source: 'https://github.com/genetics-statistics/gemma-wrapper', version: version+' (Pjotr Prins)', date: Time.now.to_s, gemma_command: gemma_command, cache_dir: Dir.tmpdir(), quiet: false, parallel:
|
83
|
+
options = { show_help: false, source: 'https://github.com/genetics-statistics/gemma-wrapper', version: version+' (Pjotr Prins)', date: Time.now.to_s, gemma_command: gemma_command, cache_dir: Dir.tmpdir(), quiet: false, permute_phenotypes: false, parallel: nil }
|
80
84
|
|
81
85
|
opts = OptionParser.new do |o|
|
82
86
|
o.banner = "\nUsage: #{File.basename($0)} [options] -- [gemma-options]"
|
@@ -91,8 +95,12 @@ opts = OptionParser.new do |o|
|
|
91
95
|
raise "Phenotype input file #{phenotypes} does not exist" if !File.exist?(phenotypes)
|
92
96
|
end
|
93
97
|
|
94
|
-
o.on('--loco
|
95
|
-
options[:loco] =
|
98
|
+
o.on('--loco', 'Run full leave-one-chromosome-out (LOCO)') do |b|
|
99
|
+
options[:loco] = b
|
100
|
+
end
|
101
|
+
|
102
|
+
o.on('--chromosomes [1,2,3]',Array,'Run specific chromosomes') do |lst|
|
103
|
+
options[:chromosomes] = lst
|
96
104
|
end
|
97
105
|
|
98
106
|
o.on('--input filen',String, 'JSON input variables (used for LOCO)') do |filen|
|
@@ -112,6 +120,10 @@ opts = OptionParser.new do |o|
|
|
112
120
|
options[:force] = true
|
113
121
|
end
|
114
122
|
|
123
|
+
o.on("--parallel", "Run jobs in parallel") do |b|
|
124
|
+
options[:parallel] = true
|
125
|
+
end
|
126
|
+
|
115
127
|
o.on("--no-parallel", "Do not run jobs in parallel") do |b|
|
116
128
|
options[:parallel] = false
|
117
129
|
end
|
@@ -185,26 +197,36 @@ warning = lambda do |*msg|
|
|
185
197
|
record[:warnings].push *msg.join("")
|
186
198
|
OUTPUT.print "WARNING: ",*msg,"\n"
|
187
199
|
end
|
200
|
+
|
188
201
|
info = lambda do |*msg|
|
189
202
|
record[:debug].push *msg.join("") if options[:json] and options[:debug]
|
190
203
|
OUTPUT.print *msg,"\n" if !options[:quiet]
|
191
204
|
end
|
192
205
|
|
206
|
+
# Fetch chromosomes
|
207
|
+
def get_chromosomes annofn
|
208
|
+
h = {}
|
209
|
+
File.open(annofn,"r").each_line do | line |
|
210
|
+
chr = line.split(/\s+/)[2]
|
211
|
+
h[chr] = true
|
212
|
+
end
|
213
|
+
h.map { |k,v| k }
|
214
|
+
end
|
193
215
|
# ---- Start banner
|
194
216
|
|
195
217
|
GEMMA_K_VERSION=version
|
196
|
-
GEMMA_K_BANNER = "gemma-wrapper #{version} (Ruby #{RUBY_VERSION}) by Pjotr Prins 2017-
|
218
|
+
GEMMA_K_BANNER = "gemma-wrapper #{version} (Ruby #{RUBY_VERSION}) by Pjotr Prins 2017-2022\n"
|
197
219
|
info.call GEMMA_K_BANNER
|
198
220
|
|
199
221
|
# Check gemma version
|
200
|
-
GEMMA_COMMAND=options[:gemma_command]
|
201
|
-
info.call "NOTE: gemma-wrapper is soon to be replaced by gemma2/lib"
|
202
|
-
|
203
222
|
begin
|
204
|
-
|
223
|
+
gemma_command2 = options[:gemma_command]
|
224
|
+
info.call "NOTE: gemma-wrapper is soon to be replaced"
|
225
|
+
|
226
|
+
GEMMA_INFO = `#{gemma_command2}`
|
205
227
|
rescue Errno::ENOENT
|
206
|
-
|
207
|
-
error.call "<#{
|
228
|
+
gemma_command2 = "gemma"
|
229
|
+
error.call "<#{gemma_command2}> command not found"
|
208
230
|
end
|
209
231
|
|
210
232
|
gemma_version_header = GEMMA_INFO.split("\n").grep(/GEMMA|Version/)[0].strip
|
@@ -230,13 +252,17 @@ if RUBY_VERSION =~ /^1/
|
|
230
252
|
warning "runs on Ruby 2.x only\n"
|
231
253
|
end
|
232
254
|
|
255
|
+
# ---- LOCO defaults to parallel
|
256
|
+
if options[:parallel] == nil
|
257
|
+
options[:parallel] = true if options[:loco]
|
258
|
+
end
|
259
|
+
|
233
260
|
debug.call(options) # some debug output
|
234
261
|
debug.call(record)
|
235
262
|
|
236
263
|
DO_COMPUTE_KINSHIP = gemma_args.include?("-gk")
|
237
264
|
DO_COMPUTE_GWA = !DO_COMPUTE_KINSHIP
|
238
265
|
|
239
|
-
# ---- Set up parallel
|
240
266
|
if options[:parallel]
|
241
267
|
begin
|
242
268
|
skip_cite = `echo "will cite" |parallel --citation`
|
@@ -248,8 +274,12 @@ if options[:parallel]
|
|
248
274
|
parallel_cmds = []
|
249
275
|
end
|
250
276
|
|
277
|
+
# ---- Fetch chromosomes from SNP annotation file
|
278
|
+
anno_idx = gemma_args.index '-a'
|
279
|
+
raise "Expected GEMMA -a genotype file switch" if anno_idx == nil
|
280
|
+
CHROMOSOMES = get_chromosomes(gemma_args[anno_idx+1])
|
281
|
+
|
251
282
|
# ---- Compute HASH on inputs
|
252
|
-
hashme = []
|
253
283
|
geno_idx = gemma_args.index '-g'
|
254
284
|
raise "Expected GEMMA -g genotype file switch" if geno_idx == nil
|
255
285
|
pheno_idx = gemma_args.index '-p'
|
@@ -279,14 +309,15 @@ execute = lambda { |cmd|
|
|
279
309
|
|
280
310
|
compute_hash = lambda do | phenofn = nil |
|
281
311
|
# Compute a HASH on the inputs
|
282
|
-
|
312
|
+
error.call "Hash is empty" if hashme == nil or hashme == []
|
313
|
+
debug.call "Hashing on ",hashme," before phenofn inject"
|
283
314
|
hashes = []
|
284
315
|
hm = if phenofn
|
285
316
|
hashme + ["-p", phenofn]
|
286
317
|
else
|
287
318
|
hashme
|
288
319
|
end
|
289
|
-
debug.call(hm)
|
320
|
+
debug.call("Hashing on ",hm)
|
290
321
|
hm.each do | item |
|
291
322
|
if File.file?(item)
|
292
323
|
hashes << Digest::SHA1.hexdigest(File.read(item))
|
@@ -299,20 +330,9 @@ compute_hash = lambda do | phenofn = nil |
|
|
299
330
|
Digest::SHA1.hexdigest hashes.join(' ')
|
300
331
|
end
|
301
332
|
|
302
|
-
HASH = compute_hash.call()
|
303
|
-
options[:hash] = HASH
|
304
|
-
|
305
|
-
# Create cache dir
|
306
|
-
FileUtils::mkdir_p options[:cache_dir]
|
307
|
-
|
308
|
-
Dir.mktmpdir do |tmpdir| # tmpdir for GEMMA output
|
309
|
-
|
310
333
|
error.call "Do not use the GEMMA -o switch!" if gemma_args.include? '-o'
|
311
334
|
error.call "Do not use the GEMMA -outdir switch!" if gemma_args.include? '-outdir'
|
312
335
|
GEMMA_ARGS_HASH = gemma_args.dup # do not include outdir
|
313
|
-
gemma_args << '-outdir'
|
314
|
-
gemma_args << tmpdir
|
315
|
-
GEMMA_ARGS = gemma_args
|
316
336
|
|
317
337
|
hashme =
|
318
338
|
if DO_COMPUTE_KINSHIP and pheno_idx != nil
|
@@ -322,10 +342,30 @@ hashme =
|
|
322
342
|
GEMMA_ARGS_HASH
|
323
343
|
end
|
324
344
|
|
345
|
+
HASH = compute_hash.call()
|
346
|
+
options[:hash] = HASH
|
347
|
+
|
348
|
+
at_exit do
|
349
|
+
Lock.release(HASH)
|
350
|
+
end
|
351
|
+
|
352
|
+
Lock.create(HASH) # this will wait for a lock to expire
|
353
|
+
|
354
|
+
joblog = options[:cache_dir]+"/"+HASH+"-parallel.log"
|
355
|
+
|
356
|
+
# Create cache dir
|
357
|
+
FileUtils::mkdir_p options[:cache_dir]
|
358
|
+
|
359
|
+
Dir.mktmpdir do |tmpdir| # tmpdir for GEMMA output
|
360
|
+
|
361
|
+
gemma_args << '-outdir'
|
362
|
+
gemma_args << tmpdir
|
363
|
+
GEMMA_ARGS = gemma_args
|
364
|
+
|
325
365
|
debug.call "Options: ",options,"\n" if !options[:quiet]
|
326
366
|
|
327
367
|
invoke_gemma = lambda do |extra_args, cache_hit = false, chr = "full", permutation = 1|
|
328
|
-
cmd = "#{
|
368
|
+
cmd = "#{gemma_command2} #{GEMMA_ARGS.join(' ')} #{extra_args.join(' ')}"
|
329
369
|
record[:gemma_command] = cmd
|
330
370
|
return if cache_hit
|
331
371
|
if options[:slurm]
|
@@ -434,11 +474,17 @@ gwas = lambda do | chr, kfn, pfn, permutation=0 |
|
|
434
474
|
end
|
435
475
|
|
436
476
|
LOCO = options[:loco]
|
477
|
+
if LOCO
|
478
|
+
if options[:chromosomes]
|
479
|
+
CHROMOSOMES = options[:chromosomes]
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
437
483
|
if DO_COMPUTE_KINSHIP
|
438
484
|
# compute K
|
439
|
-
info.call
|
440
|
-
if LOCO
|
441
|
-
|
485
|
+
info.call CHROMOSOMES
|
486
|
+
if LOCO
|
487
|
+
CHROMOSOMES.each do |chr|
|
442
488
|
info.call "LOCO for ",chr
|
443
489
|
kinship.call(chr)
|
444
490
|
end
|
@@ -447,13 +493,24 @@ if DO_COMPUTE_KINSHIP
|
|
447
493
|
end
|
448
494
|
else
|
449
495
|
# DO_COMPUTE_GWA
|
450
|
-
|
496
|
+
begin
|
497
|
+
json_in = JSON.parse(File.read(options[:input]))
|
498
|
+
rescue TypeError
|
499
|
+
raise "Missing JSON input file?"
|
500
|
+
end
|
451
501
|
raise "JSON problem, file #{options[:input]} is not -gk derived" if json_in["type"] != "K"
|
452
502
|
|
453
503
|
pfn = options[:permute_phenotypes] # can be nil
|
454
|
-
|
455
|
-
|
456
|
-
|
504
|
+
if LOCO
|
505
|
+
k_files = json_in["files"].map { |rec| [rec[0],rec[2]] }
|
506
|
+
k_files.each do | chr, kfn | # call a GWA for each chromosome
|
507
|
+
gwas.call(chr,kfn,pfn)
|
508
|
+
end
|
509
|
+
else
|
510
|
+
kfn = json_in["files"][0][2]
|
511
|
+
CHROMOSOMES.each do | chr |
|
512
|
+
gwas.call(chr,kfn,pfn)
|
513
|
+
end
|
457
514
|
end
|
458
515
|
# Permute
|
459
516
|
if options[:permutate]
|
@@ -505,22 +562,34 @@ end
|
|
505
562
|
# ---- Invoke parallel
|
506
563
|
if options[:parallel]
|
507
564
|
# parallel_cmds = ["echo 1","sleep 1 && echo 2", "false", "echo 3"]
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
err = execute.call(cmd+"|parallel -j #{jobs}")
|
516
|
-
break if err == 0
|
565
|
+
|
566
|
+
Tempfile.open("commands.txt") do |f|
|
567
|
+
cmdfn = f.path
|
568
|
+
File.open(cmdfn,"w") do |f|
|
569
|
+
parallel_cmds.each do |c|
|
570
|
+
f.puts(c)
|
571
|
+
end
|
517
572
|
end
|
573
|
+
cmd = "cat \"#{cmdfn}\""
|
574
|
+
err = execute.call(cmd+"|parallel --joblog #{joblog}") # first try optimistically to run all jobs in parallel
|
518
575
|
if err != 0
|
519
|
-
|
520
|
-
|
576
|
+
[16,8,4,1].each do |jobs|
|
577
|
+
info.call("Failed to complete parallel run -- retrying with smaller RAM footprint!")
|
578
|
+
err = execute.call(cmd+"|parallel -j #{jobs} --resume --joblog #{joblog}")
|
579
|
+
break if err == 0
|
580
|
+
end
|
581
|
+
if err != 0
|
582
|
+
info.call("Parallel run failed!")
|
583
|
+
debug.call("Job log is: ",File.read(joblog))
|
584
|
+
# Remove remaining files
|
585
|
+
FileUtils.mv joblog, joblog+".bak", verbose: false, force: true
|
586
|
+
FileUtils.rm_rf("#{tmpdir}/*", secure: true)
|
587
|
+
exit err
|
588
|
+
end
|
521
589
|
end
|
522
590
|
end
|
523
591
|
info.call("Run successful!")
|
592
|
+
FileUtils.mv joblog, joblog+".bak", verbose: false, force: true
|
524
593
|
end
|
525
594
|
json_out.call
|
526
595
|
|
data/gemma-wrapper.gemspec
CHANGED
data/lib/lock.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# Locking module for gemma (wrapper)
|
2
|
+
#
|
3
|
+
|
4
|
+
=begin
|
5
|
+
|
6
|
+
The logic is as follows:
|
7
|
+
|
8
|
+
1. a program creates a named lock file (based on a hash of its inputs) with its PID
|
9
|
+
2. on exit it destroys the file
|
10
|
+
3. a new program checks for the lock file
|
11
|
+
4. if it exists and the PID is still in the ps table - wait
|
12
|
+
5. when the pid disappears or the lock file - continue
|
13
|
+
6. a timeout will return an error in 3 minutes
|
14
|
+
|
15
|
+
Note that there is a theoretical chance the lock file existed, but disappeared. I think I have it covered by ignoring the unlink errors. Also the use of /proc/PID is Linux specific.
|
16
|
+
|
17
|
+
=end
|
18
|
+
|
19
|
+
|
20
|
+
require 'timeout'
|
21
|
+
|
22
|
+
module Lock
|
23
|
+
|
24
|
+
def self.local name
|
25
|
+
ENV['HOME']+"/."+name.gsub("/","-")+".lck"
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.lock_pid name
|
29
|
+
lockfn = local(name)
|
30
|
+
if File.exist?(lockfn)
|
31
|
+
File.read(lockfn).to_i
|
32
|
+
else
|
33
|
+
0
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.locked? name
|
38
|
+
lockfn = local(name)
|
39
|
+
pid = lock_pid(name)
|
40
|
+
if File.exist?("/proc/#{pid}")
|
41
|
+
true
|
42
|
+
else
|
43
|
+
# the program went away - remove any 'stale' lock
|
44
|
+
begin
|
45
|
+
File.unlink(lockfn)
|
46
|
+
rescue Errno::ENOENT
|
47
|
+
# ignore error when the lock file went missing
|
48
|
+
end
|
49
|
+
false # --> no longer locked
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def Lock::create name
|
54
|
+
wait_for(name)
|
55
|
+
lockfn = local(name)
|
56
|
+
if File.exist?(lockfn)
|
57
|
+
$stderr.print "\nERROR: Can not steal #{lockfn}"
|
58
|
+
exit 1
|
59
|
+
end
|
60
|
+
File.open(lockfn, File::RDWR|File::CREAT, 0644) do |f|
|
61
|
+
f.flock(File::LOCK_EX)
|
62
|
+
f.print(Process.pid)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def Lock::wait_for name
|
67
|
+
lockfn = local(name)
|
68
|
+
begin
|
69
|
+
status = Timeout::timeout(180) { # 3 minutes
|
70
|
+
while locked?(name)
|
71
|
+
$stderr.print("\nWaiting for lock #{lockfn}...")
|
72
|
+
sleep 2
|
73
|
+
end
|
74
|
+
}
|
75
|
+
rescue Timeout::Error
|
76
|
+
$stderr.print "\nERROR: Timed out, but I can not steal #{lockfn}"
|
77
|
+
exit 1
|
78
|
+
end
|
79
|
+
# yah! lock is released
|
80
|
+
end
|
81
|
+
|
82
|
+
def Lock::release name
|
83
|
+
lockfn = local(name)
|
84
|
+
if Process.pid == lock_pid(name)
|
85
|
+
begin
|
86
|
+
File.unlink(lockfn) # PID expired
|
87
|
+
rescue Errno::ENOENT
|
88
|
+
# ignore error when the lock file went missing
|
89
|
+
end
|
90
|
+
else
|
91
|
+
$stderr.print "\nERROR: can not release #{lockfn} because it is not owned by me"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bio-gemma-wrapper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.99.
|
4
|
+
version: 0.99.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pjotr Prins
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: GEMMA wrapper adds LOCO and permutation support. Also runs in parallel
|
14
14
|
and caches K between runs with LOCO support
|
@@ -24,11 +24,12 @@ files:
|
|
24
24
|
- VERSION
|
25
25
|
- bin/gemma-wrapper
|
26
26
|
- gemma-wrapper.gemspec
|
27
|
+
- lib/lock.rb
|
27
28
|
homepage: https://github.com/genetics-statistics/gemma-wrapper
|
28
29
|
licenses:
|
29
30
|
- GPL3
|
30
31
|
metadata: {}
|
31
|
-
post_install_message:
|
32
|
+
post_install_message:
|
32
33
|
rdoc_options: []
|
33
34
|
require_paths:
|
34
35
|
- lib
|
@@ -43,8 +44,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
44
|
- !ruby/object:Gem::Version
|
44
45
|
version: '0'
|
45
46
|
requirements: []
|
46
|
-
rubygems_version: 3.2.
|
47
|
-
signing_key:
|
47
|
+
rubygems_version: 3.2.22
|
48
|
+
signing_key:
|
48
49
|
specification_version: 4
|
49
50
|
summary: GEMMA with LOCO and permutations
|
50
51
|
test_files: []
|