ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf/incloader.pm
Revision: 1.4
Committed: Thu May 13 17:33:29 2010 UTC (14 years, 2 months ago) by root
Branch: MAIN
CVS Tags: rel-3_0
Changes since 1.3: +93 -31 lines
Log Message:
preload unicore/...

File Contents

# Content
1 #
2 # This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 #
4 # Copyright (©) 2005,2006,2007,2008,2009,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 #
6 # Deliantra is free software: you can redistribute it and/or modify it under
7 # the terms of the Affero GNU General Public License as published by the
8 # Free Software Foundation, either version 3 of the License, or (at your
9 # option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the Affero GNU General Public License
17 # and the GNU General Public License along with this program. If not, see
18 # <http://www.gnu.org/licenses/>.
19 #
20 # The authors can be reached via e-mail to <support@deliantra.net>
21 #
22
23 package cf::incloader;
24
25 use common::sense;
26
27 our %FILE; # pre-loaded files
28 our $S1; # disk-size
29 our $S2; # memory-size
30
31 our $PID = $$;
32
33 sub find_inc($) {
34 for my $dir (@cf::ORIG_INC) {
35 ref $dir and next;
36
37 my $path = "$dir/$_[0]";
38
39 return $path
40 unless Coro::AIO::aio_stat $path;
41 }
42
43 undef
44 }
45
46 # async inc loader. yay.
47 sub inc_loader {
48 my $mod = $_[1];
49
50 my ($path, $data);
51
52 if (my $file = delete $FILE{$mod}) {
53 # we already have it in memory..
54 cf::debug "incloader: using preloaded file for $mod";
55 $path = $file->[0];
56 $data = Compress::LZF::decompress $file->[1];
57
58 } else {
59 # we need to load it from disk.
60
61 return if $$ != $PID; # do not do this in child processes
62
63 if (cf::in_main && !cf::tick_inhibit) {
64 Carp::cluck "ERROR: attempted synchronous perl module load ($mod)";
65 return; # do it blockingly
66 } else {
67 cf::debug "incloader: loading perl module $mod";
68 }
69
70 # find real file
71 $path = find_inc $mod;
72
73 defined $path
74 or Carp::croak "Can't locate $mod in \@INC";
75
76 0 <= Coro::AIO::aio_load $path, $data
77 or next;
78
79 # hackish way to pre-cache .so/.bs files
80 if ($path =~ s/\.pm$//) {
81 my @c = split /\//, $path;
82 for (reverse 2 .. $#c) {
83 my $a = join "/", @c[0..$_-1];
84 my $b = join "/", @c[$_..$#c];
85
86 Coro::AIO::aio_stat "$a/auto/$b/."
87 and next;
88
89 -f _
90 or next;
91
92 $path = "$a/auto/$b/";
93
94 cf::debug "incloader: pre-caching $path";
95
96 my $files = Coro::AIO::aio_readdir $path;
97
98 my $grp = IO::AIO::aio_group;
99
100 for my $file (@$files) {
101 next if $file =~ /(^\.packlist|\.h$|\.xst$)/;
102
103 add $grp IO::AIO::aio_open "$path/$file", IO::AIO::O_RDONLY, 0, sub {
104 my ($fh) = @_
105 or return;
106
107 add $grp IO::AIO::aio_stat $fh, sub {
108 my $size = -s _;
109
110 add $grp IO::AIO::aio_readahead $fh, 0, $size;
111
112 cf::debug "incloader: pre-caching $path/$file ($size)";
113 };
114 };
115 }
116
117 Coro::AIO::aio_wait $grp;
118 last;
119 }
120 }
121 }
122
123 # avoid upgrade when prepending #line, perlio is bugged
124 utf8::downgrade $path, 1;
125 $data = "#line 1 $path\n$data";
126
127 open my $fh, "<", \$data or die;
128
129 cf::get_slot 0.1, 10, "\@INC loader";
130
131 $fh
132 }
133
134 # load file/directory, so we can load it synchronously
135 sub preload_($$$) {
136 my ($path, $name, $regex) = @_;
137
138 if (exists $INC{$name}) {
139 cf::debug "incloader: $name already loaded, not preloading.";
140 } else {
141 Coro::AIO::aio_stat $path
142 and return;
143
144 if (-d _) {
145 &preload_ ("$path/$_", "$name/$_", $regex)
146 for @{ Coro::AIO::aio_readdir $path };
147 } elsif ($name =~ $regex) {
148 0 <= Coro::AIO::aio_load $path, my $data
149 or die "$path: $!";
150
151 $S1 += length $data;
152
153 $data =~ s/^(?:#[^\n]*\n)+//;
154 $data = Compress::LZF::compress $data;
155 $S2 += (length $path) + (length $data) + 96;
156
157 $FILE{$name} = [$path, $data];
158 }
159 }
160 }
161
162 sub preload($;$) {
163 my ($name, $regex) = @_;
164
165 my $path = find_inc $name;
166
167 defined $path
168 or return;
169
170 preload_ $path, $name, $regex;
171 }
172
173 sub preload_stuff {
174 # load some stuff so perl doesn't load it later... :/
175
176 eval { &Storable::nstore_fd };
177
178 preload "utf8_heavy.pl";
179 preload "unicore", qr/\.pl$/;
180
181 cf::info "incloader: preloaded $S1 octets from disk, cache now $S2 octets.";
182 }
183
184 sub init {
185 # save original @INC
186 @cf::ORIG_INC = ($cf::LIBDIR, @INC) unless @cf::ORIG_INC;
187
188 # make sure we can do scalar-opens
189 open my $dummy, "<", \my $dummy2;
190
191 preload_stuff;
192
193 @INC = (\&inc_loader, @cf::ORIG_INC); # @ORIG_INC is needed for DynaLoader, AutoLoad etc.
194
195 cf::debug "incloader: module loading will be asynchronous from this point on.";
196 }
197
198 1