bootsnap 1.4.1 → 1.10.3
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 +189 -0
- data/LICENSE.txt +1 -1
- data/README.md +67 -18
- data/exe/bootsnap +5 -0
- data/ext/bootsnap/bootsnap.c +319 -119
- 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 +65 -18
- data/lib/bootsnap/compile_cache/json.rb +88 -0
- data/lib/bootsnap/compile_cache/yaml.rb +332 -39
- data/lib/bootsnap/compile_cache.rb +35 -7
- data/lib/bootsnap/explicit_require.rb +5 -3
- data/lib/bootsnap/load_path_cache/cache.rb +83 -32
- data/lib/bootsnap/load_path_cache/change_observer.rb +6 -1
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +39 -47
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +12 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +69 -26
- data/lib/bootsnap/load_path_cache/path.rb +8 -5
- data/lib/bootsnap/load_path_cache/path_scanner.rb +56 -29
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +6 -5
- data/lib/bootsnap/load_path_cache/store.rb +49 -18
- data/lib/bootsnap/load_path_cache.rb +20 -32
- data/lib/bootsnap/setup.rb +3 -33
- data/lib/bootsnap/version.rb +3 -1
- data/lib/bootsnap.rb +126 -36
- metadata +15 -97
- data/.gitignore +0 -17
- data/.rubocop.yml +0 -20
- data/.travis.yml +0 -24
- 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 -100
- data/shipit.rubygems.yml +0 -0
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
* Suggested reading order:
|
3
3
|
* 1. Skim Init_bootsnap
|
4
4
|
* 2. Skim bs_fetch
|
5
|
-
* 3. The rest of
|
5
|
+
* 3. The rest of everyrything
|
6
6
|
*
|
7
7
|
* Init_bootsnap sets up the ruby objects and binds bs_fetch to
|
8
8
|
* Bootsnap::CompileCache::Native.fetch.
|
@@ -14,6 +14,7 @@
|
|
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>
|
@@ -21,6 +22,9 @@
|
|
21
22
|
#ifndef _WIN32
|
22
23
|
#include <sys/utsname.h>
|
23
24
|
#endif
|
25
|
+
#ifdef __GLIBC__
|
26
|
+
#include <gnu/libc-version.h>
|
27
|
+
#endif
|
24
28
|
|
25
29
|
/* 1000 is an arbitrary limit; FNV64 plus some slashes brings the cap down to
|
26
30
|
* 981 for the cache dir */
|
@@ -29,6 +33,12 @@
|
|
29
33
|
|
30
34
|
#define KEY_SIZE 64
|
31
35
|
|
36
|
+
#define MAX_CREATE_TEMPFILE_ATTEMPT 3
|
37
|
+
|
38
|
+
#ifndef RB_UNLIKELY
|
39
|
+
#define RB_UNLIKELY(x) (x)
|
40
|
+
#endif
|
41
|
+
|
32
42
|
/*
|
33
43
|
* An instance of this key is written as the first 64 bytes of each cache file.
|
34
44
|
* The mtime and size members track whether the file contents have changed, and
|
@@ -65,7 +75,7 @@ struct bs_cache_key {
|
|
65
75
|
STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
|
66
76
|
|
67
77
|
/* Effectively a schema version. Bumping invalidates all previous caches */
|
68
|
-
static const uint32_t current_version =
|
78
|
+
static const uint32_t current_version = 4;
|
69
79
|
|
70
80
|
/* hash of e.g. "x86_64-darwin17", invalidating when ruby is recompiled on a
|
71
81
|
* new OS ABI, etc. */
|
@@ -74,38 +84,44 @@ static uint32_t current_ruby_platform;
|
|
74
84
|
static uint32_t current_ruby_revision;
|
75
85
|
/* Invalidates cache when RubyVM::InstructionSequence.compile_option changes */
|
76
86
|
static uint32_t current_compile_option_crc32 = 0;
|
87
|
+
/* Current umask */
|
88
|
+
static mode_t current_umask;
|
77
89
|
|
78
90
|
/* Bootsnap::CompileCache::{Native, Uncompilable} */
|
79
91
|
static VALUE rb_mBootsnap;
|
80
92
|
static VALUE rb_mBootsnap_CompileCache;
|
81
93
|
static VALUE rb_mBootsnap_CompileCache_Native;
|
82
|
-
static VALUE
|
83
|
-
static ID
|
94
|
+
static VALUE rb_cBootsnap_CompileCache_UNCOMPILABLE;
|
95
|
+
static ID instrumentation_method;
|
96
|
+
static VALUE sym_miss;
|
97
|
+
static VALUE sym_stale;
|
98
|
+
static bool instrumentation_enabled = false;
|
84
99
|
|
85
100
|
/* Functions exposed as module functions on Bootsnap::CompileCache::Native */
|
101
|
+
static VALUE bs_instrumentation_enabled_set(VALUE self, VALUE enabled);
|
86
102
|
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);
|
103
|
+
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
|
104
|
+
static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
88
105
|
|
89
106
|
/* Helpers */
|
90
|
-
static
|
91
|
-
static void bs_cache_path(const char * cachedir, const char * path, char ** cache_path);
|
107
|
+
static void bs_cache_path(const char * cachedir, const VALUE path, char (* cache_path)[MAX_CACHEPATH_SIZE]);
|
92
108
|
static int bs_read_key(int fd, struct bs_cache_key * key);
|
93
109
|
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
|
96
|
-
static int
|
110
|
+
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args);
|
111
|
+
static VALUE bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
112
|
+
static int open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance);
|
113
|
+
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);
|
114
|
+
static uint32_t get_ruby_revision(void);
|
97
115
|
static uint32_t get_ruby_platform(void);
|
98
116
|
|
99
117
|
/*
|
100
118
|
* Helper functions to call ruby methods on handler object without crashing on
|
101
119
|
* exception.
|
102
120
|
*/
|
103
|
-
static int bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data);
|
104
|
-
static VALUE prot_storage_to_output(VALUE arg);
|
121
|
+
static int bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data);
|
105
122
|
static VALUE prot_input_to_output(VALUE arg);
|
106
|
-
static void bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag);
|
107
|
-
static
|
108
|
-
static int bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
123
|
+
static void bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag);
|
124
|
+
static int bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
109
125
|
struct s2o_data;
|
110
126
|
struct i2o_data;
|
111
127
|
struct i2s_data;
|
@@ -132,16 +148,35 @@ Init_bootsnap(void)
|
|
132
148
|
rb_mBootsnap = rb_define_module("Bootsnap");
|
133
149
|
rb_mBootsnap_CompileCache = rb_define_module_under(rb_mBootsnap, "CompileCache");
|
134
150
|
rb_mBootsnap_CompileCache_Native = rb_define_module_under(rb_mBootsnap_CompileCache, "Native");
|
135
|
-
|
151
|
+
rb_cBootsnap_CompileCache_UNCOMPILABLE = rb_const_get(rb_mBootsnap_CompileCache, rb_intern("UNCOMPILABLE"));
|
152
|
+
rb_global_variable(&rb_cBootsnap_CompileCache_UNCOMPILABLE);
|
136
153
|
|
137
|
-
current_ruby_revision =
|
154
|
+
current_ruby_revision = get_ruby_revision();
|
138
155
|
current_ruby_platform = get_ruby_platform();
|
139
156
|
|
140
|
-
|
157
|
+
instrumentation_method = rb_intern("_instrument");
|
158
|
+
|
159
|
+
sym_miss = ID2SYM(rb_intern("miss"));
|
160
|
+
rb_global_variable(&sym_miss);
|
141
161
|
|
162
|
+
sym_stale = ID2SYM(rb_intern("stale"));
|
163
|
+
rb_global_variable(&sym_stale);
|
164
|
+
|
165
|
+
rb_define_module_function(rb_mBootsnap, "instrumentation_enabled=", bs_instrumentation_enabled_set, 1);
|
142
166
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
|
143
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch,
|
167
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
|
168
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile, 3);
|
144
169
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
|
170
|
+
|
171
|
+
current_umask = umask(0777);
|
172
|
+
umask(current_umask);
|
173
|
+
}
|
174
|
+
|
175
|
+
static VALUE
|
176
|
+
bs_instrumentation_enabled_set(VALUE self, VALUE enabled)
|
177
|
+
{
|
178
|
+
instrumentation_enabled = RTEST(enabled);
|
179
|
+
return enabled;
|
145
180
|
}
|
146
181
|
|
147
182
|
/*
|
@@ -172,7 +207,7 @@ bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
|
|
172
207
|
* - 32 bits doesn't feel collision-resistant enough; 64 is nice.
|
173
208
|
*/
|
174
209
|
static uint64_t
|
175
|
-
|
210
|
+
fnv1a_64_iter_cstr(uint64_t h, const char *str)
|
176
211
|
{
|
177
212
|
unsigned char *s = (unsigned char *)str;
|
178
213
|
|
@@ -185,12 +220,46 @@ fnv1a_64_iter(uint64_t h, const char *str)
|
|
185
220
|
}
|
186
221
|
|
187
222
|
static uint64_t
|
188
|
-
|
223
|
+
fnv1a_64_iter(uint64_t h, const VALUE str)
|
224
|
+
{
|
225
|
+
unsigned char *s = (unsigned char *)RSTRING_PTR(str);
|
226
|
+
unsigned char *str_end = (unsigned char *)RSTRING_PTR(str) + RSTRING_LEN(str);
|
227
|
+
|
228
|
+
while (s < str_end) {
|
229
|
+
h ^= (uint64_t)*s++;
|
230
|
+
h += (h << 1) + (h << 4) + (h << 5) + (h << 7) + (h << 8) + (h << 40);
|
231
|
+
}
|
232
|
+
|
233
|
+
return h;
|
234
|
+
}
|
235
|
+
|
236
|
+
static uint64_t
|
237
|
+
fnv1a_64(const VALUE str)
|
189
238
|
{
|
190
239
|
uint64_t h = (uint64_t)0xcbf29ce484222325ULL;
|
191
240
|
return fnv1a_64_iter(h, str);
|
192
241
|
}
|
193
242
|
|
243
|
+
/*
|
244
|
+
* Ruby's revision may be Integer or String. CRuby 2.7 or later uses
|
245
|
+
* Git commit ID as revision. It's String.
|
246
|
+
*/
|
247
|
+
static uint32_t
|
248
|
+
get_ruby_revision(void)
|
249
|
+
{
|
250
|
+
VALUE ruby_revision;
|
251
|
+
|
252
|
+
ruby_revision = rb_const_get(rb_cObject, rb_intern("RUBY_REVISION"));
|
253
|
+
if (RB_TYPE_P(ruby_revision, RUBY_T_FIXNUM)) {
|
254
|
+
return FIX2INT(ruby_revision);
|
255
|
+
} else {
|
256
|
+
uint64_t hash;
|
257
|
+
|
258
|
+
hash = fnv1a_64(ruby_revision);
|
259
|
+
return (uint32_t)(hash >> 32);
|
260
|
+
}
|
261
|
+
}
|
262
|
+
|
194
263
|
/*
|
195
264
|
* When ruby's version doesn't change, but it's recompiled on a different OS
|
196
265
|
* (or OS version), we need to invalidate the cache.
|
@@ -206,16 +275,19 @@ get_ruby_platform(void)
|
|
206
275
|
VALUE ruby_platform;
|
207
276
|
|
208
277
|
ruby_platform = rb_const_get(rb_cObject, rb_intern("RUBY_PLATFORM"));
|
209
|
-
hash = fnv1a_64(
|
278
|
+
hash = fnv1a_64(ruby_platform);
|
210
279
|
|
211
280
|
#ifdef _WIN32
|
212
281
|
return (uint32_t)(hash >> 32) ^ (uint32_t)GetVersion();
|
282
|
+
#elif defined(__GLIBC__)
|
283
|
+
hash = fnv1a_64_iter_cstr(hash, gnu_get_libc_version());
|
284
|
+
return (uint32_t)(hash >> 32);
|
213
285
|
#else
|
214
286
|
struct utsname utsname;
|
215
287
|
|
216
288
|
/* Not worth crashing if this fails; lose extra cache invalidation potential */
|
217
289
|
if (uname(&utsname) >= 0) {
|
218
|
-
hash =
|
290
|
+
hash = fnv1a_64_iter_cstr(hash, utsname.version);
|
219
291
|
}
|
220
292
|
|
221
293
|
return (uint32_t)(hash >> 32);
|
@@ -230,14 +302,13 @@ get_ruby_platform(void)
|
|
230
302
|
* The path will look something like: <cachedir>/12/34567890abcdef
|
231
303
|
*/
|
232
304
|
static void
|
233
|
-
bs_cache_path(const char * cachedir, const
|
305
|
+
bs_cache_path(const char * cachedir, const VALUE path, char (* cache_path)[MAX_CACHEPATH_SIZE])
|
234
306
|
{
|
235
307
|
uint64_t hash = fnv1a_64(path);
|
236
|
-
|
237
308
|
uint8_t first_byte = (hash >> (64 - 8));
|
238
309
|
uint64_t remainder = hash & 0x00ffffffffffffff;
|
239
310
|
|
240
|
-
sprintf(*cache_path, "%s/%
|
311
|
+
sprintf(*cache_path, "%s/%02"PRIx8"/%014"PRIx64, cachedir, first_byte, remainder);
|
241
312
|
}
|
242
313
|
|
243
314
|
/*
|
@@ -267,7 +338,7 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
|
|
267
338
|
* conversions on the ruby VALUE arguments before passing them along.
|
268
339
|
*/
|
269
340
|
static VALUE
|
270
|
-
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
341
|
+
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args)
|
271
342
|
{
|
272
343
|
FilePathValue(path_v);
|
273
344
|
|
@@ -282,27 +353,51 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
|
282
353
|
char * path = RSTRING_PTR(path_v);
|
283
354
|
char cache_path[MAX_CACHEPATH_SIZE];
|
284
355
|
|
285
|
-
|
286
|
-
|
287
|
-
bs_cache_path(cachedir, path, &tmp);
|
288
|
-
}
|
356
|
+
/* generate cache path to cache_path */
|
357
|
+
bs_cache_path(cachedir, path_v, &cache_path);
|
289
358
|
|
290
|
-
return bs_fetch(path, path_v, cache_path, handler);
|
359
|
+
return bs_fetch(path, path_v, cache_path, handler, args);
|
291
360
|
}
|
292
361
|
|
362
|
+
/*
|
363
|
+
* Entrypoint for Bootsnap::CompileCache::Native.precompile.
|
364
|
+
* Similar to fetch, but it only generate the cache if missing
|
365
|
+
* and doesn't return the content.
|
366
|
+
*/
|
367
|
+
static VALUE
|
368
|
+
bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
369
|
+
{
|
370
|
+
FilePathValue(path_v);
|
371
|
+
|
372
|
+
Check_Type(cachedir_v, T_STRING);
|
373
|
+
Check_Type(path_v, T_STRING);
|
374
|
+
|
375
|
+
if (RSTRING_LEN(cachedir_v) > MAX_CACHEDIR_SIZE) {
|
376
|
+
rb_raise(rb_eArgError, "cachedir too long");
|
377
|
+
}
|
378
|
+
|
379
|
+
char * cachedir = RSTRING_PTR(cachedir_v);
|
380
|
+
char * path = RSTRING_PTR(path_v);
|
381
|
+
char cache_path[MAX_CACHEPATH_SIZE];
|
382
|
+
|
383
|
+
/* generate cache path to cache_path */
|
384
|
+
bs_cache_path(cachedir, path_v, &cache_path);
|
385
|
+
|
386
|
+
return bs_precompile(path, path_v, cache_path, handler);
|
387
|
+
}
|
293
388
|
/*
|
294
389
|
* Open the file we want to load/cache and generate a cache key for it if it
|
295
390
|
* was loaded.
|
296
391
|
*/
|
297
392
|
static int
|
298
|
-
open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenance)
|
393
|
+
open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
299
394
|
{
|
300
395
|
struct stat statbuf;
|
301
396
|
int fd;
|
302
397
|
|
303
398
|
fd = open(path, O_RDONLY);
|
304
399
|
if (fd < 0) {
|
305
|
-
*errno_provenance =
|
400
|
+
*errno_provenance = "bs_fetch:open_current_file:open";
|
306
401
|
return fd;
|
307
402
|
}
|
308
403
|
#ifdef _WIN32
|
@@ -310,7 +405,7 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
310
405
|
#endif
|
311
406
|
|
312
407
|
if (fstat(fd, &statbuf) < 0) {
|
313
|
-
*errno_provenance =
|
408
|
+
*errno_provenance = "bs_fetch:open_current_file:fstat";
|
314
409
|
close(fd);
|
315
410
|
return -1;
|
316
411
|
}
|
@@ -326,7 +421,9 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
326
421
|
}
|
327
422
|
|
328
423
|
#define ERROR_WITH_ERRNO -1
|
329
|
-
#define
|
424
|
+
#define CACHE_MISS -2
|
425
|
+
#define CACHE_STALE -3
|
426
|
+
#define CACHE_UNCOMPILABLE -4
|
330
427
|
|
331
428
|
/*
|
332
429
|
* Read the cache key from the given fd, which must have position 0 (e.g.
|
@@ -334,15 +431,16 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
334
431
|
*
|
335
432
|
* Possible return values:
|
336
433
|
* - 0 (OK, key was loaded)
|
337
|
-
* - CACHE_MISSING_OR_INVALID (-2)
|
338
434
|
* - ERROR_WITH_ERRNO (-1, errno is set)
|
435
|
+
* - CACHE_MISS (-2)
|
436
|
+
* - CACHE_STALE (-3)
|
339
437
|
*/
|
340
438
|
static int
|
341
439
|
bs_read_key(int fd, struct bs_cache_key * key)
|
342
440
|
{
|
343
441
|
ssize_t nread = read(fd, key, KEY_SIZE);
|
344
442
|
if (nread < 0) return ERROR_WITH_ERRNO;
|
345
|
-
if (nread < KEY_SIZE) return
|
443
|
+
if (nread < KEY_SIZE) return CACHE_STALE;
|
346
444
|
return 0;
|
347
445
|
}
|
348
446
|
|
@@ -352,19 +450,19 @@ bs_read_key(int fd, struct bs_cache_key * key)
|
|
352
450
|
*
|
353
451
|
* Possible return values:
|
354
452
|
* - 0 (OK, key was loaded)
|
355
|
-
* -
|
453
|
+
* - CACHE_MISS (-2)
|
454
|
+
* - CACHE_STALE (-3)
|
356
455
|
* - ERROR_WITH_ERRNO (-1, errno is set)
|
357
456
|
*/
|
358
457
|
static int
|
359
|
-
open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_provenance)
|
458
|
+
open_cache_file(const char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
360
459
|
{
|
361
460
|
int fd, res;
|
362
461
|
|
363
462
|
fd = open(path, O_RDONLY);
|
364
463
|
if (fd < 0) {
|
365
|
-
*errno_provenance =
|
366
|
-
|
367
|
-
return ERROR_WITH_ERRNO;
|
464
|
+
*errno_provenance = "bs_fetch:open_cache_file:open";
|
465
|
+
return CACHE_MISS;
|
368
466
|
}
|
369
467
|
#ifdef _WIN32
|
370
468
|
setmode(fd, O_BINARY);
|
@@ -372,7 +470,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
372
470
|
|
373
471
|
res = bs_read_key(fd, key);
|
374
472
|
if (res < 0) {
|
375
|
-
*errno_provenance =
|
473
|
+
*errno_provenance = "bs_fetch:open_cache_file:read";
|
376
474
|
close(fd);
|
377
475
|
return res;
|
378
476
|
}
|
@@ -396,7 +494,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
396
494
|
* or exception, will be the final data returnable to the user.
|
397
495
|
*/
|
398
496
|
static int
|
399
|
-
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, char ** errno_provenance)
|
497
|
+
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance)
|
400
498
|
{
|
401
499
|
char * data = NULL;
|
402
500
|
ssize_t nread;
|
@@ -405,26 +503,30 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
405
503
|
VALUE storage_data;
|
406
504
|
|
407
505
|
if (data_size > 100000000000) {
|
408
|
-
*errno_provenance =
|
506
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:datasize";
|
409
507
|
errno = EINVAL; /* because wtf? */
|
410
|
-
ret =
|
508
|
+
ret = ERROR_WITH_ERRNO;
|
411
509
|
goto done;
|
412
510
|
}
|
413
511
|
data = ALLOC_N(char, data_size);
|
414
512
|
nread = read(fd, data, data_size);
|
415
513
|
if (nread < 0) {
|
416
|
-
*errno_provenance =
|
417
|
-
ret =
|
514
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:read";
|
515
|
+
ret = ERROR_WITH_ERRNO;
|
418
516
|
goto done;
|
419
517
|
}
|
420
518
|
if (nread != data_size) {
|
421
|
-
ret =
|
519
|
+
ret = CACHE_STALE;
|
422
520
|
goto done;
|
423
521
|
}
|
424
522
|
|
425
|
-
storage_data =
|
523
|
+
storage_data = rb_str_new(data, data_size);
|
426
524
|
|
427
|
-
*exception_tag = bs_storage_to_output(handler, storage_data, output_data);
|
525
|
+
*exception_tag = bs_storage_to_output(handler, args, storage_data, output_data);
|
526
|
+
if (*output_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
527
|
+
ret = CACHE_UNCOMPILABLE;
|
528
|
+
goto done;
|
529
|
+
}
|
428
530
|
ret = 0;
|
429
531
|
done:
|
430
532
|
if (data != NULL) xfree(data);
|
@@ -465,30 +567,36 @@ mkpath(char * file_path, mode_t mode)
|
|
465
567
|
* path.
|
466
568
|
*/
|
467
569
|
static int
|
468
|
-
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char ** errno_provenance)
|
570
|
+
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, const char ** errno_provenance)
|
469
571
|
{
|
470
572
|
char template[MAX_CACHEPATH_SIZE + 20];
|
471
|
-
char * dest;
|
472
573
|
char * tmp_path;
|
473
|
-
int fd, ret;
|
574
|
+
int fd, ret, attempt;
|
474
575
|
ssize_t nwrite;
|
475
576
|
|
476
|
-
|
477
|
-
|
577
|
+
for (attempt = 0; attempt < MAX_CREATE_TEMPFILE_ATTEMPT; ++attempt) {
|
578
|
+
tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE);
|
579
|
+
strcat(tmp_path, ".tmp.XXXXXX");
|
478
580
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
}
|
486
|
-
fd = open(tmp_path, O_WRONLY | O_CREAT, 0664);
|
487
|
-
if (fd < 0) {
|
488
|
-
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:open";
|
581
|
+
// mkstemp modifies the template to be the actual created path
|
582
|
+
fd = mkstemp(tmp_path);
|
583
|
+
if (fd > 0) break;
|
584
|
+
|
585
|
+
if (attempt == 0 && mkpath(tmp_path, 0775) < 0) {
|
586
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkpath";
|
489
587
|
return -1;
|
490
588
|
}
|
491
589
|
}
|
590
|
+
if (fd < 0) {
|
591
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkstemp";
|
592
|
+
return -1;
|
593
|
+
}
|
594
|
+
|
595
|
+
if (chmod(tmp_path, 0644) < 0) {
|
596
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
597
|
+
return -1;
|
598
|
+
}
|
599
|
+
|
492
600
|
#ifdef _WIN32
|
493
601
|
setmode(fd, O_BINARY);
|
494
602
|
#endif
|
@@ -496,11 +604,11 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
496
604
|
key->data_size = RSTRING_LEN(data);
|
497
605
|
nwrite = write(fd, key, KEY_SIZE);
|
498
606
|
if (nwrite < 0) {
|
499
|
-
*errno_provenance =
|
607
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:write";
|
500
608
|
return -1;
|
501
609
|
}
|
502
610
|
if (nwrite != KEY_SIZE) {
|
503
|
-
*errno_provenance =
|
611
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:keysize";
|
504
612
|
errno = EIO; /* Lies but whatever */
|
505
613
|
return -1;
|
506
614
|
}
|
@@ -508,7 +616,7 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
508
616
|
nwrite = write(fd, RSTRING_PTR(data), RSTRING_LEN(data));
|
509
617
|
if (nwrite < 0) return -1;
|
510
618
|
if (nwrite != RSTRING_LEN(data)) {
|
511
|
-
*errno_provenance =
|
619
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:writelength";
|
512
620
|
errno = EIO; /* Lies but whatever */
|
513
621
|
return -1;
|
514
622
|
}
|
@@ -516,7 +624,12 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
516
624
|
close(fd);
|
517
625
|
ret = rename(tmp_path, path);
|
518
626
|
if (ret < 0) {
|
519
|
-
*errno_provenance =
|
627
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:rename";
|
628
|
+
return -1;
|
629
|
+
}
|
630
|
+
ret = chmod(path, 0664 & ~current_umask);
|
631
|
+
if (ret < 0) {
|
632
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
520
633
|
}
|
521
634
|
return ret;
|
522
635
|
}
|
@@ -525,13 +638,13 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
525
638
|
/* Read contents from an fd, whose contents are asserted to be +size+ bytes
|
526
639
|
* long, into a buffer */
|
527
640
|
static ssize_t
|
528
|
-
bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance)
|
641
|
+
bs_read_contents(int fd, size_t size, char ** contents, const char ** errno_provenance)
|
529
642
|
{
|
530
643
|
ssize_t nread;
|
531
644
|
*contents = ALLOC_N(char, size);
|
532
645
|
nread = read(fd, *contents, size);
|
533
646
|
if (nread < 0) {
|
534
|
-
*errno_provenance =
|
647
|
+
*errno_provenance = "bs_fetch:bs_read_contents:read";
|
535
648
|
}
|
536
649
|
return nread;
|
537
650
|
}
|
@@ -581,13 +694,13 @@ bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance
|
|
581
694
|
* - Return storage_to_output(storage_data)
|
582
695
|
*/
|
583
696
|
static VALUE
|
584
|
-
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
697
|
+
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args)
|
585
698
|
{
|
586
699
|
struct bs_cache_key cached_key, current_key;
|
587
700
|
char * contents = NULL;
|
588
701
|
int cache_fd = -1, current_fd = -1;
|
589
702
|
int res, valid_cache = 0, exception_tag = 0;
|
590
|
-
char * errno_provenance = NULL;
|
703
|
+
const char * errno_provenance = NULL;
|
591
704
|
|
592
705
|
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
593
706
|
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
@@ -601,26 +714,42 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
601
714
|
|
602
715
|
/* Open the cache key if it exists, and read its cache key in */
|
603
716
|
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
|
604
|
-
if (cache_fd ==
|
717
|
+
if (cache_fd == CACHE_MISS || cache_fd == CACHE_STALE) {
|
605
718
|
/* This is ok: valid_cache remains false, we re-populate it. */
|
719
|
+
if (RB_UNLIKELY(instrumentation_enabled)) {
|
720
|
+
rb_funcall(rb_mBootsnap, instrumentation_method, 2, cache_fd == CACHE_MISS ? sym_miss : sym_stale, path_v);
|
721
|
+
}
|
606
722
|
} else if (cache_fd < 0) {
|
607
723
|
goto fail_errno;
|
608
724
|
} else {
|
609
725
|
/* True if the cache existed and no invalidating changes have occurred since
|
610
726
|
* it was generated. */
|
611
727
|
valid_cache = cache_key_equal(¤t_key, &cached_key);
|
728
|
+
if (RB_UNLIKELY(instrumentation_enabled)) {
|
729
|
+
if (!valid_cache) {
|
730
|
+
rb_funcall(rb_mBootsnap, instrumentation_method, 2, sym_stale, path_v);
|
731
|
+
}
|
732
|
+
}
|
612
733
|
}
|
613
734
|
|
614
735
|
if (valid_cache) {
|
615
736
|
/* Fetch the cache data and return it if we're able to load it successfully */
|
616
737
|
res = fetch_cached_data(
|
617
|
-
cache_fd, (ssize_t)cached_key.data_size, handler,
|
738
|
+
cache_fd, (ssize_t)cached_key.data_size, handler, args,
|
618
739
|
&output_data, &exception_tag, &errno_provenance
|
619
740
|
);
|
620
|
-
if (exception_tag != 0)
|
621
|
-
else if (res ==
|
622
|
-
|
623
|
-
|
741
|
+
if (exception_tag != 0) goto raise;
|
742
|
+
else if (res == CACHE_UNCOMPILABLE) {
|
743
|
+
/* If fetch_cached_data returned `Uncompilable` we fallback to `input_to_output`
|
744
|
+
This happens if we have say, an unsafe YAML cache, but try to load it in safe mode */
|
745
|
+
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
|
746
|
+
input_data = rb_str_new(contents, current_key.size);
|
747
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
748
|
+
if (exception_tag != 0) goto raise;
|
749
|
+
goto succeed;
|
750
|
+
} else if (res == CACHE_MISS || res == CACHE_STALE) valid_cache = 0;
|
751
|
+
else if (res == ERROR_WITH_ERRNO) goto fail_errno;
|
752
|
+
else if (!NIL_P(output_data)) goto succeed; /* fast-path, goal */
|
624
753
|
}
|
625
754
|
close(cache_fd);
|
626
755
|
cache_fd = -1;
|
@@ -628,37 +757,47 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
628
757
|
|
629
758
|
/* Read the contents of the source file into a buffer */
|
630
759
|
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
|
631
|
-
input_data =
|
760
|
+
input_data = rb_str_new(contents, current_key.size);
|
632
761
|
|
633
762
|
/* Try to compile the input_data using input_to_storage(input_data) */
|
634
|
-
exception_tag = bs_input_to_storage(handler, input_data, path_v, &storage_data);
|
763
|
+
exception_tag = bs_input_to_storage(handler, args, input_data, path_v, &storage_data);
|
635
764
|
if (exception_tag != 0) goto raise;
|
636
765
|
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
637
766
|
* to cache anything; just return input_to_output(input_data) */
|
638
|
-
if (storage_data ==
|
639
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
767
|
+
if (storage_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
768
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
640
769
|
if (exception_tag != 0) goto raise;
|
641
770
|
goto succeed;
|
642
771
|
}
|
643
772
|
/* If storage_data isn't a string, we can't cache it */
|
644
773
|
if (!RB_TYPE_P(storage_data, T_STRING)) goto invalid_type_storage_data;
|
645
774
|
|
646
|
-
/*
|
647
|
-
|
648
|
-
|
775
|
+
/* Attempt to write the cache key and storage_data to the cache directory.
|
776
|
+
* We do however ignore any failures to persist the cache, as it's better
|
777
|
+
* to move along, than to interrupt the process.
|
778
|
+
*/
|
779
|
+
atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
649
780
|
|
650
781
|
/* Having written the cache, now convert storage_data to output_data */
|
651
|
-
exception_tag = bs_storage_to_output(handler, storage_data, &output_data);
|
782
|
+
exception_tag = bs_storage_to_output(handler, args, storage_data, &output_data);
|
652
783
|
if (exception_tag != 0) goto raise;
|
653
784
|
|
654
|
-
|
655
|
-
|
656
|
-
|
785
|
+
if (output_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
786
|
+
/* If storage_to_output returned `Uncompilable` we fallback to `input_to_output` */
|
787
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
788
|
+
if (exception_tag != 0) goto raise;
|
789
|
+
} else if (NIL_P(output_data)) {
|
790
|
+
/* If output_data is nil, delete the cache entry and generate the output
|
791
|
+
* using input_to_output */
|
657
792
|
if (unlink(cache_path) < 0) {
|
658
|
-
|
659
|
-
|
793
|
+
/* If the cache was already deleted, it might be that another process did it before us.
|
794
|
+
* No point raising an error */
|
795
|
+
if (errno != ENOENT) {
|
796
|
+
errno_provenance = "bs_fetch:unlink";
|
797
|
+
goto fail_errno;
|
798
|
+
}
|
660
799
|
}
|
661
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
800
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
662
801
|
if (exception_tag != 0) goto raise;
|
663
802
|
}
|
664
803
|
|
@@ -689,6 +828,79 @@ invalid_type_storage_data:
|
|
689
828
|
#undef CLEANUP
|
690
829
|
}
|
691
830
|
|
831
|
+
static VALUE
|
832
|
+
bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
833
|
+
{
|
834
|
+
struct bs_cache_key cached_key, current_key;
|
835
|
+
char * contents = NULL;
|
836
|
+
int cache_fd = -1, current_fd = -1;
|
837
|
+
int res, valid_cache = 0, exception_tag = 0;
|
838
|
+
const char * errno_provenance = NULL;
|
839
|
+
|
840
|
+
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
841
|
+
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
842
|
+
|
843
|
+
/* Open the source file and generate a cache key for it */
|
844
|
+
current_fd = open_current_file(path, ¤t_key, &errno_provenance);
|
845
|
+
if (current_fd < 0) goto fail;
|
846
|
+
|
847
|
+
/* Open the cache key if it exists, and read its cache key in */
|
848
|
+
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
|
849
|
+
if (cache_fd == CACHE_MISS || cache_fd == CACHE_STALE) {
|
850
|
+
/* This is ok: valid_cache remains false, we re-populate it. */
|
851
|
+
} else if (cache_fd < 0) {
|
852
|
+
goto fail;
|
853
|
+
} else {
|
854
|
+
/* True if the cache existed and no invalidating changes have occurred since
|
855
|
+
* it was generated. */
|
856
|
+
valid_cache = cache_key_equal(¤t_key, &cached_key);
|
857
|
+
}
|
858
|
+
|
859
|
+
if (valid_cache) {
|
860
|
+
goto succeed;
|
861
|
+
}
|
862
|
+
|
863
|
+
close(cache_fd);
|
864
|
+
cache_fd = -1;
|
865
|
+
/* Cache is stale, invalid, or missing. Regenerate and write it out. */
|
866
|
+
|
867
|
+
/* Read the contents of the source file into a buffer */
|
868
|
+
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail;
|
869
|
+
input_data = rb_str_new(contents, current_key.size);
|
870
|
+
|
871
|
+
/* Try to compile the input_data using input_to_storage(input_data) */
|
872
|
+
exception_tag = bs_input_to_storage(handler, Qnil, input_data, path_v, &storage_data);
|
873
|
+
if (exception_tag != 0) goto fail;
|
874
|
+
|
875
|
+
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
876
|
+
* to cache anything; just return false */
|
877
|
+
if (storage_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
878
|
+
goto fail;
|
879
|
+
}
|
880
|
+
/* If storage_data isn't a string, we can't cache it */
|
881
|
+
if (!RB_TYPE_P(storage_data, T_STRING)) goto fail;
|
882
|
+
|
883
|
+
/* Write the cache key and storage_data to the cache directory */
|
884
|
+
res = atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
885
|
+
if (res < 0) goto fail;
|
886
|
+
|
887
|
+
goto succeed;
|
888
|
+
|
889
|
+
#define CLEANUP \
|
890
|
+
if (contents != NULL) xfree(contents); \
|
891
|
+
if (current_fd >= 0) close(current_fd); \
|
892
|
+
if (cache_fd >= 0) close(cache_fd);
|
893
|
+
|
894
|
+
succeed:
|
895
|
+
CLEANUP;
|
896
|
+
return Qtrue;
|
897
|
+
fail:
|
898
|
+
CLEANUP;
|
899
|
+
return Qfalse;
|
900
|
+
#undef CLEANUP
|
901
|
+
}
|
902
|
+
|
903
|
+
|
692
904
|
/*****************************************************************************/
|
693
905
|
/********************* Handler Wrappers **************************************/
|
694
906
|
/*****************************************************************************
|
@@ -708,11 +920,13 @@ invalid_type_storage_data:
|
|
708
920
|
|
709
921
|
struct s2o_data {
|
710
922
|
VALUE handler;
|
923
|
+
VALUE args;
|
711
924
|
VALUE storage_data;
|
712
925
|
};
|
713
926
|
|
714
927
|
struct i2o_data {
|
715
928
|
VALUE handler;
|
929
|
+
VALUE args;
|
716
930
|
VALUE input_data;
|
717
931
|
};
|
718
932
|
|
@@ -723,29 +937,31 @@ struct i2s_data {
|
|
723
937
|
};
|
724
938
|
|
725
939
|
static VALUE
|
726
|
-
|
940
|
+
try_storage_to_output(VALUE arg)
|
727
941
|
{
|
728
942
|
struct s2o_data * data = (struct s2o_data *)arg;
|
729
|
-
return rb_funcall(data->handler, rb_intern("storage_to_output"),
|
943
|
+
return rb_funcall(data->handler, rb_intern("storage_to_output"), 2, data->storage_data, data->args);
|
730
944
|
}
|
731
945
|
|
732
946
|
static int
|
733
|
-
bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
|
947
|
+
bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data)
|
734
948
|
{
|
735
949
|
int state;
|
736
950
|
struct s2o_data s2o_data = {
|
737
951
|
.handler = handler,
|
952
|
+
.args = args,
|
738
953
|
.storage_data = storage_data,
|
739
954
|
};
|
740
|
-
*output_data = rb_protect(
|
955
|
+
*output_data = rb_protect(try_storage_to_output, (VALUE)&s2o_data, &state);
|
741
956
|
return state;
|
742
957
|
}
|
743
958
|
|
744
959
|
static void
|
745
|
-
bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag)
|
960
|
+
bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag)
|
746
961
|
{
|
747
962
|
struct i2o_data i2o_data = {
|
748
963
|
.handler = handler,
|
964
|
+
.args = args,
|
749
965
|
.input_data = input_data,
|
750
966
|
};
|
751
967
|
*output_data = rb_protect(prot_input_to_output, (VALUE)&i2o_data, exception_tag);
|
@@ -755,7 +971,7 @@ static VALUE
|
|
755
971
|
prot_input_to_output(VALUE arg)
|
756
972
|
{
|
757
973
|
struct i2o_data * data = (struct i2o_data *)arg;
|
758
|
-
return rb_funcall(data->handler, rb_intern("input_to_output"),
|
974
|
+
return rb_funcall(data->handler, rb_intern("input_to_output"), 2, data->input_data, data->args);
|
759
975
|
}
|
760
976
|
|
761
977
|
static VALUE
|
@@ -765,24 +981,8 @@ try_input_to_storage(VALUE arg)
|
|
765
981
|
return rb_funcall(data->handler, rb_intern("input_to_storage"), 2, data->input_data, data->pathval);
|
766
982
|
}
|
767
983
|
|
768
|
-
static VALUE
|
769
|
-
rescue_input_to_storage(VALUE arg)
|
770
|
-
{
|
771
|
-
return uncompilable;
|
772
|
-
}
|
773
|
-
|
774
|
-
static VALUE
|
775
|
-
prot_input_to_storage(VALUE arg)
|
776
|
-
{
|
777
|
-
struct i2s_data * data = (struct i2s_data *)arg;
|
778
|
-
return rb_rescue2(
|
779
|
-
try_input_to_storage, (VALUE)data,
|
780
|
-
rescue_input_to_storage, Qnil,
|
781
|
-
rb_eBootsnap_CompileCache_Uncompilable, 0);
|
782
|
-
}
|
783
|
-
|
784
984
|
static int
|
785
|
-
bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
985
|
+
bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
786
986
|
{
|
787
987
|
int state;
|
788
988
|
struct i2s_data i2s_data = {
|
@@ -790,6 +990,6 @@ bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * stor
|
|
790
990
|
.input_data = input_data,
|
791
991
|
.pathval = pathval,
|
792
992
|
};
|
793
|
-
*storage_data = rb_protect(
|
993
|
+
*storage_data = rb_protect(try_input_to_storage, (VALUE)&i2s_data, &state);
|
794
994
|
return state;
|
795
995
|
}
|