bootsnap 1.1.8-java → 1.5.0-java
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 +5 -5
- data/CHANGELOG.md +89 -0
- data/README.md +46 -6
- data/exe/bootsnap +5 -0
- data/ext/bootsnap/bootsnap.c +125 -87
- data/ext/bootsnap/extconf.rb +3 -1
- data/lib/bootsnap.rb +15 -6
- data/lib/bootsnap/bundler.rb +6 -3
- data/lib/bootsnap/cli.rb +136 -0
- data/lib/bootsnap/compile_cache.rb +32 -4
- data/lib/bootsnap/compile_cache/iseq.rb +25 -16
- data/lib/bootsnap/compile_cache/yaml.rb +75 -42
- data/lib/bootsnap/explicit_require.rb +2 -1
- data/lib/bootsnap/load_path_cache.rb +35 -9
- data/lib/bootsnap/load_path_cache/cache.rb +48 -29
- data/lib/bootsnap/load_path_cache/change_observer.rb +36 -29
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +39 -7
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +70 -53
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +18 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +148 -0
- data/lib/bootsnap/load_path_cache/path.rb +8 -7
- data/lib/bootsnap/load_path_cache/path_scanner.rb +50 -39
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +32 -0
- data/lib/bootsnap/load_path_cache/store.rb +20 -14
- data/lib/bootsnap/setup.rb +11 -13
- data/lib/bootsnap/version.rb +2 -1
- metadata +25 -28
- data/.gitignore +0 -17
- data/.rubocop.yml +0 -20
- data/.travis.yml +0 -4
- data/CODE_OF_CONDUCT.md +0 -74
- data/CONTRIBUTING.md +0 -21
- data/Gemfile +0 -8
- data/Rakefile +0 -11
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/bin/testunit +0 -8
- data/bootsnap.gemspec +0 -39
- data/dev.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a6981435a732015b8043d7ffc3a63c0603b6f1de300db2610becafc93c25b91e
|
4
|
+
data.tar.gz: d977b2ee4969224edd10530a4cb4332bcaea0298f8f147e4e3aaf846cbc584ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d544e765dcc1ccd998ffd0c65ee8aeda19e0a8a37cc37a0fbcb9917384283d27902acb81c079ec7c6b3cb1fe2ffb4fa752b1b668ae34bfdfc0b695cf943159bb
|
7
|
+
data.tar.gz: 7036e0b6c86ada90a48fe5d384b029c2c2f383e3231d3bcab43cc6bb765ef49cce4dc785120bac88c293aae2f24f4e23ba6d15ba9eb5ffa953eb0f8843e5e588
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,92 @@
|
|
1
|
+
# 1.5.0
|
2
|
+
|
3
|
+
* Add a command line to statically precompile the ISeq cache. (#326)
|
4
|
+
|
5
|
+
# 1.4.9
|
6
|
+
|
7
|
+
* [Windows support](https://github.com/Shopify/bootsnap/pull/319)
|
8
|
+
* [Fix potential crash](https://github.com/Shopify/bootsnap/pull/322)
|
9
|
+
|
10
|
+
# 1.4.8
|
11
|
+
|
12
|
+
* [Prevent FallbackScan from polluting exception cause](https://github.com/Shopify/bootsnap/pull/314)
|
13
|
+
|
14
|
+
# 1.4.7
|
15
|
+
|
16
|
+
* Various performance enhancements
|
17
|
+
* Fix race condition in heavy concurrent load scenarios that would cause bootsnap to raise
|
18
|
+
|
19
|
+
# 1.4.6
|
20
|
+
|
21
|
+
* Fix bug that was erroneously considering that files containing `.` in the names were being
|
22
|
+
required if a different file with the same name was already being required
|
23
|
+
|
24
|
+
Example:
|
25
|
+
|
26
|
+
require 'foo'
|
27
|
+
require 'foo.en'
|
28
|
+
|
29
|
+
Before bootsnap was considering `foo.en` to be the same file as `foo`
|
30
|
+
|
31
|
+
* Use glibc as part of the ruby_platform cache key
|
32
|
+
|
33
|
+
# 1.4.5
|
34
|
+
|
35
|
+
* MRI 2.7 support
|
36
|
+
* Fixed concurrency bugs
|
37
|
+
|
38
|
+
# 1.4.4
|
39
|
+
|
40
|
+
* Disable ISeq cache in `bootsnap/setup` by default in Ruby 2.5
|
41
|
+
|
42
|
+
# 1.4.3
|
43
|
+
|
44
|
+
* Fix some cache permissions and umask issues after switch to mkstemp
|
45
|
+
|
46
|
+
# 1.4.2
|
47
|
+
|
48
|
+
* Fix bug when removing features loaded by relative path from `$LOADED_FEATURES`
|
49
|
+
* Fix bug with propagation of `NameError` up from nested calls to `require`
|
50
|
+
|
51
|
+
# 1.4.1
|
52
|
+
|
53
|
+
* Don't register change observers to frozen objects.
|
54
|
+
|
55
|
+
# 1.4.0
|
56
|
+
|
57
|
+
* When running in development mode, always fall back to a full path scan on LoadError, making
|
58
|
+
bootsnap more able to detect newly-created files. (#230)
|
59
|
+
* Respect `$LOADED_FEATURES.delete` in order to support code reloading, for integration with
|
60
|
+
Zeitwerk. (#230)
|
61
|
+
* Minor performance improvement: flow-control exceptions no longer generate backtraces.
|
62
|
+
* Better support for requiring from environments where some features are not supported (especially
|
63
|
+
JRuby). (#226)k
|
64
|
+
* More robust handling of OS errors when creating files. (#225)
|
65
|
+
|
66
|
+
# 1.3.2
|
67
|
+
|
68
|
+
* Fix Spring + Bootsnap incompatibility when there are files with similar names.
|
69
|
+
* Fix `YAML.load_file` monkey patch to keep accepting File objects as arguments.
|
70
|
+
* Fix the API for `ActiveSupport::Dependencies#autoloadable_module?`.
|
71
|
+
* Some performance improvements.
|
72
|
+
|
73
|
+
# 1.3.1
|
74
|
+
|
75
|
+
* Change load path scanning to more correctly follow symlinks.
|
76
|
+
|
77
|
+
# 1.3.0
|
78
|
+
|
79
|
+
* Handle cases where load path entries are symlinked (https://github.com/Shopify/bootsnap/pull/136)
|
80
|
+
|
81
|
+
# 1.2.1
|
82
|
+
|
83
|
+
* Fix method visibility of `Kernel#require`.
|
84
|
+
|
85
|
+
# 1.2.0
|
86
|
+
|
87
|
+
* Add `LoadedFeaturesIndex` to preserve fix a common bug related to `LOAD_PATH` modifications after
|
88
|
+
loading bootsnap.
|
89
|
+
|
1
90
|
# 1.1.8
|
2
91
|
|
3
92
|
* Don't cache YAML documents with `!ruby/object`
|
data/README.md
CHANGED
@@ -1,16 +1,21 @@
|
|
1
|
-
# Bootsnap [](https://github.com/Shopify/bootsnap/actions)
|
2
2
|
|
3
3
|
Bootsnap is a library that plugs into Ruby, with optional support for `ActiveSupport` and `YAML`,
|
4
4
|
to optimize and cache expensive computations. See [How Does This Work](#how-does-this-work).
|
5
5
|
|
6
6
|
#### Performance
|
7
|
-
|
7
|
+
|
8
|
+
- [Discourse](https://github.com/discourse/discourse) reports a boot time reduction of approximately
|
9
|
+
50%, from roughly 6 to 3 seconds on one machine;
|
8
10
|
- One of our smaller internal apps also sees a reduction of 50%, from 3.6 to 1.8 seconds;
|
9
|
-
- The core Shopify platform -- a rather large monolithic application -- boots about 75% faster,
|
11
|
+
- The core Shopify platform -- a rather large monolithic application -- boots about 75% faster,
|
12
|
+
dropping from around 25s to 6.5s.
|
13
|
+
* In Shopify core (a large app), about 25% of this gain can be attributed to `compile_cache_*`
|
14
|
+
features; 75% to path caching, and ~1% to `disable_trace`. This is fairly representative.
|
10
15
|
|
11
16
|
## Usage
|
12
17
|
|
13
|
-
This gem works on
|
18
|
+
This gem works on macOS and Linux.
|
14
19
|
|
15
20
|
Add `bootsnap` to your `Gemfile`:
|
16
21
|
|
@@ -24,6 +29,17 @@ If you are using Rails, add this to `config/boot.rb` immediately after `require
|
|
24
29
|
require 'bootsnap/setup'
|
25
30
|
```
|
26
31
|
|
32
|
+
Note that bootsnap writes to `tmp/cache`, and that directory *must* be writable. Rails will fail to
|
33
|
+
boot if it is not. If this is unacceptable (e.g. you are running in a read-only container and
|
34
|
+
unwilling to mount in a writable tmpdir), you should remove this line or wrap it in a conditional.
|
35
|
+
|
36
|
+
**Note also that bootsnap will never clean up its own cache: this is left up to you. Depending on your
|
37
|
+
deployment strategy, you may need to periodically purge `tmp/cache/bootsnap*`. If you notice deploys
|
38
|
+
getting progressively slower, this is almost certainly the cause.**
|
39
|
+
|
40
|
+
It's technically possible to simply specify `gem 'bootsnap', require: 'bootsnap/setup'`, but it's
|
41
|
+
important to load Bootsnap as early as possible to get maximum performance improvement.
|
42
|
+
|
27
43
|
You can see how this require works [here](https://github.com/Shopify/bootsnap/blob/master/lib/bootsnap/setup.rb).
|
28
44
|
|
29
45
|
If you are not using Rails, or if you are but want more control over things, add this to your
|
@@ -38,12 +54,14 @@ Bootsnap.setup(
|
|
38
54
|
development_mode: env == 'development', # Current working environment, e.g. RACK_ENV, RAILS_ENV, etc
|
39
55
|
load_path_cache: true, # Optimize the LOAD_PATH with a cache
|
40
56
|
autoload_paths_cache: true, # Optimize ActiveSupport autoloads with cache
|
41
|
-
disable_trace: true, #
|
57
|
+
disable_trace: true, # Set `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }`
|
42
58
|
compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
|
43
59
|
compile_cache_yaml: true # Compile YAML into a cache
|
44
60
|
)
|
45
61
|
```
|
46
62
|
|
63
|
+
**Note that `disable_trace` will break debuggers and tracing.**
|
64
|
+
|
47
65
|
**Protip:** You can replace `require 'bootsnap'` with `BootLib::Require.from_gem('bootsnap',
|
48
66
|
'bootsnap')` using [this trick](https://github.com/Shopify/bootsnap/wiki/Bootlib::Require). This
|
49
67
|
will help optimize boot time further if you have an extremely large `$LOAD_PATH`.
|
@@ -196,7 +214,7 @@ Bootsnap writes a cache file containing a 64 byte header followed by the cache c
|
|
196
214
|
is a cache key including several fields:
|
197
215
|
|
198
216
|
* `version`, hardcoded in bootsnap. Essentially a schema version;
|
199
|
-
* `
|
217
|
+
* `ruby_platform`, A hash of `RUBY_PLATFORM` (e.g. x86_64-linux-gnu) variable and glibc version (on Linux) or OS version (`uname -v` on BSD, macOS)
|
200
218
|
* `compile_option`, which changes with `RubyVM::InstructionSequence.compile_option` does;
|
201
219
|
* `ruby_revision`, the version of Ruby this was compiled with;
|
202
220
|
* `size`, the size of the source file;
|
@@ -275,3 +293,25 @@ open /c/nope.bundle -> -1
|
|
275
293
|
```
|
276
294
|
# (nothing!)
|
277
295
|
```
|
296
|
+
|
297
|
+
## Precompilation
|
298
|
+
|
299
|
+
In development environments the bootsnap compilation cache is generated on the fly when source files are loaded.
|
300
|
+
But in production environments, such as docker images, you might need to precompile the cache.
|
301
|
+
|
302
|
+
To do so you can use the `bootsnap precompile` command.
|
303
|
+
|
304
|
+
Example:
|
305
|
+
|
306
|
+
```bash
|
307
|
+
$ bundle exec bootsnap precompile --gemfile app/ lib/
|
308
|
+
```
|
309
|
+
|
310
|
+
## When not to use Bootsnap
|
311
|
+
|
312
|
+
*Alternative engines*: Bootsnap is pretty reliant on MRI features, and parts are disabled entirely on alternative ruby
|
313
|
+
engines.
|
314
|
+
|
315
|
+
*Non-local filesystems*: Bootsnap depends on `tmp/cache` (or whatever you set its cache directory
|
316
|
+
to) being on a relatively fast filesystem. If you put it on a network mount, bootsnap is very likely
|
317
|
+
to slow your application down quite a lot.
|
data/exe/bootsnap
ADDED
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -21,6 +21,9 @@
|
|
21
21
|
#ifndef _WIN32
|
22
22
|
#include <sys/utsname.h>
|
23
23
|
#endif
|
24
|
+
#ifdef __GLIBC__
|
25
|
+
#include <gnu/libc-version.h>
|
26
|
+
#endif
|
24
27
|
|
25
28
|
/* 1000 is an arbitrary limit; FNV64 plus some slashes brings the cap down to
|
26
29
|
* 981 for the cache dir */
|
@@ -29,6 +32,8 @@
|
|
29
32
|
|
30
33
|
#define KEY_SIZE 64
|
31
34
|
|
35
|
+
#define MAX_CREATE_TEMPFILE_ATTEMPT 3
|
36
|
+
|
32
37
|
/*
|
33
38
|
* An instance of this key is written as the first 64 bytes of each cache file.
|
34
39
|
* The mtime and size members track whether the file contents have changed, and
|
@@ -74,6 +79,8 @@ static uint32_t current_ruby_platform;
|
|
74
79
|
static uint32_t current_ruby_revision;
|
75
80
|
/* Invalidates cache when RubyVM::InstructionSequence.compile_option changes */
|
76
81
|
static uint32_t current_compile_option_crc32 = 0;
|
82
|
+
/* Current umask */
|
83
|
+
static mode_t current_umask;
|
77
84
|
|
78
85
|
/* Bootsnap::CompileCache::{Native, Uncompilable} */
|
79
86
|
static VALUE rb_mBootsnap;
|
@@ -84,29 +91,29 @@ static ID uncompilable;
|
|
84
91
|
|
85
92
|
/* Functions exposed as module functions on Bootsnap::CompileCache::Native */
|
86
93
|
static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
|
87
|
-
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
94
|
+
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
|
88
95
|
|
89
96
|
/* Helpers */
|
90
97
|
static uint64_t fnv1a_64(const char *str);
|
91
|
-
static void bs_cache_path(const char * cachedir, const char * path, char
|
98
|
+
static void bs_cache_path(const char * cachedir, const char * path, const char * extra, char (* cache_path)[MAX_CACHEPATH_SIZE]);
|
92
99
|
static int bs_read_key(int fd, struct bs_cache_key * key);
|
93
100
|
static int cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2);
|
94
|
-
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
95
|
-
static int open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenance);
|
96
|
-
static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, char ** errno_provenance);
|
97
|
-
static
|
101
|
+
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args);
|
102
|
+
static int open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance);
|
103
|
+
static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance);
|
104
|
+
static uint32_t get_ruby_revision(void);
|
98
105
|
static uint32_t get_ruby_platform(void);
|
99
106
|
|
100
107
|
/*
|
101
108
|
* Helper functions to call ruby methods on handler object without crashing on
|
102
109
|
* exception.
|
103
110
|
*/
|
104
|
-
static int bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data);
|
111
|
+
static int bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data);
|
105
112
|
static VALUE prot_storage_to_output(VALUE arg);
|
106
113
|
static VALUE prot_input_to_output(VALUE arg);
|
107
|
-
static void bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag);
|
114
|
+
static void bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag);
|
108
115
|
static VALUE prot_input_to_storage(VALUE arg);
|
109
|
-
static int bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
116
|
+
static int bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
110
117
|
struct s2o_data;
|
111
118
|
struct i2o_data;
|
112
119
|
struct i2s_data;
|
@@ -135,14 +142,17 @@ Init_bootsnap(void)
|
|
135
142
|
rb_mBootsnap_CompileCache_Native = rb_define_module_under(rb_mBootsnap_CompileCache, "Native");
|
136
143
|
rb_eBootsnap_CompileCache_Uncompilable = rb_define_class_under(rb_mBootsnap_CompileCache, "Uncompilable", rb_eStandardError);
|
137
144
|
|
138
|
-
current_ruby_revision =
|
145
|
+
current_ruby_revision = get_ruby_revision();
|
139
146
|
current_ruby_platform = get_ruby_platform();
|
140
147
|
|
141
148
|
uncompilable = rb_intern("__bootsnap_uncompilable__");
|
142
149
|
|
143
150
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
|
144
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch,
|
151
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
|
145
152
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
|
153
|
+
|
154
|
+
current_umask = umask(0777);
|
155
|
+
umask(current_umask);
|
146
156
|
}
|
147
157
|
|
148
158
|
/*
|
@@ -192,6 +202,26 @@ fnv1a_64(const char *str)
|
|
192
202
|
return fnv1a_64_iter(h, str);
|
193
203
|
}
|
194
204
|
|
205
|
+
/*
|
206
|
+
* Ruby's revision may be Integer or String. CRuby 2.7 or later uses
|
207
|
+
* Git commit ID as revision. It's String.
|
208
|
+
*/
|
209
|
+
static uint32_t
|
210
|
+
get_ruby_revision(void)
|
211
|
+
{
|
212
|
+
VALUE ruby_revision;
|
213
|
+
|
214
|
+
ruby_revision = rb_const_get(rb_cObject, rb_intern("RUBY_REVISION"));
|
215
|
+
if (RB_TYPE_P(ruby_revision, RUBY_T_FIXNUM)) {
|
216
|
+
return FIX2INT(ruby_revision);
|
217
|
+
} else {
|
218
|
+
uint64_t hash;
|
219
|
+
|
220
|
+
hash = fnv1a_64(StringValueCStr(ruby_revision));
|
221
|
+
return (uint32_t)(hash >> 32);
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
195
225
|
/*
|
196
226
|
* When ruby's version doesn't change, but it's recompiled on a different OS
|
197
227
|
* (or OS version), we need to invalidate the cache.
|
@@ -211,6 +241,9 @@ get_ruby_platform(void)
|
|
211
241
|
|
212
242
|
#ifdef _WIN32
|
213
243
|
return (uint32_t)(hash >> 32) ^ (uint32_t)GetVersion();
|
244
|
+
#elif defined(__GLIBC__)
|
245
|
+
hash = fnv1a_64_iter(hash, gnu_get_libc_version());
|
246
|
+
return (uint32_t)(hash >> 32);
|
214
247
|
#else
|
215
248
|
struct utsname utsname;
|
216
249
|
|
@@ -231,9 +264,12 @@ get_ruby_platform(void)
|
|
231
264
|
* The path will look something like: <cachedir>/12/34567890abcdef
|
232
265
|
*/
|
233
266
|
static void
|
234
|
-
bs_cache_path(const char * cachedir, const char * path, char
|
267
|
+
bs_cache_path(const char * cachedir, const char * path, const char * extra, char (* cache_path)[MAX_CACHEPATH_SIZE])
|
235
268
|
{
|
236
269
|
uint64_t hash = fnv1a_64(path);
|
270
|
+
if (extra) {
|
271
|
+
hash ^= fnv1a_64(extra);
|
272
|
+
}
|
237
273
|
|
238
274
|
uint8_t first_byte = (hash >> (64 - 8));
|
239
275
|
uint64_t remainder = hash & 0x00ffffffffffffff;
|
@@ -268,8 +304,10 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
|
|
268
304
|
* conversions on the ruby VALUE arguments before passing them along.
|
269
305
|
*/
|
270
306
|
static VALUE
|
271
|
-
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
307
|
+
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args)
|
272
308
|
{
|
309
|
+
FilePathValue(path_v);
|
310
|
+
|
273
311
|
Check_Type(cachedir_v, T_STRING);
|
274
312
|
Check_Type(path_v, T_STRING);
|
275
313
|
|
@@ -280,13 +318,16 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
|
280
318
|
char * cachedir = RSTRING_PTR(cachedir_v);
|
281
319
|
char * path = RSTRING_PTR(path_v);
|
282
320
|
char cache_path[MAX_CACHEPATH_SIZE];
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
321
|
+
char * extra = NULL;
|
322
|
+
if (!NIL_P(args)) {
|
323
|
+
VALUE args_serial = rb_marshal_dump(args, Qnil);
|
324
|
+
extra = RSTRING_PTR(args_serial);
|
287
325
|
}
|
288
326
|
|
289
|
-
|
327
|
+
/* generate cache path to cache_path */
|
328
|
+
bs_cache_path(cachedir, path, extra, &cache_path);
|
329
|
+
|
330
|
+
return bs_fetch(path, path_v, cache_path, handler, args);
|
290
331
|
}
|
291
332
|
|
292
333
|
/*
|
@@ -294,14 +335,14 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
|
294
335
|
* was loaded.
|
295
336
|
*/
|
296
337
|
static int
|
297
|
-
open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenance)
|
338
|
+
open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
298
339
|
{
|
299
340
|
struct stat statbuf;
|
300
341
|
int fd;
|
301
342
|
|
302
343
|
fd = open(path, O_RDONLY);
|
303
344
|
if (fd < 0) {
|
304
|
-
*errno_provenance =
|
345
|
+
*errno_provenance = "bs_fetch:open_current_file:open";
|
305
346
|
return fd;
|
306
347
|
}
|
307
348
|
#ifdef _WIN32
|
@@ -309,7 +350,7 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
309
350
|
#endif
|
310
351
|
|
311
352
|
if (fstat(fd, &statbuf) < 0) {
|
312
|
-
*errno_provenance =
|
353
|
+
*errno_provenance = "bs_fetch:open_current_file:fstat";
|
313
354
|
close(fd);
|
314
355
|
return -1;
|
315
356
|
}
|
@@ -355,13 +396,13 @@ bs_read_key(int fd, struct bs_cache_key * key)
|
|
355
396
|
* - ERROR_WITH_ERRNO (-1, errno is set)
|
356
397
|
*/
|
357
398
|
static int
|
358
|
-
open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_provenance)
|
399
|
+
open_cache_file(const char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
359
400
|
{
|
360
401
|
int fd, res;
|
361
402
|
|
362
403
|
fd = open(path, O_RDONLY);
|
363
404
|
if (fd < 0) {
|
364
|
-
*errno_provenance =
|
405
|
+
*errno_provenance = "bs_fetch:open_cache_file:open";
|
365
406
|
if (errno == ENOENT) return CACHE_MISSING_OR_INVALID;
|
366
407
|
return ERROR_WITH_ERRNO;
|
367
408
|
}
|
@@ -371,7 +412,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
371
412
|
|
372
413
|
res = bs_read_key(fd, key);
|
373
414
|
if (res < 0) {
|
374
|
-
*errno_provenance =
|
415
|
+
*errno_provenance = "bs_fetch:open_cache_file:read";
|
375
416
|
close(fd);
|
376
417
|
return res;
|
377
418
|
}
|
@@ -395,7 +436,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
395
436
|
* or exception, will be the final data returnable to the user.
|
396
437
|
*/
|
397
438
|
static int
|
398
|
-
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, char ** errno_provenance)
|
439
|
+
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance)
|
399
440
|
{
|
400
441
|
char * data = NULL;
|
401
442
|
ssize_t nread;
|
@@ -404,7 +445,7 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
404
445
|
VALUE storage_data;
|
405
446
|
|
406
447
|
if (data_size > 100000000000) {
|
407
|
-
*errno_provenance =
|
448
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:datasize";
|
408
449
|
errno = EINVAL; /* because wtf? */
|
409
450
|
ret = -1;
|
410
451
|
goto done;
|
@@ -412,7 +453,7 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
412
453
|
data = ALLOC_N(char, data_size);
|
413
454
|
nread = read(fd, data, data_size);
|
414
455
|
if (nread < 0) {
|
415
|
-
*errno_provenance =
|
456
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:read";
|
416
457
|
ret = -1;
|
417
458
|
goto done;
|
418
459
|
}
|
@@ -421,9 +462,9 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
421
462
|
goto done;
|
422
463
|
}
|
423
464
|
|
424
|
-
storage_data =
|
465
|
+
storage_data = rb_str_new(data, data_size);
|
425
466
|
|
426
|
-
*exception_tag = bs_storage_to_output(handler, storage_data, output_data);
|
467
|
+
*exception_tag = bs_storage_to_output(handler, args, storage_data, output_data);
|
427
468
|
ret = 0;
|
428
469
|
done:
|
429
470
|
if (data != NULL) xfree(data);
|
@@ -464,30 +505,36 @@ mkpath(char * file_path, mode_t mode)
|
|
464
505
|
* path.
|
465
506
|
*/
|
466
507
|
static int
|
467
|
-
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char ** errno_provenance)
|
508
|
+
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, const char ** errno_provenance)
|
468
509
|
{
|
469
510
|
char template[MAX_CACHEPATH_SIZE + 20];
|
470
|
-
char * dest;
|
471
511
|
char * tmp_path;
|
472
|
-
int fd, ret;
|
512
|
+
int fd, ret, attempt;
|
473
513
|
ssize_t nwrite;
|
474
514
|
|
475
|
-
|
476
|
-
|
515
|
+
for (attempt = 0; attempt < MAX_CREATE_TEMPFILE_ATTEMPT; ++attempt) {
|
516
|
+
tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE);
|
517
|
+
strcat(tmp_path, ".tmp.XXXXXX");
|
477
518
|
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
}
|
485
|
-
fd = open(tmp_path, O_WRONLY | O_CREAT, 0664);
|
486
|
-
if (fd < 0) {
|
487
|
-
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:open";
|
519
|
+
// mkstemp modifies the template to be the actual created path
|
520
|
+
fd = mkstemp(tmp_path);
|
521
|
+
if (fd > 0) break;
|
522
|
+
|
523
|
+
if (attempt == 0 && mkpath(tmp_path, 0775) < 0) {
|
524
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkpath";
|
488
525
|
return -1;
|
489
526
|
}
|
490
527
|
}
|
528
|
+
if (fd < 0) {
|
529
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkstemp";
|
530
|
+
return -1;
|
531
|
+
}
|
532
|
+
|
533
|
+
if (chmod(tmp_path, 0644) < 0) {
|
534
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
535
|
+
return -1;
|
536
|
+
}
|
537
|
+
|
491
538
|
#ifdef _WIN32
|
492
539
|
setmode(fd, O_BINARY);
|
493
540
|
#endif
|
@@ -495,11 +542,11 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
495
542
|
key->data_size = RSTRING_LEN(data);
|
496
543
|
nwrite = write(fd, key, KEY_SIZE);
|
497
544
|
if (nwrite < 0) {
|
498
|
-
*errno_provenance =
|
545
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:write";
|
499
546
|
return -1;
|
500
547
|
}
|
501
548
|
if (nwrite != KEY_SIZE) {
|
502
|
-
*errno_provenance =
|
549
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:keysize";
|
503
550
|
errno = EIO; /* Lies but whatever */
|
504
551
|
return -1;
|
505
552
|
}
|
@@ -507,7 +554,7 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
507
554
|
nwrite = write(fd, RSTRING_PTR(data), RSTRING_LEN(data));
|
508
555
|
if (nwrite < 0) return -1;
|
509
556
|
if (nwrite != RSTRING_LEN(data)) {
|
510
|
-
*errno_provenance =
|
557
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:writelength";
|
511
558
|
errno = EIO; /* Lies but whatever */
|
512
559
|
return -1;
|
513
560
|
}
|
@@ -515,38 +562,27 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
515
562
|
close(fd);
|
516
563
|
ret = rename(tmp_path, path);
|
517
564
|
if (ret < 0) {
|
518
|
-
*errno_provenance =
|
565
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:rename";
|
566
|
+
return -1;
|
567
|
+
}
|
568
|
+
ret = chmod(path, 0664 & ~current_umask);
|
569
|
+
if (ret < 0) {
|
570
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
519
571
|
}
|
520
572
|
return ret;
|
521
573
|
}
|
522
574
|
|
523
|
-
/*
|
524
|
-
* Given an errno value (converted to a ruby Fixnum), return the corresponding
|
525
|
-
* Errno::* constant. If none is found, return StandardError instead.
|
526
|
-
*/
|
527
|
-
static VALUE
|
528
|
-
prot_exception_for_errno(VALUE err)
|
529
|
-
{
|
530
|
-
if (err != INT2FIX(0)) {
|
531
|
-
VALUE mErrno = rb_const_get(rb_cObject, rb_intern("Errno"));
|
532
|
-
VALUE constants = rb_funcall(mErrno, rb_intern("constants"), 0);
|
533
|
-
VALUE which = rb_funcall(constants, rb_intern("[]"), 1, err);
|
534
|
-
return rb_funcall(mErrno, rb_intern("const_get"), 1, which);
|
535
|
-
}
|
536
|
-
return rb_eStandardError;
|
537
|
-
}
|
538
|
-
|
539
575
|
|
540
576
|
/* Read contents from an fd, whose contents are asserted to be +size+ bytes
|
541
577
|
* long, into a buffer */
|
542
578
|
static ssize_t
|
543
|
-
bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance)
|
579
|
+
bs_read_contents(int fd, size_t size, char ** contents, const char ** errno_provenance)
|
544
580
|
{
|
545
581
|
ssize_t nread;
|
546
582
|
*contents = ALLOC_N(char, size);
|
547
583
|
nread = read(fd, *contents, size);
|
548
584
|
if (nread < 0) {
|
549
|
-
*errno_provenance =
|
585
|
+
*errno_provenance = "bs_fetch:bs_read_contents:read";
|
550
586
|
}
|
551
587
|
return nread;
|
552
588
|
}
|
@@ -596,13 +632,13 @@ bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance
|
|
596
632
|
* - Return storage_to_output(storage_data)
|
597
633
|
*/
|
598
634
|
static VALUE
|
599
|
-
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
635
|
+
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args)
|
600
636
|
{
|
601
637
|
struct bs_cache_key cached_key, current_key;
|
602
638
|
char * contents = NULL;
|
603
639
|
int cache_fd = -1, current_fd = -1;
|
604
640
|
int res, valid_cache = 0, exception_tag = 0;
|
605
|
-
char * errno_provenance = NULL;
|
641
|
+
const char * errno_provenance = NULL;
|
606
642
|
|
607
643
|
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
608
644
|
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
@@ -629,7 +665,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
629
665
|
if (valid_cache) {
|
630
666
|
/* Fetch the cache data and return it if we're able to load it successfully */
|
631
667
|
res = fetch_cached_data(
|
632
|
-
cache_fd, (ssize_t)cached_key.data_size, handler,
|
668
|
+
cache_fd, (ssize_t)cached_key.data_size, handler, args,
|
633
669
|
&output_data, &exception_tag, &errno_provenance
|
634
670
|
);
|
635
671
|
if (exception_tag != 0) goto raise;
|
@@ -643,15 +679,15 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
643
679
|
|
644
680
|
/* Read the contents of the source file into a buffer */
|
645
681
|
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
|
646
|
-
input_data =
|
682
|
+
input_data = rb_str_new(contents, current_key.size);
|
647
683
|
|
648
684
|
/* Try to compile the input_data using input_to_storage(input_data) */
|
649
|
-
exception_tag = bs_input_to_storage(handler, input_data, path_v, &storage_data);
|
685
|
+
exception_tag = bs_input_to_storage(handler, args, input_data, path_v, &storage_data);
|
650
686
|
if (exception_tag != 0) goto raise;
|
651
687
|
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
652
688
|
* to cache anything; just return input_to_output(input_data) */
|
653
689
|
if (storage_data == uncompilable) {
|
654
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
690
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
655
691
|
if (exception_tag != 0) goto raise;
|
656
692
|
goto succeed;
|
657
693
|
}
|
@@ -663,17 +699,17 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
663
699
|
if (res < 0) goto fail_errno;
|
664
700
|
|
665
701
|
/* Having written the cache, now convert storage_data to output_data */
|
666
|
-
exception_tag = bs_storage_to_output(handler, storage_data, &output_data);
|
702
|
+
exception_tag = bs_storage_to_output(handler, args, storage_data, &output_data);
|
667
703
|
if (exception_tag != 0) goto raise;
|
668
704
|
|
669
705
|
/* If output_data is nil, delete the cache entry and generate the output
|
670
706
|
* using input_to_output */
|
671
707
|
if (NIL_P(output_data)) {
|
672
708
|
if (unlink(cache_path) < 0) {
|
673
|
-
errno_provenance =
|
709
|
+
errno_provenance = "bs_fetch:unlink";
|
674
710
|
goto fail_errno;
|
675
711
|
}
|
676
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
712
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
677
713
|
if (exception_tag != 0) goto raise;
|
678
714
|
}
|
679
715
|
|
@@ -689,11 +725,7 @@ succeed:
|
|
689
725
|
return output_data;
|
690
726
|
fail_errno:
|
691
727
|
CLEANUP;
|
692
|
-
exception =
|
693
|
-
if (res) exception = rb_eStandardError;
|
694
|
-
if (errno_provenance != NULL) {
|
695
|
-
exception = rb_exc_new_str(exception, rb_str_new2(errno_provenance));
|
696
|
-
}
|
728
|
+
exception = rb_syserr_new(errno, errno_provenance);
|
697
729
|
rb_exc_raise(exception);
|
698
730
|
__builtin_unreachable();
|
699
731
|
raise:
|
@@ -727,16 +759,19 @@ invalid_type_storage_data:
|
|
727
759
|
|
728
760
|
struct s2o_data {
|
729
761
|
VALUE handler;
|
762
|
+
VALUE args;
|
730
763
|
VALUE storage_data;
|
731
764
|
};
|
732
765
|
|
733
766
|
struct i2o_data {
|
734
767
|
VALUE handler;
|
768
|
+
VALUE args;
|
735
769
|
VALUE input_data;
|
736
770
|
};
|
737
771
|
|
738
772
|
struct i2s_data {
|
739
773
|
VALUE handler;
|
774
|
+
VALUE args;
|
740
775
|
VALUE input_data;
|
741
776
|
VALUE pathval;
|
742
777
|
};
|
@@ -745,15 +780,16 @@ static VALUE
|
|
745
780
|
prot_storage_to_output(VALUE arg)
|
746
781
|
{
|
747
782
|
struct s2o_data * data = (struct s2o_data *)arg;
|
748
|
-
return rb_funcall(data->handler, rb_intern("storage_to_output"),
|
783
|
+
return rb_funcall(data->handler, rb_intern("storage_to_output"), 2, data->storage_data, data->args);
|
749
784
|
}
|
750
785
|
|
751
786
|
static int
|
752
|
-
bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
|
787
|
+
bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data)
|
753
788
|
{
|
754
789
|
int state;
|
755
790
|
struct s2o_data s2o_data = {
|
756
791
|
.handler = handler,
|
792
|
+
.args = args,
|
757
793
|
.storage_data = storage_data,
|
758
794
|
};
|
759
795
|
*output_data = rb_protect(prot_storage_to_output, (VALUE)&s2o_data, &state);
|
@@ -761,10 +797,11 @@ bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
|
|
761
797
|
}
|
762
798
|
|
763
799
|
static void
|
764
|
-
bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag)
|
800
|
+
bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag)
|
765
801
|
{
|
766
802
|
struct i2o_data i2o_data = {
|
767
803
|
.handler = handler,
|
804
|
+
.args = args,
|
768
805
|
.input_data = input_data,
|
769
806
|
};
|
770
807
|
*output_data = rb_protect(prot_input_to_output, (VALUE)&i2o_data, exception_tag);
|
@@ -774,18 +811,18 @@ static VALUE
|
|
774
811
|
prot_input_to_output(VALUE arg)
|
775
812
|
{
|
776
813
|
struct i2o_data * data = (struct i2o_data *)arg;
|
777
|
-
return rb_funcall(data->handler, rb_intern("input_to_output"),
|
814
|
+
return rb_funcall(data->handler, rb_intern("input_to_output"), 2, data->input_data, data->args);
|
778
815
|
}
|
779
816
|
|
780
817
|
static VALUE
|
781
818
|
try_input_to_storage(VALUE arg)
|
782
819
|
{
|
783
820
|
struct i2s_data * data = (struct i2s_data *)arg;
|
784
|
-
return rb_funcall(data->handler, rb_intern("input_to_storage"),
|
821
|
+
return rb_funcall(data->handler, rb_intern("input_to_storage"), 3, data->input_data, data->pathval, data->args);
|
785
822
|
}
|
786
823
|
|
787
824
|
static VALUE
|
788
|
-
rescue_input_to_storage(VALUE arg)
|
825
|
+
rescue_input_to_storage(VALUE arg, VALUE e)
|
789
826
|
{
|
790
827
|
return uncompilable;
|
791
828
|
}
|
@@ -801,11 +838,12 @@ prot_input_to_storage(VALUE arg)
|
|
801
838
|
}
|
802
839
|
|
803
840
|
static int
|
804
|
-
bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
841
|
+
bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
805
842
|
{
|
806
843
|
int state;
|
807
844
|
struct i2s_data i2s_data = {
|
808
845
|
.handler = handler,
|
846
|
+
.args = args,
|
809
847
|
.input_data = input_data,
|
810
848
|
.pathval = pathval,
|
811
849
|
};
|