bootsnap 0.2.15 → 0.3.0.pre
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/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
|
-

|
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
|