swigra_multibatch
6.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#! /usr/bin/perl
#
use strict;
use vars qw(@files %processes %prevstats $swigradir $parser);
use constant VMEM_LIMIT => 20000*1000*1000;
use constant CONCURRENCY_LEVEL => 20;
use constant DELAY => 2;
use POSIX ":sys_wait_h";
# pakiet Ubuntu: libbsd-resource-perl
use BSD::Resource;
%processes = (); # Will map PIDs to [‹świgra file›, ‹utime›, ‹stime›, ‹inactivity counter›, ‹vmem›]
%prevstats = ();
# open STAT, "<", "../parsestats.tab";
# while (<STAT>) {
# }
# close STAT;
open STAT, ">", "../parsestats.tab";
# SWI Prolog on Linux has the bad habit of hanging in a system call
# when memory gets exhausted. For that reason we have to implement a
# framework for killing processes that get inactive but do not die.
# pakiet Ubuntu: libfile-slurp-perl
use File::Slurp;
# This function is Linux-specific.
# We assume this test will be performed each second.
sub is_alive {
my $pid = shift;
my @stat = split ' ', read_file("/proc/$pid/stat");
# Pola w tym pliku są opisane w man proc
# [2] state
# [13] cpu time in user mode
# [14] cpu time kernel mode
# [22] virtual memory size
my $active = $stat[2] eq 'R' # process status
|| $stat[13] > $processes{$pid}->[1] # utime
|| $stat[14] > $processes{$pid}->[2]; # stime
if ($active) {
$processes{$pid}->[3] = 0;
$processes{$pid}->[1] = $stat[13]; # remember current utime
$processes{$pid}->[2] = $stat[14]; # remember current stime
$processes{$pid}->[4] = $stat[22] # remember max vmem
if $processes{$pid}->[4] < $stat[22];
} else {
$processes{$pid}->[3] ++;
}
print STDERR join ' ', "Process $pid is", ($active?'':'not'), 'active. ', 'IC:', $processes{$pid}->[3],
'stat:', @stat[2,13..14,22], "\n";
return $processes{$pid}->[3] < 10; # after 10 seconds of inactivity we consider a process really dead
}
die unless @ARGV == 1;
$swigradir = "$ENV{HOME}/swigra/parser";
$parser = 'gfjp2-bin';
@files = glob(shift);
print STDERR "There are ".scalar(@files)." files to process.\n";
while (@files >0) {
for my $kid (keys %processes) {
if (!is_alive($kid)) {
print STDERR "Killing $kid!\n";
kill('TERM', $kid);
}
}
my $kid = waitpid(-1, WNOHANG);
while ($kid >0) {
if (defined $processes{$kid}) {
print STDERR "Process $kid for $processes{$kid}->[0] has finished, ${\(scalar(keys %processes)-1)} running.\n";
print STAT "$processes{$kid}->[0]\t$processes{$kid}->[1]\t$processes{$kid}->[2]\t$processes{$kid}->[4]\n";
delete $processes{$kid};
# spawn_child(shift @files);
} else {
print STDERR "STRANGE: we haven't been waiting for process $kid.\n";
}
$kid = waitpid(-1, WNOHANG);
}
spawn_child(shift @files)
while (keys %processes < CONCURRENCY_LEVEL && @files > 0);
sleep DELAY;
}
# wait for remaining children
while (keys %processes > 0) {
for my $kid (keys %processes) {
if (!is_alive($kid)) {
print STDERR "Killing $kid!\n";
kill('TERM', $kid);
}
}
my $kid = waitpid(-1, WNOHANG);
while ($kid >0) {
if (defined $processes{$kid}) {
print STDERR "Process $kid for $processes{$kid}->[0] has finished, ${\(scalar(keys %processes)-1)} running.\n";
delete $processes{$kid};
# spawn_child(shift @files);
} else {
print STDERR "STRANGE: we haven't been waiting for process $kid.\n";
}
$kid = waitpid(-1, WNOHANG);
}
sleep DELAY;
}
# close STAT;
########################################################################
sub run_swigra { # to be executed in a child process!
my $doa = shift;
my $forest = $doa;
$forest =~ s/.doa$/.forest/;
# ulimit -n 8*60:
# “Processes have soft and hard resource limits. On crossing the
# soft limit they receive a signal (for example the "SIGXCPU" or
# "SIGXFSZ", corresponding to the "RLIMIT_CPU" and "RLIMIT_FSIZE",
# respectively). The processes can trap and handle some of these
# signals, please see "Signals" in perlipc. After the hard limit
# the processes will be ruthlessly killed by the "KILL" signal
# which cannot be caught.
# We set the soft limit (setting the hard limit results in “invalid argument” error!):
setrlimit(RLIMIT_CPU,60*60,RLIM_INFINITY) || die "setrlimit failed for cputime: $!";
# było 20G, ale trochę szkodziło serwerowi
setrlimit(RLIMIT_VMEM,VMEM_LIMIT,RLIM_INFINITY) || die "setrlimit failed for vmem: $!";
# nice -n 19
setpriority(PRIO_PROCESS,0, 19) || die "setpriority failed: $!";
open STDOUT, '>', $forest or die "Can't redirect STDOUT: $!";
exec 'swipl', '-x', "$swigradir/$parser",'-t','halt', '-g',"['$doa']";
}
sub spawn_child {
my $file = shift;
my $forest = $file;
$forest =~ s/.doa$/.forest/;
if ( -s $forest ) { # file exists and has nonzero size
print STDERR "--- $forest already present.\n";
return;
}
if (!defined(my $kidpid = fork())) {
# fork returned undef, so failed
die "cannot fork for $file:\n $!";
} elsif ($kidpid == 0) {
# fork returned 0, so this branch is the child
run_swigra($file);
# if the exec fails, fall through to the next statement
die "can't exec Swigra: $!";
} else {
# fork returned neither 0 nor undef,
# so this branch is the parent
print STDERR "Started process $kidpid for $file.\n";
$processes{$kidpid} = [$file,0,0,0];
}
}
# # from http://www.perlmonks.org/?node_id=254756:
## failed with EPERM!
# sub set_max_cpu_time
# {
# my $n_seconds = shift;
# my $s = pack( 'LL', $n_seconds, $n_seconds+1 );
# $! = 0;
# if ( syscall( 128, 0, $s ) == -1 ) # SYS_setrlimit, RLIMIT_CPU
# {
# if ( $! == 1 ) # EPERM
# {
# die "$!; Sorry, you need to be superuser to do that.\n";
# }
# elsif ( $! == 14 ) # EFAULT
# {
# die "$!; Error: argument to setrlimit pointed to an illegal ad
# +dress.\n";
# }
# elsif ( $! == 22 ) # EINVAL
# {
# die "$!; Error: the new rlim_cur exceeds the new rlim_max,
# or the limit specified cannot be lowered because current usa
# +ge
# is already higher than the limit.\n";
# }
# }
# }
# non-blocking test for running children:
# use POSIX ":sys_wait_h";
# #...
# do {
# $kid = waitpid(-1, WNOHANG);
# } while $kid > 0;
# redyrekcje:
#open(STDERR,">&STDOUT")