bootsnap 0.2.15 → 0.3.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +33 -27
- data/ext/bootsnap/bootsnap.c +361 -388
- data/ext/bootsnap/bootsnap.h +1 -3
- data/lib/bootsnap.rb +1 -0
- data/lib/bootsnap/compile_cache.rb +3 -3
- data/lib/bootsnap/compile_cache/iseq.rb +7 -1
- data/lib/bootsnap/compile_cache/yaml.rb +2 -1
- data/lib/bootsnap/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '082f8dd8be53486b79ff5248ad426cc28263778b'
|
4
|
+
data.tar.gz: 5bc6a79f2054aa8b869704cdb9aeab685eabb400
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad062964bbc28d997901a1f6016289a4c6671071f9e1c4640820fe9545427136e1268f6588b350e7d058d85641604b0004225a744792fcce9b24bfcb9e5a3172
|
7
|
+
data.tar.gz: 33c77f2e085ba965e1a9fccb65b5afd42a4db0485dbfca7aa5d3e3920497ff41d0a3ca0ca4b6706df55814d29f015056e6d862568fe8b48ad5194f6eb80e26dc
|
data/README.md
CHANGED
@@ -2,9 +2,6 @@
|
|
2
2
|
|
3
3
|
**Beta-quality. See [the last section of this README](#trustworthiness).**
|
4
4
|
|
5
|
-
**`compile_cache_*` features are only supported on MacOS (extremely likely to error on Linux;
|
6
|
-
won't even compile on other platforms).**
|
7
|
-
|
8
5
|
Bootsnap is a library that plugs into a number of Ruby and (optionally) `ActiveSupport` and `YAML`
|
9
6
|
methods to optimize and cache expensive computations. See [the How Does This Work section](#how-does-this-work) for more information.
|
10
7
|
|
@@ -120,7 +117,8 @@ result too, raising a `LoadError` without touching the filesystem at all.
|
|
120
117
|
|
121
118
|
### Compilation Caching
|
122
119
|
|
123
|
-
*(A
|
120
|
+
*(A more readable implementation of this concept can be found in
|
121
|
+
[yomikomu](https://github.com/ko1/yomikomu)).*
|
124
122
|
|
125
123
|
Ruby has complex grammar and parsing it is not a particularly cheap operation. Since 1.9, Ruby has
|
126
124
|
translated ruby source to an internal bytecode format, which is then executed by the Ruby VM. Since
|
@@ -134,9 +132,8 @@ implementation. We use the same strategy of compilation caching for YAML documen
|
|
134
132
|
equivalent of Ruby's "bytecode" format being a MessagePack document (or, in the case of YAML
|
135
133
|
documents with types unsupported by MessagePack, a Marshal stream).
|
136
134
|
|
137
|
-
These compilation results are stored
|
138
|
-
the
|
139
|
-
like changing mount flags). However, this is a very performant implementation.
|
135
|
+
These compilation results are stored in a cache directory, with filenames generated by taking a hash
|
136
|
+
of the full expanded path of the input file (FNV1a-64).
|
140
137
|
|
141
138
|
Whereas before, the sequence of syscalls generated to `require` a file would look like:
|
142
139
|
|
@@ -158,32 +155,36 @@ With bootsnap, we get:
|
|
158
155
|
```
|
159
156
|
open /c/foo.rb -> n
|
160
157
|
fstat64 n
|
161
|
-
|
162
|
-
|
158
|
+
close n
|
159
|
+
open /c/foo.rb -> n
|
160
|
+
fstat64 n
|
161
|
+
open (cache) -> m
|
162
|
+
read m
|
163
|
+
read m
|
164
|
+
close m
|
163
165
|
close n
|
164
166
|
```
|
165
167
|
|
166
|
-
|
168
|
+
This may look worse at a glance, but underlies a large performance difference.
|
167
169
|
|
168
|
-
* `
|
169
|
-
|
170
|
+
*(The first three syscalls in both listings -- `open`, `fstat64`, `close` -- are not inherently
|
171
|
+
useful. [This ruby patch](https://bugs.ruby-lang.org/issues/13378) optimizes them out when coupled
|
172
|
+
with bootsnap.)*
|
170
173
|
|
171
|
-
|
174
|
+
Bootsnap writes a cache file containing a 64 byte header followed by the cache contents. The header
|
175
|
+
is a cache key including several fields:
|
172
176
|
|
173
177
|
* `version`, hardcoded in bootsnap. Essentially a schema version;
|
178
|
+
* `os_version`, A hash of the current kernel version (on macOS, BSD) or glibc version (on Linux);
|
174
179
|
* `compile_option`, which changes with `RubyVM::InstructionSequence.compile_option` does;
|
175
|
-
* `
|
176
|
-
|
177
|
-
* `
|
178
|
-
* `
|
180
|
+
* `ruby_revision`, the version of Ruby this was compiled with;
|
181
|
+
* `size`, the size of the source file;
|
182
|
+
* `mtime`, the last-modification timestamp of the source file when it was compiled; and
|
183
|
+
* `data_size`, the number of bytes following the header, which we need to read it into a buffer.
|
179
184
|
|
180
185
|
If the key is valid, the result is loaded from the value. Otherwise, it is regenerated and clobbers
|
181
186
|
the current cache.
|
182
187
|
|
183
|
-
This diagram may help illustrate how it works:
|
184
|
-
|
185
|
-
![Compilation Caching](https://burkelibbey.s3.amazonaws.com/bootsnap-compile-cache.png)
|
186
|
-
|
187
188
|
### Putting it all together
|
188
189
|
|
189
190
|
Imagine we have this file structure:
|
@@ -227,8 +228,13 @@ With bootsnap, we get:
|
|
227
228
|
```
|
228
229
|
open /c/foo.rb -> n
|
229
230
|
fstat64 n
|
230
|
-
|
231
|
-
|
231
|
+
close n
|
232
|
+
open /c/foo.rb -> n
|
233
|
+
fstat64 n
|
234
|
+
open (cache) -> m
|
235
|
+
read m
|
236
|
+
read m
|
237
|
+
close m
|
232
238
|
close n
|
233
239
|
```
|
234
240
|
|
@@ -253,8 +259,8 @@ open /c/nope.bundle -> -1
|
|
253
259
|
|
254
260
|
We use the `*_path_cache` features in production and haven't experienced any issues in a long time.
|
255
261
|
|
256
|
-
The `compile_cache_*` features work well for us in development on macOS
|
257
|
-
|
262
|
+
The `compile_cache_*` features work well for us in development on macOS. It should work on Linux,
|
263
|
+
and we intend to deploy it in production, but haven't we haven't yet.
|
258
264
|
|
259
265
|
`disable_trace` should be completely safe, but we don't really use it because some people like to
|
260
266
|
use tools that make use of `trace` instructions.
|
@@ -264,5 +270,5 @@ use tools that make use of `trace` instructions.
|
|
264
270
|
| `load_path_cache` | everywhere |
|
265
271
|
| `autoload_path_cache` | everywhere |
|
266
272
|
| `disable_trace` | nowhere, but it's safe unless you need tracing |
|
267
|
-
| `compile_cache_iseq` | development,
|
268
|
-
| `compile_cache_yaml` | development,
|
273
|
+
| `compile_cache_iseq` | development, but probably safe to use everywhere |
|
274
|
+
| `compile_cache_yaml` | development, but probably safe to use everywhere |
|
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -1,60 +1,78 @@
|
|
1
1
|
#include "bootsnap.h"
|
2
|
+
#include "ruby.h"
|
3
|
+
#include <stdint.h>
|
2
4
|
#include <sys/types.h>
|
3
|
-
#include <sys/xattr.h>
|
4
|
-
#include <sys/stat.h>
|
5
5
|
#include <errno.h>
|
6
|
-
#include <unistd.h>
|
7
6
|
#include <fcntl.h>
|
8
|
-
#include <
|
9
|
-
#include <utime.h>
|
7
|
+
#include <sys/stat.h>
|
10
8
|
|
11
|
-
#ifdef
|
12
|
-
|
13
|
-
#include <Availability.h>
|
14
|
-
#define _ENOATTR ENOATTR
|
9
|
+
#ifdef __linux__
|
10
|
+
#include <gnu/libc-version.h>
|
15
11
|
#else
|
16
|
-
#
|
12
|
+
#include <sys/sysctl.h>
|
17
13
|
#endif
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
#define MAX_CACHEPATH_SIZE 1000
|
16
|
+
#define MAX_CACHEDIR_SIZE 981
|
17
|
+
|
18
|
+
#define KEY_SIZE 64
|
19
|
+
|
20
|
+
struct bs_cache_key {
|
21
|
+
uint32_t version;
|
22
|
+
uint32_t os_version;
|
23
|
+
uint32_t compile_option;
|
24
|
+
uint32_t ruby_revision;
|
25
|
+
uint64_t size;
|
26
|
+
uint64_t mtime;
|
27
|
+
uint64_t data_size; /* not used for equality */
|
28
|
+
uint8_t pad[24];
|
29
|
+
} __attribute__((packed));
|
30
|
+
|
31
|
+
#define STATIC_ASSERT(X) STATIC_ASSERT2(X,__LINE__)
|
32
|
+
#define STATIC_ASSERT2(X,L) STATIC_ASSERT3(X,L)
|
33
|
+
#define STATIC_ASSERT3(X,L) STATIC_ASSERT_MSG(X,at_line_##L)
|
34
|
+
#define STATIC_ASSERT_MSG(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1]
|
35
|
+
|
36
|
+
STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
|
37
|
+
|
38
|
+
static const uint32_t current_version = 2;
|
39
|
+
|
40
|
+
static uint32_t current_os_version;
|
41
|
+
static uint32_t current_ruby_revision;
|
42
|
+
static uint32_t current_compile_option_crc32 = 0;
|
24
43
|
|
25
44
|
static VALUE rb_mBootsnap;
|
26
45
|
static VALUE rb_mBootsnap_CompileCache;
|
27
46
|
static VALUE rb_mBootsnap_CompileCache_Native;
|
28
47
|
static VALUE rb_eBootsnap_CompileCache_Uncompilable;
|
29
|
-
static uint32_t current_ruby_revision;
|
30
|
-
static uint32_t current_compile_option_crc32 = 0;
|
31
48
|
static ID uncompilable;
|
32
49
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
static struct
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
.retry = 0,
|
48
|
-
};
|
50
|
+
/* Real API */
|
51
|
+
static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
|
52
|
+
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
53
|
+
|
54
|
+
/* Helpers */
|
55
|
+
static uint64_t fnv_64a(const char *str);
|
56
|
+
static void bs_cache_path(const char * cachedir, const char * path, char ** cache_path);
|
57
|
+
static int bs_read_key(int fd, struct bs_cache_key * key);
|
58
|
+
static int cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2);
|
59
|
+
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
60
|
+
static int open_current_file(char * path, struct bs_cache_key * key);
|
61
|
+
static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag);
|
62
|
+
static VALUE prot_exception_for_errno(VALUE err);
|
63
|
+
static uint32_t get_os_version(void);
|
49
64
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
65
|
+
static int bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data);
|
66
|
+
static VALUE prot_storage_to_output(VALUE arg);
|
67
|
+
static VALUE prot_input_to_output(VALUE arg);
|
68
|
+
static void bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag);
|
69
|
+
static VALUE prot_input_to_storage(VALUE arg);
|
70
|
+
static int bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
71
|
+
|
72
|
+
struct s2o_data {
|
73
|
+
VALUE handler;
|
74
|
+
VALUE storage_data;
|
75
|
+
};
|
58
76
|
|
59
77
|
struct i2o_data {
|
60
78
|
VALUE handler;
|
@@ -67,59 +85,6 @@ struct i2s_data {
|
|
67
85
|
VALUE pathval;
|
68
86
|
};
|
69
87
|
|
70
|
-
struct s2o_data {
|
71
|
-
VALUE handler;
|
72
|
-
VALUE storage_data;
|
73
|
-
};
|
74
|
-
|
75
|
-
static const uint8_t current_version = 11;
|
76
|
-
static const char * xattr_key_name = "user.aotcc.key";
|
77
|
-
static const char * xattr_data_name = "user.aotcc.value";
|
78
|
-
static const size_t xattr_key_size = sizeof (struct xattr_key);
|
79
|
-
|
80
|
-
#ifdef __MAC_10_15 // Mac OS 10.15 (future)
|
81
|
-
static const int os_version = 15;
|
82
|
-
#elif __MAC_10_14 // Mac OS 10.14 (future)
|
83
|
-
static const int os_version = 14;
|
84
|
-
#elif __MAC_10_13 // Mac OS 10.13 (future)
|
85
|
-
static const int os_version = 13;
|
86
|
-
#elif __MAC_10_12 // Mac OS X Sierra
|
87
|
-
static const int os_version = 12;
|
88
|
-
#elif __MAC_10_11 // Mac OS X El Capitan
|
89
|
-
static const int os_version = 11;
|
90
|
-
# else
|
91
|
-
static const int os_version = 0;
|
92
|
-
#endif
|
93
|
-
|
94
|
-
#ifdef __APPLE__
|
95
|
-
#define GETXATTR_TRAILER ,0,0
|
96
|
-
#define SETXATTR_TRAILER ,0
|
97
|
-
#define REMOVEXATTR_TRAILER ,0
|
98
|
-
#else
|
99
|
-
#define GETXATTR_TRAILER
|
100
|
-
#define SETXATTR_TRAILER
|
101
|
-
#define REMOVEXATTR_TRAILER
|
102
|
-
#endif
|
103
|
-
|
104
|
-
/* forward declarations */
|
105
|
-
static int bs_fetch_data(int fd, size_t size, VALUE handler, VALUE * storage_data, int * exception_tag);
|
106
|
-
static int bs_update_key(int fd, uint32_t data_size, uint64_t current_mtime);
|
107
|
-
static int bs_open(const char * path, bool * writable);
|
108
|
-
static int bs_get_cache(int fd, struct xattr_key * key);
|
109
|
-
static size_t bs_read_contents(int fd, size_t size, char ** contents);
|
110
|
-
static int bs_close_and_unclobber_times(int * fd, const char * path, time_t atime, time_t mtime);
|
111
|
-
static VALUE bs_fetch(VALUE self, VALUE pathval, VALUE handler);
|
112
|
-
static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32val);
|
113
|
-
static VALUE prot_exception_for_errno(VALUE err);
|
114
|
-
static VALUE prot_input_to_output(VALUE arg);
|
115
|
-
static void bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag);
|
116
|
-
static VALUE prot_input_to_storage(VALUE arg);
|
117
|
-
static int bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
118
|
-
static VALUE prot_storage_to_output(VALUE arg);
|
119
|
-
static int bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data);
|
120
|
-
static int logging_enabled();
|
121
|
-
static VALUE bs_stats(VALUE self);
|
122
|
-
|
123
88
|
void
|
124
89
|
Init_bootsnap(void)
|
125
90
|
{
|
@@ -127,339 +92,356 @@ Init_bootsnap(void)
|
|
127
92
|
rb_mBootsnap_CompileCache = rb_define_module_under(rb_mBootsnap, "CompileCache");
|
128
93
|
rb_mBootsnap_CompileCache_Native = rb_define_module_under(rb_mBootsnap_CompileCache, "Native");
|
129
94
|
rb_eBootsnap_CompileCache_Uncompilable = rb_define_class_under(rb_mBootsnap_CompileCache, "Uncompilable", rb_eStandardError);
|
95
|
+
|
130
96
|
current_ruby_revision = FIX2INT(rb_const_get(rb_cObject, rb_intern("RUBY_REVISION")));
|
97
|
+
current_os_version = get_os_version();
|
131
98
|
|
132
99
|
uncompilable = rb_intern("__bootsnap_uncompilable__");
|
133
100
|
|
134
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch",
|
135
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "stats", bs_stats, 0);
|
101
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 3);
|
136
102
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
|
137
103
|
}
|
138
104
|
|
139
105
|
static VALUE
|
140
|
-
|
106
|
+
bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
|
141
107
|
{
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
rb_hash_aset(ret, ID2SYM(rb_intern("unwritable")), INT2NUM(stats.unwritable));
|
146
|
-
rb_hash_aset(ret, ID2SYM(rb_intern("uncompilable")), INT2NUM(stats.uncompilable));
|
147
|
-
rb_hash_aset(ret, ID2SYM(rb_intern("fail")), INT2NUM(stats.fail));
|
148
|
-
rb_hash_aset(ret, ID2SYM(rb_intern("retry")), INT2NUM(stats.retry));
|
149
|
-
return ret;
|
108
|
+
Check_Type(crc32_v, T_FIXNUM);
|
109
|
+
current_compile_option_crc32 = FIX2UINT(crc32_v);
|
110
|
+
return Qnil;
|
150
111
|
}
|
151
112
|
|
152
|
-
static
|
153
|
-
|
113
|
+
static uint64_t
|
114
|
+
fnv_64a(const char *str)
|
154
115
|
{
|
155
|
-
|
156
|
-
|
157
|
-
|
116
|
+
unsigned char *s = (unsigned char *)str;
|
117
|
+
uint64_t h = ((uint64_t)0xcbf29ce484222325ULL);
|
118
|
+
|
119
|
+
while (*s) {
|
120
|
+
h ^= (uint64_t)*s++;
|
121
|
+
h += (h << 1) + (h << 4) + (h << 5) + (h << 7) + (h << 8) + (h << 40);
|
122
|
+
}
|
123
|
+
|
124
|
+
return h;
|
158
125
|
}
|
159
126
|
|
160
|
-
|
161
|
-
|
127
|
+
/* On Darwin and FreeBSD, this is the kernel version, since that tends to vary
|
128
|
+
* with whole-system upgrades. What we probably care about more is the libc
|
129
|
+
* version, which is what we explicitly ask for on linux.
|
130
|
+
* (and KERN_OSRELEASE came back empty for me one one linux box, so...?)
|
131
|
+
*/
|
132
|
+
static uint32_t
|
133
|
+
get_os_version(void)
|
134
|
+
{
|
135
|
+
size_t len;
|
136
|
+
uint64_t hash;
|
137
|
+
#ifdef __linux__
|
138
|
+
const char * version;
|
139
|
+
version = gnu_get_libc_version();
|
140
|
+
hash = fnv_64a(version);
|
141
|
+
#else
|
142
|
+
char * version;
|
143
|
+
int mib[2] = {CTL_KERN, KERN_OSRELEASE};
|
144
|
+
if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) return 0;
|
145
|
+
version = malloc(sizeof(char) * len);
|
146
|
+
if (sysctl(mib, 2, version, &len, NULL, 0) < 0) return 0;
|
147
|
+
hash = fnv_64a(version);
|
148
|
+
free(version);
|
149
|
+
#endif
|
150
|
+
return (uint32_t)(hash >> 32);
|
151
|
+
}
|
162
152
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
if (state) exception = rb_eStandardError; \
|
168
|
-
goto fail; \
|
169
|
-
} while(0);
|
153
|
+
static void
|
154
|
+
bs_cache_path(const char * cachedir, const char * path, char ** cache_path)
|
155
|
+
{
|
156
|
+
uint64_t hash = fnv_64a(path);
|
170
157
|
|
171
|
-
|
172
|
-
|
158
|
+
uint8_t first_byte = (hash >> (64 - 8));
|
159
|
+
uint64_t remainder = hash & 0x00ffffffffffffff;
|
173
160
|
|
174
|
-
|
175
|
-
|
161
|
+
sprintf(*cache_path, "%s/%02x/%014llx", cachedir, first_byte, remainder);
|
162
|
+
}
|
176
163
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
164
|
+
static int
|
165
|
+
cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
|
166
|
+
{
|
167
|
+
return (
|
168
|
+
k1->version == k2->version &&
|
169
|
+
k1->os_version == k2->os_version &&
|
170
|
+
k1->compile_option == k2->compile_option &&
|
171
|
+
k1->ruby_revision == k2->ruby_revision &&
|
172
|
+
k1->size == k2->size &&
|
173
|
+
k1->mtime == k2->mtime
|
174
|
+
);
|
175
|
+
}
|
182
176
|
|
183
177
|
static VALUE
|
184
|
-
|
178
|
+
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
185
179
|
{
|
186
|
-
|
187
|
-
|
188
|
-
VALUE exception;
|
189
|
-
int exception_tag;
|
190
|
-
|
191
|
-
int fd, ret, retry;
|
192
|
-
bool valid_cache;
|
193
|
-
bool writable;
|
194
|
-
uint32_t data_size;
|
195
|
-
struct xattr_key cache_key;
|
196
|
-
struct stat statbuf;
|
197
|
-
char * contents;
|
180
|
+
Check_Type(cachedir_v, T_STRING);
|
181
|
+
Check_Type(path_v, T_STRING);
|
198
182
|
|
199
|
-
|
200
|
-
|
201
|
-
VALUE output_data; /* return data, e.g. ruby hash or loaded iseq */
|
202
|
-
|
203
|
-
/* don't leak memory */
|
204
|
-
#define return error!
|
205
|
-
#define rb_raise error!
|
206
|
-
|
207
|
-
retry = 0;
|
208
|
-
begin:
|
209
|
-
output_data = Qnil;
|
210
|
-
contents = 0;
|
211
|
-
|
212
|
-
/* Blow up if we can't turn our argument into a char* */
|
213
|
-
Check_Type(pathval, T_STRING);
|
214
|
-
path = RSTRING_PTR(pathval);
|
215
|
-
|
216
|
-
/* open the file, get its mtime and read the cache key xattr */
|
217
|
-
CHECK_C(fd = bs_open(path, &writable), "open");
|
218
|
-
CHECK_C( fstat(fd, &statbuf), "fstat");
|
219
|
-
CHECK_C(valid_cache = bs_get_cache(fd, &cache_key), "fgetxattr");
|
220
|
-
|
221
|
-
/* `valid_cache` is true if the cache key isn't trivially invalid, e.g. built
|
222
|
-
* with a different RUBY_REVISION */
|
223
|
-
if (valid_cache && cache_key.mtime == (uint64_t)statbuf.st_mtime) {
|
224
|
-
/* if the mtimes match, assume the cache is valid. fetch the cached data. */
|
225
|
-
ret = bs_fetch_data(fd, (size_t)cache_key.data_size, handler, &output_data, &exception_tag);
|
226
|
-
if (ret == -1 && errno == _ENOATTR) {
|
227
|
-
/* the key was present, but the data was missing. remove the key, and
|
228
|
-
* start over */
|
229
|
-
CHECK_C(fremovexattr(fd, xattr_key_name REMOVEXATTR_TRAILER), "fremovexattr");
|
230
|
-
goto retry;
|
231
|
-
}
|
232
|
-
CHECK_RB0();
|
233
|
-
CHECK_C(ret, "fgetxattr/fetch-data");
|
234
|
-
if (!NIL_P(output_data)) {
|
235
|
-
stats.hit++;
|
236
|
-
SUCCEED(output_data); /* this is the fast-path to shoot for */
|
237
|
-
}
|
238
|
-
valid_cache = false; /* invalid cache; we'll want to regenerate it */
|
183
|
+
if (RSTRING_LEN(cachedir_v) > MAX_CACHEDIR_SIZE) {
|
184
|
+
rb_raise(rb_eArgError, "cachedir too long");
|
239
185
|
}
|
240
186
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
/* we need to pass this char* to ruby-land */
|
245
|
-
input_data = rb_str_new_static(contents, statbuf.st_size);
|
187
|
+
char * cachedir = RSTRING_PTR(cachedir_v);
|
188
|
+
char * path = RSTRING_PTR(path_v);
|
189
|
+
char cache_path[MAX_CACHEPATH_SIZE];
|
246
190
|
|
247
|
-
/*
|
248
|
-
|
249
|
-
|
250
|
-
if (!writable) {
|
251
|
-
stats.unwritable++;
|
252
|
-
CHECK_RB(bs_input_to_output(handler, input_data, &output_data, &exception_tag));
|
253
|
-
SUCCEED(output_data);
|
191
|
+
{ /* generate cache path to cache_path */
|
192
|
+
char * tmp = (char *)&cache_path;
|
193
|
+
bs_cache_path(cachedir, path, &tmp);
|
254
194
|
}
|
255
195
|
|
256
|
-
|
257
|
-
|
258
|
-
* to be updated. */
|
259
|
-
stats.miss++;
|
196
|
+
return bs_fetch(path, path_v, cache_path, handler);
|
197
|
+
}
|
260
198
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
* we just call the input_to_output handler method, bypassing the storage format. */
|
267
|
-
CHECK_RB(bs_input_to_output(handler, input_data, &output_data, &exception_tag));
|
268
|
-
stats.uncompilable++;
|
269
|
-
SUCCEED(output_data);
|
270
|
-
}
|
199
|
+
static int
|
200
|
+
open_current_file(char * path, struct bs_cache_key * key)
|
201
|
+
{
|
202
|
+
struct stat statbuf;
|
203
|
+
int fd;
|
271
204
|
|
272
|
-
|
273
|
-
if (
|
274
|
-
goto invalid_type_storage_data;
|
275
|
-
}
|
205
|
+
fd = open(path, O_RDONLY);
|
206
|
+
if (fd < 0) return fd;
|
276
207
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
fprintf(stderr, "[OPT_AOT_LOG] warning: compiled artifact is over 64MB, which is too large to store in an xattr.%s\n", path);
|
281
|
-
}
|
282
|
-
CHECK_RB(bs_input_to_output(handler, input_data, &output_data, &exception_tag));
|
283
|
-
SUCCEED(output_data);
|
208
|
+
if (fstat(fd, &statbuf) < 0) {
|
209
|
+
close(fd);
|
210
|
+
return -1;
|
284
211
|
}
|
285
212
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
213
|
+
key->version = current_version;
|
214
|
+
key->os_version = current_os_version;
|
215
|
+
key->compile_option = current_compile_option_crc32;
|
216
|
+
key->ruby_revision = current_ruby_revision;
|
217
|
+
key->size = (uint64_t)statbuf.st_size;
|
218
|
+
key->mtime = (uint64_t)statbuf.st_mtime;
|
292
219
|
|
293
|
-
|
294
|
-
|
220
|
+
return fd;
|
221
|
+
}
|
295
222
|
|
296
|
-
|
297
|
-
|
223
|
+
#define ERROR_WITH_ERRNO -1
|
224
|
+
#define CACHE_MISSING_OR_INVALID -2
|
298
225
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
226
|
+
/*
|
227
|
+
* @return CACHE_MISSING_OR_INVALID
|
228
|
+
* @return ERROR_WITH_ERRNO(-1) and errno
|
229
|
+
*/
|
230
|
+
static int
|
231
|
+
bs_read_key(int fd, struct bs_cache_key * key)
|
232
|
+
{
|
233
|
+
ssize_t nread = read(fd, key, KEY_SIZE);
|
234
|
+
if (nread < 0) return ERROR_WITH_ERRNO;
|
235
|
+
if (nread < KEY_SIZE) return CACHE_MISSING_OR_INVALID;
|
236
|
+
return 0;
|
237
|
+
}
|
306
238
|
|
307
|
-
|
239
|
+
/*
|
240
|
+
* @return CACHE_MISSING_OR_INVALID
|
241
|
+
* @return ERROR_WITH_ERRNO(-1) and errno
|
242
|
+
*/
|
243
|
+
static int
|
244
|
+
open_cache_file(const char * path, struct bs_cache_key * key)
|
245
|
+
{
|
246
|
+
int fd, res;
|
308
247
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
248
|
+
fd = open(path, O_RDWR, 0644);
|
249
|
+
if (fd < 0) {
|
250
|
+
if (errno == ENOENT) return CACHE_MISSING_OR_INVALID;
|
251
|
+
return ERROR_WITH_ERRNO;
|
252
|
+
}
|
314
253
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
fail:
|
320
|
-
CLEANUP;
|
321
|
-
stats.fail++;
|
322
|
-
rb_exc_raise(exception);
|
323
|
-
__builtin_unreachable();
|
324
|
-
invalid_type_storage_data:
|
325
|
-
CLEANUP;
|
326
|
-
stats.fail++;
|
327
|
-
Check_Type(storage_data, T_STRING);
|
328
|
-
__builtin_unreachable();
|
329
|
-
retry:
|
330
|
-
CLEANUP;
|
331
|
-
stats.retry++;
|
332
|
-
if (retry == 1) {
|
333
|
-
rb_raise(rb_eRuntimeError, "internal error in bootsnap");
|
334
|
-
__builtin_unreachable();
|
254
|
+
res = bs_read_key(fd, key);
|
255
|
+
if (res < 0) {
|
256
|
+
close(fd);
|
257
|
+
return res;
|
335
258
|
}
|
336
|
-
|
337
|
-
|
338
|
-
raise:
|
339
|
-
CLEANUP;
|
340
|
-
stats.fail++;
|
341
|
-
rb_jump_tag(exception_tag);
|
342
|
-
__builtin_unreachable();
|
259
|
+
|
260
|
+
return fd;
|
343
261
|
}
|
344
262
|
|
345
263
|
static int
|
346
|
-
|
264
|
+
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag)
|
347
265
|
{
|
266
|
+
char * data;
|
267
|
+
ssize_t nread;
|
348
268
|
int ret;
|
349
|
-
ssize_t nbytes;
|
350
|
-
void * xattr_data;
|
351
|
-
VALUE storage_data;
|
352
269
|
|
353
|
-
|
354
|
-
*exception_tag = 0;
|
270
|
+
VALUE storage_data;
|
355
271
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
ret = -1;
|
360
|
-
goto done;
|
272
|
+
if (data_size > 100000000000) {
|
273
|
+
errno = EINVAL; /* because wtf? */
|
274
|
+
return -1;
|
361
275
|
}
|
362
|
-
|
363
|
-
|
276
|
+
data = ALLOC_N(char, data_size);
|
277
|
+
nread = read(fd, data, data_size);
|
278
|
+
if (nread < 0) {
|
364
279
|
ret = -1;
|
365
280
|
goto done;
|
366
281
|
}
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
*exception_tag = ret;
|
371
|
-
errno = 0;
|
282
|
+
if (nread != data_size) {
|
283
|
+
ret = CACHE_MISSING_OR_INVALID;
|
284
|
+
goto done;
|
372
285
|
}
|
286
|
+
|
287
|
+
storage_data = rb_str_new_static(data, data_size);
|
288
|
+
|
289
|
+
*exception_tag = bs_storage_to_output(handler, storage_data, output_data);
|
290
|
+
ret = 0;
|
373
291
|
done:
|
374
|
-
xfree(
|
292
|
+
xfree(data);
|
375
293
|
return ret;
|
376
294
|
}
|
377
295
|
|
378
|
-
static
|
379
|
-
|
296
|
+
static ssize_t
|
297
|
+
bs_read_contents(int fd, size_t size, char ** contents)
|
380
298
|
{
|
381
|
-
|
382
|
-
|
383
|
-
xattr_key = (struct xattr_key){
|
384
|
-
.version = current_version,
|
385
|
-
.os_version = os_version,
|
386
|
-
.data_size = data_size,
|
387
|
-
.compile_option = current_compile_option_crc32,
|
388
|
-
.ruby_revision = current_ruby_revision,
|
389
|
-
.mtime = current_mtime,
|
390
|
-
};
|
391
|
-
|
392
|
-
return fsetxattr(fd, xattr_key_name, &xattr_key, (size_t)xattr_key_size, 0 SETXATTR_TRAILER);
|
299
|
+
*contents = ALLOC_N(char, size);
|
300
|
+
return read(fd, *contents, size);
|
393
301
|
}
|
394
302
|
|
395
|
-
/*
|
396
|
-
* Open the file O_RDWR if possible, or O_RDONLY if that throws EACCES.
|
397
|
-
* Set +writable+ to indicate which mode was used.
|
398
|
-
*/
|
399
303
|
static int
|
400
|
-
|
304
|
+
mkpath(char * file_path, mode_t mode)
|
401
305
|
{
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
*
|
408
|
-
if (
|
409
|
-
|
306
|
+
/* It would likely be more efficient to count back until we
|
307
|
+
* find a component that *does* exist, but this will only run
|
308
|
+
* at most 256 times, so it seems not worthwhile to change. */
|
309
|
+
char * p;
|
310
|
+
for (p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) {
|
311
|
+
*p = '\0';
|
312
|
+
if (mkdir(file_path, mode) == -1) {
|
313
|
+
if (errno != EEXIST) {
|
314
|
+
*p = '/';
|
315
|
+
return -1;
|
316
|
+
}
|
410
317
|
}
|
411
|
-
|
318
|
+
*p = '/';
|
412
319
|
}
|
413
|
-
return
|
320
|
+
return 0;
|
414
321
|
}
|
415
322
|
|
416
|
-
/*
|
417
|
-
* Fetch the cache key from the relevant xattr into +key+.
|
418
|
-
* Returns:
|
419
|
-
* 0: invalid/no cache
|
420
|
-
* 1: valid cache
|
421
|
-
* -1: fgetxattr failed, errno is set
|
422
|
-
*/
|
423
323
|
static int
|
424
|
-
|
324
|
+
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data)
|
425
325
|
{
|
426
|
-
|
326
|
+
char template[MAX_CACHEPATH_SIZE + 20];
|
327
|
+
char * dest;
|
328
|
+
char * tmp_path;
|
329
|
+
int fd;
|
330
|
+
ssize_t nwrite;
|
427
331
|
|
428
|
-
|
429
|
-
|
332
|
+
dest = strncpy(template, path, MAX_CACHEPATH_SIZE);
|
333
|
+
strcat(dest, ".tmp.XXXXXX");
|
334
|
+
|
335
|
+
tmp_path = mktemp(template);
|
336
|
+
fd = open(tmp_path, O_WRONLY | O_CREAT, 0644);
|
337
|
+
if (fd < 0) {
|
338
|
+
if (mkpath(path, 0755) < 0) return -1;
|
339
|
+
fd = open(tmp_path, O_WRONLY | O_CREAT, 0644);
|
340
|
+
if (fd < 0) return -1;
|
341
|
+
}
|
342
|
+
|
343
|
+
key->data_size = RSTRING_LEN(data);
|
344
|
+
nwrite = write(fd, key, KEY_SIZE);
|
345
|
+
if (nwrite < 0) return -1;
|
346
|
+
if (nwrite != KEY_SIZE) {
|
347
|
+
errno = EIO; /* Lies but whatever */
|
430
348
|
return -1;
|
431
349
|
}
|
432
350
|
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
}
|
351
|
+
nwrite = write(fd, RSTRING_PTR(data), RSTRING_LEN(data));
|
352
|
+
if (nwrite < 0) return -1;
|
353
|
+
if (nwrite != RSTRING_LEN(data)) {
|
354
|
+
errno = EIO; /* Lies but whatever */
|
355
|
+
return -1;
|
356
|
+
}
|
439
357
|
|
440
|
-
|
441
|
-
|
442
|
-
* contents must be freed with xfree() when done.
|
443
|
-
*/
|
444
|
-
static size_t
|
445
|
-
bs_read_contents(int fd, size_t size, char ** contents)
|
446
|
-
{
|
447
|
-
*contents = ALLOC_N(char, size);
|
448
|
-
return read(fd, *contents, size);
|
358
|
+
close(fd);
|
359
|
+
return rename(tmp_path, path);
|
449
360
|
}
|
450
361
|
|
451
|
-
static
|
452
|
-
|
362
|
+
static VALUE
|
363
|
+
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
453
364
|
{
|
454
|
-
struct
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
365
|
+
struct bs_cache_key cached_key, current_key;
|
366
|
+
char * contents = NULL;
|
367
|
+
int cache_fd = -1, current_fd = -1;
|
368
|
+
int res, valid_cache, exception_tag = 0;
|
369
|
+
|
370
|
+
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
371
|
+
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
372
|
+
VALUE output_data; /* return data, e.g. ruby hash or loaded iseq */
|
373
|
+
|
374
|
+
VALUE exception;
|
375
|
+
|
376
|
+
current_fd = open_current_file(path, ¤t_key);
|
377
|
+
if (current_fd < 0) goto fail_errno;
|
378
|
+
|
379
|
+
cache_fd = open_cache_file(cache_path, &cached_key);
|
380
|
+
if (cache_fd < 0 && cache_fd != CACHE_MISSING_OR_INVALID) goto fail_errno;
|
381
|
+
|
382
|
+
valid_cache = cache_key_equal(¤t_key, &cached_key);
|
383
|
+
|
384
|
+
if (valid_cache) {
|
385
|
+
/* Fetch the cache data and return it if we're able to load it successfully */
|
386
|
+
res = fetch_cached_data(cache_fd, (ssize_t)cached_key.data_size, handler, &output_data, &exception_tag);
|
387
|
+
if (exception_tag != 0) goto raise;
|
388
|
+
else if (res == CACHE_MISSING_OR_INVALID) valid_cache = 0;
|
389
|
+
else if (res == ERROR_WITH_ERRNO) goto fail_errno;
|
390
|
+
else if (!NIL_P(output_data)) goto succeed; /* fast-path, goal */
|
391
|
+
}
|
392
|
+
close(cache_fd);
|
393
|
+
cache_fd = -1;
|
394
|
+
/* Cache is stale, invalid, or missing. Regenerate and write it out. */
|
395
|
+
|
396
|
+
if (bs_read_contents(current_fd, current_key.size, &contents) < 0) goto fail_errno;
|
397
|
+
input_data = rb_str_new_static(contents, current_key.size);
|
398
|
+
|
399
|
+
exception_tag = bs_input_to_storage(handler, input_data, path_v, &storage_data);
|
400
|
+
if (exception_tag != 0) goto raise;
|
401
|
+
if (storage_data == uncompilable) {
|
402
|
+
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
403
|
+
if (exception_tag != 0) goto raise;
|
404
|
+
goto succeed;
|
460
405
|
}
|
461
|
-
|
462
|
-
|
406
|
+
if (!RB_TYPE_P(storage_data, T_STRING)) goto invalid_type_storage_data;
|
407
|
+
|
408
|
+
res = atomic_write_cache_file(cache_path, ¤t_key, storage_data);
|
409
|
+
if (res < 0) goto fail_errno;
|
410
|
+
|
411
|
+
exception_tag = bs_storage_to_output(handler, storage_data, &output_data);
|
412
|
+
if (exception_tag != 0) goto raise;
|
413
|
+
|
414
|
+
if (NIL_P(output_data)) {
|
415
|
+
if (unlink(cache_path) < 0) goto fail_errno;
|
416
|
+
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
417
|
+
if (exception_tag != 0) goto raise;
|
418
|
+
}
|
419
|
+
// output_data is now the correct return; cascade into succeed:
|
420
|
+
|
421
|
+
#define CLEANUP \
|
422
|
+
if (contents != NULL) xfree(contents); \
|
423
|
+
if (current_fd >= 0) close(current_fd); \
|
424
|
+
if (cache_fd >= 0) close(cache_fd);
|
425
|
+
|
426
|
+
succeed:
|
427
|
+
CLEANUP;
|
428
|
+
return output_data;
|
429
|
+
fail_errno:
|
430
|
+
CLEANUP;
|
431
|
+
exception = rb_protect(prot_exception_for_errno, INT2FIX(errno), &res);
|
432
|
+
if (res) exception = rb_eStandardError;
|
433
|
+
rb_exc_raise(exception);
|
434
|
+
__builtin_unreachable();
|
435
|
+
raise:
|
436
|
+
CLEANUP;
|
437
|
+
rb_jump_tag(exception_tag);
|
438
|
+
__builtin_unreachable();
|
439
|
+
invalid_type_storage_data:
|
440
|
+
CLEANUP;
|
441
|
+
Check_Type(storage_data, T_STRING);
|
442
|
+
__builtin_unreachable();
|
443
|
+
|
444
|
+
#undef CLEANUP
|
463
445
|
}
|
464
446
|
|
465
447
|
static VALUE
|
@@ -474,11 +456,28 @@ prot_exception_for_errno(VALUE err)
|
|
474
456
|
return rb_eStandardError;
|
475
457
|
}
|
476
458
|
|
459
|
+
|
460
|
+
/*****************************************************************************/
|
461
|
+
/********************* Handler Wrappers **************************************/
|
462
|
+
/*****************************************************************************/
|
463
|
+
|
477
464
|
static VALUE
|
478
|
-
|
465
|
+
prot_storage_to_output(VALUE arg)
|
479
466
|
{
|
480
|
-
struct
|
481
|
-
return rb_funcall(data->handler, rb_intern("
|
467
|
+
struct s2o_data * data = (struct s2o_data *)arg;
|
468
|
+
return rb_funcall(data->handler, rb_intern("storage_to_output"), 1, data->storage_data);
|
469
|
+
}
|
470
|
+
|
471
|
+
static int
|
472
|
+
bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
|
473
|
+
{
|
474
|
+
int state;
|
475
|
+
struct s2o_data s2o_data = {
|
476
|
+
.handler = handler,
|
477
|
+
.storage_data = storage_data,
|
478
|
+
};
|
479
|
+
*output_data = rb_protect(prot_storage_to_output, (VALUE)&s2o_data, &state);
|
480
|
+
return state;
|
482
481
|
}
|
483
482
|
|
484
483
|
static void
|
@@ -491,6 +490,13 @@ bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * e
|
|
491
490
|
*output_data = rb_protect(prot_input_to_output, (VALUE)&i2o_data, exception_tag);
|
492
491
|
}
|
493
492
|
|
493
|
+
static VALUE
|
494
|
+
prot_input_to_output(VALUE arg)
|
495
|
+
{
|
496
|
+
struct i2o_data * data = (struct i2o_data *)arg;
|
497
|
+
return rb_funcall(data->handler, rb_intern("input_to_output"), 1, data->input_data);
|
498
|
+
}
|
499
|
+
|
494
500
|
static VALUE
|
495
501
|
try_input_to_storage(VALUE arg)
|
496
502
|
{
|
@@ -526,36 +532,3 @@ bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * stor
|
|
526
532
|
*storage_data = rb_protect(prot_input_to_storage, (VALUE)&i2s_data, &state);
|
527
533
|
return state;
|
528
534
|
}
|
529
|
-
|
530
|
-
static VALUE
|
531
|
-
prot_storage_to_output(VALUE arg)
|
532
|
-
{
|
533
|
-
struct s2o_data * data = (struct s2o_data *)arg;
|
534
|
-
return rb_funcall(data->handler, rb_intern("storage_to_output"), 1, data->storage_data);
|
535
|
-
}
|
536
|
-
|
537
|
-
static int
|
538
|
-
bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
|
539
|
-
{
|
540
|
-
int state;
|
541
|
-
struct s2o_data s2o_data = {
|
542
|
-
.handler = handler,
|
543
|
-
.storage_data = storage_data,
|
544
|
-
};
|
545
|
-
*output_data = rb_protect(prot_storage_to_output, (VALUE)&s2o_data, &state);
|
546
|
-
return state;
|
547
|
-
}
|
548
|
-
|
549
|
-
/* default no if empty, yes if present, no if "0" */
|
550
|
-
static int
|
551
|
-
logging_enabled()
|
552
|
-
{
|
553
|
-
char * log = getenv("OPT_AOT_LOG");
|
554
|
-
if (log == 0) {
|
555
|
-
return 0;
|
556
|
-
} else if (log[0] == '0') {
|
557
|
-
return 0;
|
558
|
-
} else {
|
559
|
-
return 1;
|
560
|
-
}
|
561
|
-
}
|
data/ext/bootsnap/bootsnap.h
CHANGED
data/lib/bootsnap.rb
CHANGED
@@ -3,13 +3,13 @@ require_relative 'compile_cache/yaml'
|
|
3
3
|
|
4
4
|
module Bootsnap
|
5
5
|
module CompileCache
|
6
|
-
def self.setup(iseq:, yaml:)
|
6
|
+
def self.setup(cache_dir:, iseq:, yaml:)
|
7
7
|
if iseq
|
8
|
-
Bootsnap::CompileCache::ISeq.install!
|
8
|
+
Bootsnap::CompileCache::ISeq.install!(cache_dir)
|
9
9
|
end
|
10
10
|
|
11
11
|
if yaml
|
12
|
-
Bootsnap::CompileCache::YAML.install!
|
12
|
+
Bootsnap::CompileCache::YAML.install!(cache_dir)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -4,6 +4,10 @@ require 'zlib'
|
|
4
4
|
module Bootsnap
|
5
5
|
module CompileCache
|
6
6
|
module ISeq
|
7
|
+
class << self
|
8
|
+
attr_accessor :cache_dir
|
9
|
+
end
|
10
|
+
|
7
11
|
def self.input_to_storage(_, path)
|
8
12
|
RubyVM::InstructionSequence.compile_file(path).to_binary
|
9
13
|
rescue SyntaxError
|
@@ -28,6 +32,7 @@ module Bootsnap
|
|
28
32
|
module InstructionSequenceMixin
|
29
33
|
def load_iseq(path)
|
30
34
|
Bootsnap::CompileCache::Native.fetch(
|
35
|
+
Bootsnap::CompileCache::ISeq.cache_dir,
|
31
36
|
path.to_s,
|
32
37
|
Bootsnap::CompileCache::ISeq
|
33
38
|
)
|
@@ -62,7 +67,8 @@ module Bootsnap
|
|
62
67
|
Bootsnap::CompileCache::Native.compile_option_crc32 = crc
|
63
68
|
end
|
64
69
|
|
65
|
-
def self.install!
|
70
|
+
def self.install!(cache_dir)
|
71
|
+
Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
|
66
72
|
Bootsnap::CompileCache::ISeq.compile_option_updated
|
67
73
|
class << RubyVM::InstructionSequence
|
68
74
|
prepend InstructionSequenceMixin
|
@@ -30,7 +30,7 @@ module Bootsnap
|
|
30
30
|
::YAML.load(data)
|
31
31
|
end
|
32
32
|
|
33
|
-
def self.install!
|
33
|
+
def self.install!(cache_dir)
|
34
34
|
require 'yaml'
|
35
35
|
require 'msgpack'
|
36
36
|
|
@@ -44,6 +44,7 @@ module Bootsnap
|
|
44
44
|
klass = class << ::YAML; self; end
|
45
45
|
klass.send(:define_method, :load_file) do |path|
|
46
46
|
Bootsnap::CompileCache::Native.fetch(
|
47
|
+
cache_dir,
|
47
48
|
path.to_s,
|
48
49
|
Bootsnap::CompileCache::YAML
|
49
50
|
)
|
data/lib/bootsnap/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bootsnap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-05-
|
11
|
+
date: 2017-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -162,9 +162,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
162
162
|
version: 2.3.0
|
163
163
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
164
|
requirements:
|
165
|
-
- - "
|
165
|
+
- - ">"
|
166
166
|
- !ruby/object:Gem::Version
|
167
|
-
version:
|
167
|
+
version: 1.3.1
|
168
168
|
requirements: []
|
169
169
|
rubyforge_project:
|
170
170
|
rubygems_version: 2.6.10
|