ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf/incloader.pm
Revision: 1.9
Committed: Sat Nov 17 23:40:02 2018 UTC (5 years, 6 months ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.8: +1 -0 lines
Log Message:
copyright update 2018

File Contents

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