bootsnap 1.4.5 → 1.16.0
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/CHANGELOG.md +227 -0
- data/LICENSE.txt +1 -1
- data/README.md +57 -20
- data/exe/bootsnap +5 -0
- data/ext/bootsnap/bootsnap.c +301 -155
- data/ext/bootsnap/extconf.rb +22 -14
- data/lib/bootsnap/bundler.rb +2 -0
- data/lib/bootsnap/cli/worker_pool.rb +136 -0
- data/lib/bootsnap/cli.rb +281 -0
- data/lib/bootsnap/compile_cache/iseq.rb +64 -19
- data/lib/bootsnap/compile_cache/json.rb +93 -0
- data/lib/bootsnap/compile_cache/yaml.rb +333 -42
- data/lib/bootsnap/compile_cache.rb +26 -8
- data/lib/bootsnap/explicit_require.rb +5 -3
- data/lib/bootsnap/load_path_cache/cache.rb +65 -37
- data/lib/bootsnap/load_path_cache/change_observer.rb +19 -3
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +28 -83
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +2 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +63 -29
- data/lib/bootsnap/load_path_cache/path.rb +42 -19
- data/lib/bootsnap/load_path_cache/path_scanner.rb +60 -29
- data/lib/bootsnap/load_path_cache/store.rb +64 -23
- data/lib/bootsnap/load_path_cache.rb +31 -38
- data/lib/bootsnap/setup.rb +3 -36
- data/lib/bootsnap/version.rb +3 -1
- data/lib/bootsnap.rb +127 -36
- metadata +15 -99
- data/.github/CODEOWNERS +0 -2
- data/.github/probots.yml +0 -2
- data/.gitignore +0 -17
- data/.rubocop.yml +0 -20
- data/.travis.yml +0 -21
- data/CODE_OF_CONDUCT.md +0 -74
- data/CONTRIBUTING.md +0 -21
- data/Gemfile +0 -8
- data/README.jp.md +0 -231
- data/Rakefile +0 -12
- data/bin/ci +0 -10
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/bin/test-minimal-support +0 -7
- data/bin/testunit +0 -8
- data/bootsnap.gemspec +0 -45
- data/dev.yml +0 -10
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +0 -106
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +0 -32
- data/shipit.rubygems.yml +0 -0
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -14,13 +14,11 @@
|
|
14
14
|
#include "bootsnap.h"
|
15
15
|
#include "ruby.h"
|
16
16
|
#include <stdint.h>
|
17
|
+
#include <stdbool.h>
|
17
18
|
#include <sys/types.h>
|
18
19
|
#include <errno.h>
|
19
20
|
#include <fcntl.h>
|
20
21
|
#include <sys/stat.h>
|
21
|
-
#ifndef _WIN32
|
22
|
-
#include <sys/utsname.h>
|
23
|
-
#endif
|
24
22
|
|
25
23
|
/* 1000 is an arbitrary limit; FNV64 plus some slashes brings the cap down to
|
26
24
|
* 981 for the cache dir */
|
@@ -29,6 +27,12 @@
|
|
29
27
|
|
30
28
|
#define KEY_SIZE 64
|
31
29
|
|
30
|
+
#define MAX_CREATE_TEMPFILE_ATTEMPT 3
|
31
|
+
|
32
|
+
#ifndef RB_UNLIKELY
|
33
|
+
#define RB_UNLIKELY(x) (x)
|
34
|
+
#endif
|
35
|
+
|
32
36
|
/*
|
33
37
|
* An instance of this key is written as the first 64 bytes of each cache file.
|
34
38
|
* The mtime and size members track whether the file contents have changed, and
|
@@ -65,7 +69,7 @@ struct bs_cache_key {
|
|
65
69
|
STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
|
66
70
|
|
67
71
|
/* Effectively a schema version. Bumping invalidates all previous caches */
|
68
|
-
static const uint32_t current_version =
|
72
|
+
static const uint32_t current_version = 4;
|
69
73
|
|
70
74
|
/* hash of e.g. "x86_64-darwin17", invalidating when ruby is recompiled on a
|
71
75
|
* new OS ABI, etc. */
|
@@ -81,21 +85,28 @@ static mode_t current_umask;
|
|
81
85
|
static VALUE rb_mBootsnap;
|
82
86
|
static VALUE rb_mBootsnap_CompileCache;
|
83
87
|
static VALUE rb_mBootsnap_CompileCache_Native;
|
84
|
-
static VALUE
|
85
|
-
static ID
|
88
|
+
static VALUE rb_cBootsnap_CompileCache_UNCOMPILABLE;
|
89
|
+
static ID instrumentation_method;
|
90
|
+
static VALUE sym_miss;
|
91
|
+
static VALUE sym_stale;
|
92
|
+
static bool instrumentation_enabled = false;
|
93
|
+
static bool readonly = false;
|
86
94
|
|
87
95
|
/* Functions exposed as module functions on Bootsnap::CompileCache::Native */
|
96
|
+
static VALUE bs_instrumentation_enabled_set(VALUE self, VALUE enabled);
|
97
|
+
static VALUE bs_readonly_set(VALUE self, VALUE enabled);
|
88
98
|
static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
|
89
|
-
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
99
|
+
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
|
100
|
+
static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
90
101
|
|
91
102
|
/* Helpers */
|
92
|
-
static
|
93
|
-
static void bs_cache_path(const char * cachedir, const char * path, char ** cache_path);
|
103
|
+
static void bs_cache_path(const char * cachedir, const VALUE path, char (* cache_path)[MAX_CACHEPATH_SIZE]);
|
94
104
|
static int bs_read_key(int fd, struct bs_cache_key * key);
|
95
105
|
static int cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2);
|
96
|
-
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
97
|
-
static
|
98
|
-
static int
|
106
|
+
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args);
|
107
|
+
static VALUE bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
108
|
+
static int open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance);
|
109
|
+
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);
|
99
110
|
static uint32_t get_ruby_revision(void);
|
100
111
|
static uint32_t get_ruby_platform(void);
|
101
112
|
|
@@ -103,12 +114,10 @@ static uint32_t get_ruby_platform(void);
|
|
103
114
|
* Helper functions to call ruby methods on handler object without crashing on
|
104
115
|
* exception.
|
105
116
|
*/
|
106
|
-
static int bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data);
|
107
|
-
static VALUE prot_storage_to_output(VALUE arg);
|
117
|
+
static int bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data);
|
108
118
|
static VALUE prot_input_to_output(VALUE arg);
|
109
|
-
static void bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag);
|
110
|
-
static
|
111
|
-
static int bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
119
|
+
static void bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag);
|
120
|
+
static int bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
112
121
|
struct s2o_data;
|
113
122
|
struct i2o_data;
|
114
123
|
struct i2s_data;
|
@@ -122,6 +131,12 @@ bs_rb_coverage_running(VALUE self)
|
|
122
131
|
return RTEST(cov) ? Qtrue : Qfalse;
|
123
132
|
}
|
124
133
|
|
134
|
+
static VALUE
|
135
|
+
bs_rb_get_path(VALUE self, VALUE fname)
|
136
|
+
{
|
137
|
+
return rb_get_path(fname);
|
138
|
+
}
|
139
|
+
|
125
140
|
/*
|
126
141
|
* Ruby C extensions are initialized by calling Init_<extname>.
|
127
142
|
*
|
@@ -133,23 +148,50 @@ void
|
|
133
148
|
Init_bootsnap(void)
|
134
149
|
{
|
135
150
|
rb_mBootsnap = rb_define_module("Bootsnap");
|
151
|
+
|
152
|
+
rb_define_singleton_method(rb_mBootsnap, "rb_get_path", bs_rb_get_path, 1);
|
153
|
+
|
136
154
|
rb_mBootsnap_CompileCache = rb_define_module_under(rb_mBootsnap, "CompileCache");
|
137
155
|
rb_mBootsnap_CompileCache_Native = rb_define_module_under(rb_mBootsnap_CompileCache, "Native");
|
138
|
-
|
156
|
+
rb_cBootsnap_CompileCache_UNCOMPILABLE = rb_const_get(rb_mBootsnap_CompileCache, rb_intern("UNCOMPILABLE"));
|
157
|
+
rb_global_variable(&rb_cBootsnap_CompileCache_UNCOMPILABLE);
|
139
158
|
|
140
159
|
current_ruby_revision = get_ruby_revision();
|
141
160
|
current_ruby_platform = get_ruby_platform();
|
142
161
|
|
143
|
-
|
162
|
+
instrumentation_method = rb_intern("_instrument");
|
163
|
+
|
164
|
+
sym_miss = ID2SYM(rb_intern("miss"));
|
165
|
+
rb_global_variable(&sym_miss);
|
166
|
+
|
167
|
+
sym_stale = ID2SYM(rb_intern("stale"));
|
168
|
+
rb_global_variable(&sym_stale);
|
144
169
|
|
170
|
+
rb_define_module_function(rb_mBootsnap, "instrumentation_enabled=", bs_instrumentation_enabled_set, 1);
|
171
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "readonly=", bs_readonly_set, 1);
|
145
172
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
|
146
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch,
|
173
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
|
174
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile, 3);
|
147
175
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
|
148
176
|
|
149
177
|
current_umask = umask(0777);
|
150
178
|
umask(current_umask);
|
151
179
|
}
|
152
180
|
|
181
|
+
static VALUE
|
182
|
+
bs_instrumentation_enabled_set(VALUE self, VALUE enabled)
|
183
|
+
{
|
184
|
+
instrumentation_enabled = RTEST(enabled);
|
185
|
+
return enabled;
|
186
|
+
}
|
187
|
+
|
188
|
+
static VALUE
|
189
|
+
bs_readonly_set(VALUE self, VALUE enabled)
|
190
|
+
{
|
191
|
+
readonly = RTEST(enabled);
|
192
|
+
return enabled;
|
193
|
+
}
|
194
|
+
|
153
195
|
/*
|
154
196
|
* Bootsnap's ruby code registers a hook that notifies us via this function
|
155
197
|
* when compile_option changes. These changes invalidate all existing caches.
|
@@ -167,22 +209,13 @@ bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
|
|
167
209
|
return Qnil;
|
168
210
|
}
|
169
211
|
|
170
|
-
/*
|
171
|
-
* We use FNV1a-64 to derive cache paths. The choice is somewhat arbitrary but
|
172
|
-
* it has several nice properties:
|
173
|
-
*
|
174
|
-
* - Tiny implementation
|
175
|
-
* - No external dependency
|
176
|
-
* - Solid performance
|
177
|
-
* - Solid randomness
|
178
|
-
* - 32 bits doesn't feel collision-resistant enough; 64 is nice.
|
179
|
-
*/
|
180
212
|
static uint64_t
|
181
|
-
fnv1a_64_iter(uint64_t h, const
|
213
|
+
fnv1a_64_iter(uint64_t h, const VALUE str)
|
182
214
|
{
|
183
|
-
unsigned char *s = (unsigned char *)str;
|
215
|
+
unsigned char *s = (unsigned char *)RSTRING_PTR(str);
|
216
|
+
unsigned char *str_end = (unsigned char *)RSTRING_PTR(str) + RSTRING_LEN(str);
|
184
217
|
|
185
|
-
while (
|
218
|
+
while (s < str_end) {
|
186
219
|
h ^= (uint64_t)*s++;
|
187
220
|
h += (h << 1) + (h << 4) + (h << 5) + (h << 7) + (h << 8) + (h << 40);
|
188
221
|
}
|
@@ -191,7 +224,7 @@ fnv1a_64_iter(uint64_t h, const char *str)
|
|
191
224
|
}
|
192
225
|
|
193
226
|
static uint64_t
|
194
|
-
fnv1a_64(const
|
227
|
+
fnv1a_64(const VALUE str)
|
195
228
|
{
|
196
229
|
uint64_t h = (uint64_t)0xcbf29ce484222325ULL;
|
197
230
|
return fnv1a_64_iter(h, str);
|
@@ -212,7 +245,7 @@ get_ruby_revision(void)
|
|
212
245
|
} else {
|
213
246
|
uint64_t hash;
|
214
247
|
|
215
|
-
hash = fnv1a_64(
|
248
|
+
hash = fnv1a_64(ruby_revision);
|
216
249
|
return (uint32_t)(hash >> 32);
|
217
250
|
}
|
218
251
|
}
|
@@ -220,10 +253,6 @@ get_ruby_revision(void)
|
|
220
253
|
/*
|
221
254
|
* When ruby's version doesn't change, but it's recompiled on a different OS
|
222
255
|
* (or OS version), we need to invalidate the cache.
|
223
|
-
*
|
224
|
-
* We actually factor in some extra information here, to be extra confident
|
225
|
-
* that we don't try to re-use caches that will not be compatible, by factoring
|
226
|
-
* in utsname.version.
|
227
256
|
*/
|
228
257
|
static uint32_t
|
229
258
|
get_ruby_platform(void)
|
@@ -232,20 +261,8 @@ get_ruby_platform(void)
|
|
232
261
|
VALUE ruby_platform;
|
233
262
|
|
234
263
|
ruby_platform = rb_const_get(rb_cObject, rb_intern("RUBY_PLATFORM"));
|
235
|
-
hash = fnv1a_64(
|
236
|
-
|
237
|
-
#ifdef _WIN32
|
238
|
-
return (uint32_t)(hash >> 32) ^ (uint32_t)GetVersion();
|
239
|
-
#else
|
240
|
-
struct utsname utsname;
|
241
|
-
|
242
|
-
/* Not worth crashing if this fails; lose extra cache invalidation potential */
|
243
|
-
if (uname(&utsname) >= 0) {
|
244
|
-
hash = fnv1a_64_iter(hash, utsname.version);
|
245
|
-
}
|
246
|
-
|
264
|
+
hash = fnv1a_64(ruby_platform);
|
247
265
|
return (uint32_t)(hash >> 32);
|
248
|
-
#endif
|
249
266
|
}
|
250
267
|
|
251
268
|
/*
|
@@ -256,14 +273,13 @@ get_ruby_platform(void)
|
|
256
273
|
* The path will look something like: <cachedir>/12/34567890abcdef
|
257
274
|
*/
|
258
275
|
static void
|
259
|
-
bs_cache_path(const char * cachedir, const
|
276
|
+
bs_cache_path(const char * cachedir, const VALUE path, char (* cache_path)[MAX_CACHEPATH_SIZE])
|
260
277
|
{
|
261
278
|
uint64_t hash = fnv1a_64(path);
|
262
|
-
|
263
279
|
uint8_t first_byte = (hash >> (64 - 8));
|
264
280
|
uint64_t remainder = hash & 0x00ffffffffffffff;
|
265
281
|
|
266
|
-
sprintf(*cache_path, "%s/%
|
282
|
+
sprintf(*cache_path, "%s/%02"PRIx8"/%014"PRIx64, cachedir, first_byte, remainder);
|
267
283
|
}
|
268
284
|
|
269
285
|
/*
|
@@ -293,7 +309,7 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
|
|
293
309
|
* conversions on the ruby VALUE arguments before passing them along.
|
294
310
|
*/
|
295
311
|
static VALUE
|
296
|
-
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
312
|
+
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args)
|
297
313
|
{
|
298
314
|
FilePathValue(path_v);
|
299
315
|
|
@@ -308,27 +324,51 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
|
308
324
|
char * path = RSTRING_PTR(path_v);
|
309
325
|
char cache_path[MAX_CACHEPATH_SIZE];
|
310
326
|
|
311
|
-
|
312
|
-
|
313
|
-
bs_cache_path(cachedir, path, &tmp);
|
314
|
-
}
|
327
|
+
/* generate cache path to cache_path */
|
328
|
+
bs_cache_path(cachedir, path_v, &cache_path);
|
315
329
|
|
316
|
-
return bs_fetch(path, path_v, cache_path, handler);
|
330
|
+
return bs_fetch(path, path_v, cache_path, handler, args);
|
317
331
|
}
|
318
332
|
|
333
|
+
/*
|
334
|
+
* Entrypoint for Bootsnap::CompileCache::Native.precompile.
|
335
|
+
* Similar to fetch, but it only generate the cache if missing
|
336
|
+
* and doesn't return the content.
|
337
|
+
*/
|
338
|
+
static VALUE
|
339
|
+
bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
340
|
+
{
|
341
|
+
FilePathValue(path_v);
|
342
|
+
|
343
|
+
Check_Type(cachedir_v, T_STRING);
|
344
|
+
Check_Type(path_v, T_STRING);
|
345
|
+
|
346
|
+
if (RSTRING_LEN(cachedir_v) > MAX_CACHEDIR_SIZE) {
|
347
|
+
rb_raise(rb_eArgError, "cachedir too long");
|
348
|
+
}
|
349
|
+
|
350
|
+
char * cachedir = RSTRING_PTR(cachedir_v);
|
351
|
+
char * path = RSTRING_PTR(path_v);
|
352
|
+
char cache_path[MAX_CACHEPATH_SIZE];
|
353
|
+
|
354
|
+
/* generate cache path to cache_path */
|
355
|
+
bs_cache_path(cachedir, path_v, &cache_path);
|
356
|
+
|
357
|
+
return bs_precompile(path, path_v, cache_path, handler);
|
358
|
+
}
|
319
359
|
/*
|
320
360
|
* Open the file we want to load/cache and generate a cache key for it if it
|
321
361
|
* was loaded.
|
322
362
|
*/
|
323
363
|
static int
|
324
|
-
open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenance)
|
364
|
+
open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
325
365
|
{
|
326
366
|
struct stat statbuf;
|
327
367
|
int fd;
|
328
368
|
|
329
369
|
fd = open(path, O_RDONLY);
|
330
370
|
if (fd < 0) {
|
331
|
-
*errno_provenance =
|
371
|
+
*errno_provenance = "bs_fetch:open_current_file:open";
|
332
372
|
return fd;
|
333
373
|
}
|
334
374
|
#ifdef _WIN32
|
@@ -336,7 +376,7 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
336
376
|
#endif
|
337
377
|
|
338
378
|
if (fstat(fd, &statbuf) < 0) {
|
339
|
-
*errno_provenance =
|
379
|
+
*errno_provenance = "bs_fetch:open_current_file:fstat";
|
340
380
|
close(fd);
|
341
381
|
return -1;
|
342
382
|
}
|
@@ -352,7 +392,9 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
352
392
|
}
|
353
393
|
|
354
394
|
#define ERROR_WITH_ERRNO -1
|
355
|
-
#define
|
395
|
+
#define CACHE_MISS -2
|
396
|
+
#define CACHE_STALE -3
|
397
|
+
#define CACHE_UNCOMPILABLE -4
|
356
398
|
|
357
399
|
/*
|
358
400
|
* Read the cache key from the given fd, which must have position 0 (e.g.
|
@@ -360,15 +402,16 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
360
402
|
*
|
361
403
|
* Possible return values:
|
362
404
|
* - 0 (OK, key was loaded)
|
363
|
-
* - CACHE_MISSING_OR_INVALID (-2)
|
364
405
|
* - ERROR_WITH_ERRNO (-1, errno is set)
|
406
|
+
* - CACHE_MISS (-2)
|
407
|
+
* - CACHE_STALE (-3)
|
365
408
|
*/
|
366
409
|
static int
|
367
410
|
bs_read_key(int fd, struct bs_cache_key * key)
|
368
411
|
{
|
369
412
|
ssize_t nread = read(fd, key, KEY_SIZE);
|
370
413
|
if (nread < 0) return ERROR_WITH_ERRNO;
|
371
|
-
if (nread < KEY_SIZE) return
|
414
|
+
if (nread < KEY_SIZE) return CACHE_STALE;
|
372
415
|
return 0;
|
373
416
|
}
|
374
417
|
|
@@ -378,19 +421,19 @@ bs_read_key(int fd, struct bs_cache_key * key)
|
|
378
421
|
*
|
379
422
|
* Possible return values:
|
380
423
|
* - 0 (OK, key was loaded)
|
381
|
-
* -
|
424
|
+
* - CACHE_MISS (-2)
|
425
|
+
* - CACHE_STALE (-3)
|
382
426
|
* - ERROR_WITH_ERRNO (-1, errno is set)
|
383
427
|
*/
|
384
428
|
static int
|
385
|
-
open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_provenance)
|
429
|
+
open_cache_file(const char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
386
430
|
{
|
387
431
|
int fd, res;
|
388
432
|
|
389
433
|
fd = open(path, O_RDONLY);
|
390
434
|
if (fd < 0) {
|
391
|
-
*errno_provenance =
|
392
|
-
|
393
|
-
return ERROR_WITH_ERRNO;
|
435
|
+
*errno_provenance = "bs_fetch:open_cache_file:open";
|
436
|
+
return CACHE_MISS;
|
394
437
|
}
|
395
438
|
#ifdef _WIN32
|
396
439
|
setmode(fd, O_BINARY);
|
@@ -398,7 +441,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
398
441
|
|
399
442
|
res = bs_read_key(fd, key);
|
400
443
|
if (res < 0) {
|
401
|
-
*errno_provenance =
|
444
|
+
*errno_provenance = "bs_fetch:open_cache_file:read";
|
402
445
|
close(fd);
|
403
446
|
return res;
|
404
447
|
}
|
@@ -422,7 +465,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
422
465
|
* or exception, will be the final data returnable to the user.
|
423
466
|
*/
|
424
467
|
static int
|
425
|
-
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, char ** errno_provenance)
|
468
|
+
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance)
|
426
469
|
{
|
427
470
|
char * data = NULL;
|
428
471
|
ssize_t nread;
|
@@ -431,26 +474,30 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
431
474
|
VALUE storage_data;
|
432
475
|
|
433
476
|
if (data_size > 100000000000) {
|
434
|
-
*errno_provenance =
|
477
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:datasize";
|
435
478
|
errno = EINVAL; /* because wtf? */
|
436
|
-
ret =
|
479
|
+
ret = ERROR_WITH_ERRNO;
|
437
480
|
goto done;
|
438
481
|
}
|
439
482
|
data = ALLOC_N(char, data_size);
|
440
483
|
nread = read(fd, data, data_size);
|
441
484
|
if (nread < 0) {
|
442
|
-
*errno_provenance =
|
443
|
-
ret =
|
485
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:read";
|
486
|
+
ret = ERROR_WITH_ERRNO;
|
444
487
|
goto done;
|
445
488
|
}
|
446
489
|
if (nread != data_size) {
|
447
|
-
ret =
|
490
|
+
ret = CACHE_STALE;
|
448
491
|
goto done;
|
449
492
|
}
|
450
493
|
|
451
|
-
storage_data =
|
494
|
+
storage_data = rb_str_new(data, data_size);
|
452
495
|
|
453
|
-
*exception_tag = bs_storage_to_output(handler, storage_data, output_data);
|
496
|
+
*exception_tag = bs_storage_to_output(handler, args, storage_data, output_data);
|
497
|
+
if (*output_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
498
|
+
ret = CACHE_UNCOMPILABLE;
|
499
|
+
goto done;
|
500
|
+
}
|
454
501
|
ret = 0;
|
455
502
|
done:
|
456
503
|
if (data != NULL) xfree(data);
|
@@ -491,29 +538,36 @@ mkpath(char * file_path, mode_t mode)
|
|
491
538
|
* path.
|
492
539
|
*/
|
493
540
|
static int
|
494
|
-
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char ** errno_provenance)
|
541
|
+
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, const char ** errno_provenance)
|
495
542
|
{
|
496
543
|
char template[MAX_CACHEPATH_SIZE + 20];
|
497
544
|
char * tmp_path;
|
498
|
-
int fd, ret;
|
545
|
+
int fd, ret, attempt;
|
499
546
|
ssize_t nwrite;
|
500
547
|
|
501
|
-
|
502
|
-
|
548
|
+
for (attempt = 0; attempt < MAX_CREATE_TEMPFILE_ATTEMPT; ++attempt) {
|
549
|
+
tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE);
|
550
|
+
strcat(tmp_path, ".tmp.XXXXXX");
|
503
551
|
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
}
|
511
|
-
fd = open(tmp_path, O_WRONLY | O_CREAT, 0664);
|
512
|
-
if (fd < 0) {
|
513
|
-
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:open";
|
552
|
+
// mkstemp modifies the template to be the actual created path
|
553
|
+
fd = mkstemp(tmp_path);
|
554
|
+
if (fd > 0) break;
|
555
|
+
|
556
|
+
if (attempt == 0 && mkpath(tmp_path, 0775) < 0) {
|
557
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkpath";
|
514
558
|
return -1;
|
515
559
|
}
|
516
560
|
}
|
561
|
+
if (fd < 0) {
|
562
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkstemp";
|
563
|
+
return -1;
|
564
|
+
}
|
565
|
+
|
566
|
+
if (chmod(tmp_path, 0644) < 0) {
|
567
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
568
|
+
return -1;
|
569
|
+
}
|
570
|
+
|
517
571
|
#ifdef _WIN32
|
518
572
|
setmode(fd, O_BINARY);
|
519
573
|
#endif
|
@@ -521,11 +575,11 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
521
575
|
key->data_size = RSTRING_LEN(data);
|
522
576
|
nwrite = write(fd, key, KEY_SIZE);
|
523
577
|
if (nwrite < 0) {
|
524
|
-
*errno_provenance =
|
578
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:write";
|
525
579
|
return -1;
|
526
580
|
}
|
527
581
|
if (nwrite != KEY_SIZE) {
|
528
|
-
*errno_provenance =
|
582
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:keysize";
|
529
583
|
errno = EIO; /* Lies but whatever */
|
530
584
|
return -1;
|
531
585
|
}
|
@@ -533,7 +587,7 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
533
587
|
nwrite = write(fd, RSTRING_PTR(data), RSTRING_LEN(data));
|
534
588
|
if (nwrite < 0) return -1;
|
535
589
|
if (nwrite != RSTRING_LEN(data)) {
|
536
|
-
*errno_provenance =
|
590
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:writelength";
|
537
591
|
errno = EIO; /* Lies but whatever */
|
538
592
|
return -1;
|
539
593
|
}
|
@@ -541,12 +595,12 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
541
595
|
close(fd);
|
542
596
|
ret = rename(tmp_path, path);
|
543
597
|
if (ret < 0) {
|
544
|
-
*errno_provenance =
|
598
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:rename";
|
545
599
|
return -1;
|
546
600
|
}
|
547
601
|
ret = chmod(path, 0664 & ~current_umask);
|
548
602
|
if (ret < 0) {
|
549
|
-
*errno_provenance =
|
603
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
550
604
|
}
|
551
605
|
return ret;
|
552
606
|
}
|
@@ -555,13 +609,13 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
555
609
|
/* Read contents from an fd, whose contents are asserted to be +size+ bytes
|
556
610
|
* long, into a buffer */
|
557
611
|
static ssize_t
|
558
|
-
bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance)
|
612
|
+
bs_read_contents(int fd, size_t size, char ** contents, const char ** errno_provenance)
|
559
613
|
{
|
560
614
|
ssize_t nread;
|
561
615
|
*contents = ALLOC_N(char, size);
|
562
616
|
nread = read(fd, *contents, size);
|
563
617
|
if (nread < 0) {
|
564
|
-
*errno_provenance =
|
618
|
+
*errno_provenance = "bs_fetch:bs_read_contents:read";
|
565
619
|
}
|
566
620
|
return nread;
|
567
621
|
}
|
@@ -611,13 +665,13 @@ bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance
|
|
611
665
|
* - Return storage_to_output(storage_data)
|
612
666
|
*/
|
613
667
|
static VALUE
|
614
|
-
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
668
|
+
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args)
|
615
669
|
{
|
616
670
|
struct bs_cache_key cached_key, current_key;
|
617
671
|
char * contents = NULL;
|
618
672
|
int cache_fd = -1, current_fd = -1;
|
619
673
|
int res, valid_cache = 0, exception_tag = 0;
|
620
|
-
char * errno_provenance = NULL;
|
674
|
+
const char * errno_provenance = NULL;
|
621
675
|
|
622
676
|
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
623
677
|
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
@@ -631,26 +685,42 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
631
685
|
|
632
686
|
/* Open the cache key if it exists, and read its cache key in */
|
633
687
|
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
|
634
|
-
if (cache_fd ==
|
688
|
+
if (cache_fd == CACHE_MISS || cache_fd == CACHE_STALE) {
|
635
689
|
/* This is ok: valid_cache remains false, we re-populate it. */
|
690
|
+
if (RB_UNLIKELY(instrumentation_enabled)) {
|
691
|
+
rb_funcall(rb_mBootsnap, instrumentation_method, 2, cache_fd == CACHE_MISS ? sym_miss : sym_stale, path_v);
|
692
|
+
}
|
636
693
|
} else if (cache_fd < 0) {
|
637
694
|
goto fail_errno;
|
638
695
|
} else {
|
639
696
|
/* True if the cache existed and no invalidating changes have occurred since
|
640
697
|
* it was generated. */
|
641
698
|
valid_cache = cache_key_equal(¤t_key, &cached_key);
|
699
|
+
if (RB_UNLIKELY(instrumentation_enabled)) {
|
700
|
+
if (!valid_cache) {
|
701
|
+
rb_funcall(rb_mBootsnap, instrumentation_method, 2, sym_stale, path_v);
|
702
|
+
}
|
703
|
+
}
|
642
704
|
}
|
643
705
|
|
644
706
|
if (valid_cache) {
|
645
707
|
/* Fetch the cache data and return it if we're able to load it successfully */
|
646
708
|
res = fetch_cached_data(
|
647
|
-
cache_fd, (ssize_t)cached_key.data_size, handler,
|
709
|
+
cache_fd, (ssize_t)cached_key.data_size, handler, args,
|
648
710
|
&output_data, &exception_tag, &errno_provenance
|
649
711
|
);
|
650
|
-
if (exception_tag != 0)
|
651
|
-
else if (res ==
|
652
|
-
|
653
|
-
|
712
|
+
if (exception_tag != 0) goto raise;
|
713
|
+
else if (res == CACHE_UNCOMPILABLE) {
|
714
|
+
/* If fetch_cached_data returned `Uncompilable` we fallback to `input_to_output`
|
715
|
+
This happens if we have say, an unsafe YAML cache, but try to load it in safe mode */
|
716
|
+
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
|
717
|
+
input_data = rb_str_new(contents, current_key.size);
|
718
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
719
|
+
if (exception_tag != 0) goto raise;
|
720
|
+
goto succeed;
|
721
|
+
} else if (res == CACHE_MISS || res == CACHE_STALE) valid_cache = 0;
|
722
|
+
else if (res == ERROR_WITH_ERRNO) goto fail_errno;
|
723
|
+
else if (!NIL_P(output_data)) goto succeed; /* fast-path, goal */
|
654
724
|
}
|
655
725
|
close(cache_fd);
|
656
726
|
cache_fd = -1;
|
@@ -658,37 +728,47 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
658
728
|
|
659
729
|
/* Read the contents of the source file into a buffer */
|
660
730
|
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
|
661
|
-
input_data =
|
731
|
+
input_data = rb_str_new(contents, current_key.size);
|
662
732
|
|
663
733
|
/* Try to compile the input_data using input_to_storage(input_data) */
|
664
|
-
exception_tag = bs_input_to_storage(handler, input_data, path_v, &storage_data);
|
734
|
+
exception_tag = bs_input_to_storage(handler, args, input_data, path_v, &storage_data);
|
665
735
|
if (exception_tag != 0) goto raise;
|
666
736
|
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
667
737
|
* to cache anything; just return input_to_output(input_data) */
|
668
|
-
if (storage_data ==
|
669
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
738
|
+
if (storage_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
739
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
670
740
|
if (exception_tag != 0) goto raise;
|
671
741
|
goto succeed;
|
672
742
|
}
|
673
743
|
/* If storage_data isn't a string, we can't cache it */
|
674
744
|
if (!RB_TYPE_P(storage_data, T_STRING)) goto invalid_type_storage_data;
|
675
745
|
|
676
|
-
/*
|
677
|
-
|
678
|
-
|
746
|
+
/* Attempt to write the cache key and storage_data to the cache directory.
|
747
|
+
* We do however ignore any failures to persist the cache, as it's better
|
748
|
+
* to move along, than to interrupt the process.
|
749
|
+
*/
|
750
|
+
atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
679
751
|
|
680
752
|
/* Having written the cache, now convert storage_data to output_data */
|
681
|
-
exception_tag = bs_storage_to_output(handler, storage_data, &output_data);
|
753
|
+
exception_tag = bs_storage_to_output(handler, args, storage_data, &output_data);
|
682
754
|
if (exception_tag != 0) goto raise;
|
683
755
|
|
684
|
-
|
685
|
-
|
686
|
-
|
756
|
+
if (output_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
757
|
+
/* If storage_to_output returned `Uncompilable` we fallback to `input_to_output` */
|
758
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
759
|
+
if (exception_tag != 0) goto raise;
|
760
|
+
} else if (NIL_P(output_data)) {
|
761
|
+
/* If output_data is nil, delete the cache entry and generate the output
|
762
|
+
* using input_to_output */
|
687
763
|
if (unlink(cache_path) < 0) {
|
688
|
-
|
689
|
-
|
764
|
+
/* If the cache was already deleted, it might be that another process did it before us.
|
765
|
+
* No point raising an error */
|
766
|
+
if (errno != ENOENT) {
|
767
|
+
errno_provenance = "bs_fetch:unlink";
|
768
|
+
goto fail_errno;
|
769
|
+
}
|
690
770
|
}
|
691
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
771
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
692
772
|
if (exception_tag != 0) goto raise;
|
693
773
|
}
|
694
774
|
|
@@ -719,6 +799,79 @@ invalid_type_storage_data:
|
|
719
799
|
#undef CLEANUP
|
720
800
|
}
|
721
801
|
|
802
|
+
static VALUE
|
803
|
+
bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
804
|
+
{
|
805
|
+
struct bs_cache_key cached_key, current_key;
|
806
|
+
char * contents = NULL;
|
807
|
+
int cache_fd = -1, current_fd = -1;
|
808
|
+
int res, valid_cache = 0, exception_tag = 0;
|
809
|
+
const char * errno_provenance = NULL;
|
810
|
+
|
811
|
+
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
812
|
+
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
813
|
+
|
814
|
+
/* Open the source file and generate a cache key for it */
|
815
|
+
current_fd = open_current_file(path, ¤t_key, &errno_provenance);
|
816
|
+
if (current_fd < 0) goto fail;
|
817
|
+
|
818
|
+
/* Open the cache key if it exists, and read its cache key in */
|
819
|
+
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
|
820
|
+
if (cache_fd == CACHE_MISS || cache_fd == CACHE_STALE) {
|
821
|
+
/* This is ok: valid_cache remains false, we re-populate it. */
|
822
|
+
} else if (cache_fd < 0) {
|
823
|
+
goto fail;
|
824
|
+
} else {
|
825
|
+
/* True if the cache existed and no invalidating changes have occurred since
|
826
|
+
* it was generated. */
|
827
|
+
valid_cache = cache_key_equal(¤t_key, &cached_key);
|
828
|
+
}
|
829
|
+
|
830
|
+
if (valid_cache) {
|
831
|
+
goto succeed;
|
832
|
+
}
|
833
|
+
|
834
|
+
close(cache_fd);
|
835
|
+
cache_fd = -1;
|
836
|
+
/* Cache is stale, invalid, or missing. Regenerate and write it out. */
|
837
|
+
|
838
|
+
/* Read the contents of the source file into a buffer */
|
839
|
+
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail;
|
840
|
+
input_data = rb_str_new(contents, current_key.size);
|
841
|
+
|
842
|
+
/* Try to compile the input_data using input_to_storage(input_data) */
|
843
|
+
exception_tag = bs_input_to_storage(handler, Qnil, input_data, path_v, &storage_data);
|
844
|
+
if (exception_tag != 0) goto fail;
|
845
|
+
|
846
|
+
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
847
|
+
* to cache anything; just return false */
|
848
|
+
if (storage_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
849
|
+
goto fail;
|
850
|
+
}
|
851
|
+
/* If storage_data isn't a string, we can't cache it */
|
852
|
+
if (!RB_TYPE_P(storage_data, T_STRING)) goto fail;
|
853
|
+
|
854
|
+
/* Write the cache key and storage_data to the cache directory */
|
855
|
+
res = atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
856
|
+
if (res < 0) goto fail;
|
857
|
+
|
858
|
+
goto succeed;
|
859
|
+
|
860
|
+
#define CLEANUP \
|
861
|
+
if (contents != NULL) xfree(contents); \
|
862
|
+
if (current_fd >= 0) close(current_fd); \
|
863
|
+
if (cache_fd >= 0) close(cache_fd);
|
864
|
+
|
865
|
+
succeed:
|
866
|
+
CLEANUP;
|
867
|
+
return Qtrue;
|
868
|
+
fail:
|
869
|
+
CLEANUP;
|
870
|
+
return Qfalse;
|
871
|
+
#undef CLEANUP
|
872
|
+
}
|
873
|
+
|
874
|
+
|
722
875
|
/*****************************************************************************/
|
723
876
|
/********************* Handler Wrappers **************************************/
|
724
877
|
/*****************************************************************************
|
@@ -738,11 +891,13 @@ invalid_type_storage_data:
|
|
738
891
|
|
739
892
|
struct s2o_data {
|
740
893
|
VALUE handler;
|
894
|
+
VALUE args;
|
741
895
|
VALUE storage_data;
|
742
896
|
};
|
743
897
|
|
744
898
|
struct i2o_data {
|
745
899
|
VALUE handler;
|
900
|
+
VALUE args;
|
746
901
|
VALUE input_data;
|
747
902
|
};
|
748
903
|
|
@@ -753,29 +908,31 @@ struct i2s_data {
|
|
753
908
|
};
|
754
909
|
|
755
910
|
static VALUE
|
756
|
-
|
911
|
+
try_storage_to_output(VALUE arg)
|
757
912
|
{
|
758
913
|
struct s2o_data * data = (struct s2o_data *)arg;
|
759
|
-
return rb_funcall(data->handler, rb_intern("storage_to_output"),
|
914
|
+
return rb_funcall(data->handler, rb_intern("storage_to_output"), 2, data->storage_data, data->args);
|
760
915
|
}
|
761
916
|
|
762
917
|
static int
|
763
|
-
bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
|
918
|
+
bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data)
|
764
919
|
{
|
765
920
|
int state;
|
766
921
|
struct s2o_data s2o_data = {
|
767
922
|
.handler = handler,
|
923
|
+
.args = args,
|
768
924
|
.storage_data = storage_data,
|
769
925
|
};
|
770
|
-
*output_data = rb_protect(
|
926
|
+
*output_data = rb_protect(try_storage_to_output, (VALUE)&s2o_data, &state);
|
771
927
|
return state;
|
772
928
|
}
|
773
929
|
|
774
930
|
static void
|
775
|
-
bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag)
|
931
|
+
bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag)
|
776
932
|
{
|
777
933
|
struct i2o_data i2o_data = {
|
778
934
|
.handler = handler,
|
935
|
+
.args = args,
|
779
936
|
.input_data = input_data,
|
780
937
|
};
|
781
938
|
*output_data = rb_protect(prot_input_to_output, (VALUE)&i2o_data, exception_tag);
|
@@ -785,7 +942,7 @@ static VALUE
|
|
785
942
|
prot_input_to_output(VALUE arg)
|
786
943
|
{
|
787
944
|
struct i2o_data * data = (struct i2o_data *)arg;
|
788
|
-
return rb_funcall(data->handler, rb_intern("input_to_output"),
|
945
|
+
return rb_funcall(data->handler, rb_intern("input_to_output"), 2, data->input_data, data->args);
|
789
946
|
}
|
790
947
|
|
791
948
|
static VALUE
|
@@ -795,31 +952,20 @@ try_input_to_storage(VALUE arg)
|
|
795
952
|
return rb_funcall(data->handler, rb_intern("input_to_storage"), 2, data->input_data, data->pathval);
|
796
953
|
}
|
797
954
|
|
798
|
-
static VALUE
|
799
|
-
rescue_input_to_storage(VALUE arg)
|
800
|
-
{
|
801
|
-
return uncompilable;
|
802
|
-
}
|
803
|
-
|
804
|
-
static VALUE
|
805
|
-
prot_input_to_storage(VALUE arg)
|
806
|
-
{
|
807
|
-
struct i2s_data * data = (struct i2s_data *)arg;
|
808
|
-
return rb_rescue2(
|
809
|
-
try_input_to_storage, (VALUE)data,
|
810
|
-
rescue_input_to_storage, Qnil,
|
811
|
-
rb_eBootsnap_CompileCache_Uncompilable, 0);
|
812
|
-
}
|
813
|
-
|
814
955
|
static int
|
815
|
-
bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
956
|
+
bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
816
957
|
{
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
958
|
+
if (readonly) {
|
959
|
+
*storage_data = rb_cBootsnap_CompileCache_UNCOMPILABLE;
|
960
|
+
return 0;
|
961
|
+
} else {
|
962
|
+
int state;
|
963
|
+
struct i2s_data i2s_data = {
|
964
|
+
.handler = handler,
|
965
|
+
.input_data = input_data,
|
966
|
+
.pathval = pathval,
|
967
|
+
};
|
968
|
+
*storage_data = rb_protect(try_input_to_storage, (VALUE)&i2s_data, &state);
|
969
|
+
return state;
|
970
|
+
}
|
825
971
|
}
|