bootsnap 0.3.0.pre → 0.3.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '082f8dd8be53486b79ff5248ad426cc28263778b'
4
- data.tar.gz: 5bc6a79f2054aa8b869704cdb9aeab685eabb400
3
+ metadata.gz: aa9caa4faf08302d5050a4d6f41e90920732beed
4
+ data.tar.gz: 60d4a539b9065e0771be2c330a614d4bd5fb8efe
5
5
  SHA512:
6
- metadata.gz: ad062964bbc28d997901a1f6016289a4c6671071f9e1c4640820fe9545427136e1268f6588b350e7d058d85641604b0004225a744792fcce9b24bfcb9e5a3172
7
- data.tar.gz: 33c77f2e085ba965e1a9fccb65b5afd42a4db0485dbfca7aa5d3e3920497ff41d0a3ca0ca4b6706df55814d29f015056e6d862568fe8b48ad5194f6eb80e26dc
6
+ metadata.gz: 383af9b805278c8d682c368b52b1ec14dd8c1d391692422c269fc9f86d9199aeee3aecdf8e33e1f369ba028316633e97d58d20da176eae74d2d0879dec14a801
7
+ data.tar.gz: 307d8dda6d264b1179ee2a955f3fa45c9c01add22233934a333a9a50b122e3d5aa7cfb80199611d35e0e0518091c474c5f3b35d5f0c88912e5fe78e036df0977
@@ -1,3 +1,8 @@
1
+ # 0.3.0 (pre)
2
+
3
+ * Migrate CompileCache from xattr as a cache backend to a cache directory
4
+ * Adds support for Linux and FreeBSD
5
+
1
6
  # 0.2.15
2
7
 
3
8
  * Support more versions of ActiveSupport (`depend_on`'s signature varies; don't reiterate it)
@@ -1,3 +1,16 @@
1
+ /*
2
+ * Suggested reading order:
3
+ * 1. Skim Init_bootsnap
4
+ * 2. Skim bs_fetch
5
+ * 3. The rest of everything
6
+ *
7
+ * Init_bootsnap sets up the ruby objects and binds bs_fetch to
8
+ * Bootsnap::CompileCache::Native.fetch.
9
+ *
10
+ * bs_fetch is the ultimate caller for for just about every other function in
11
+ * here.
12
+ */
13
+
1
14
  #include "bootsnap.h"
2
15
  #include "ruby.h"
3
16
  #include <stdint.h>
@@ -5,18 +18,29 @@
5
18
  #include <errno.h>
6
19
  #include <fcntl.h>
7
20
  #include <sys/stat.h>
21
+ #include <sys/utsname.h>
8
22
 
9
- #ifdef __linux__
10
- #include <gnu/libc-version.h>
11
- #else
12
- #include <sys/sysctl.h>
13
- #endif
14
-
23
+ /* 1000 is an arbitrary limit; FNV64 plus some slashes brings the cap down to
24
+ * 981 for the cache dir */
15
25
  #define MAX_CACHEPATH_SIZE 1000
16
26
  #define MAX_CACHEDIR_SIZE 981
17
27
 
18
28
  #define KEY_SIZE 64
19
29
 
30
+ /*
31
+ * An instance of this key is written as the first 64 bytes of each cache file.
32
+ * The mtime and size members track whether the file contents have changed, and
33
+ * the version, os_version, compile_option, and ruby_revision members track
34
+ * changes to the environment that could invalidate compile results without
35
+ * file contents having changed. The data_size member is not truly part of the
36
+ * "key". Really, this could be called a "header" with the first six members
37
+ * being an embedded "key" struct and an additional data_size member.
38
+ *
39
+ * The data_size indicates the remaining number of bytes in the cache file
40
+ * after the header (the size of the cached artifact).
41
+ *
42
+ * After data_size, the struct is padded to 64 bytes.
43
+ */
20
44
  struct bs_cache_key {
21
45
  uint32_t version;
22
46
  uint32_t os_version;
@@ -28,31 +52,40 @@ struct bs_cache_key {
28
52
  uint8_t pad[24];
29
53
  } __attribute__((packed));
30
54
 
55
+ /*
56
+ * If the struct padding isn't correct to pad the key to 64 bytes, refuse to
57
+ * compile.
58
+ */
31
59
  #define STATIC_ASSERT(X) STATIC_ASSERT2(X,__LINE__)
32
60
  #define STATIC_ASSERT2(X,L) STATIC_ASSERT3(X,L)
33
61
  #define STATIC_ASSERT3(X,L) STATIC_ASSERT_MSG(X,at_line_##L)
34
62
  #define STATIC_ASSERT_MSG(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1]
35
-
36
63
  STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
37
64
 
65
+ /* Effectively a schema version. Bumping invalidates all previous caches */
38
66
  static const uint32_t current_version = 2;
39
67
 
68
+ /* Derived from kernel or libc version; intended to roughly correspond to when
69
+ * ABIs have changed, requiring recompilation of native gems. */
40
70
  static uint32_t current_os_version;
71
+ /* Invalidates cache when switching ruby versions */
41
72
  static uint32_t current_ruby_revision;
73
+ /* Invalidates cache when RubyVM::InstructionSequence.compile_option changes */
42
74
  static uint32_t current_compile_option_crc32 = 0;
43
75
 
76
+ /* Bootsnap::CompileCache::{Native, Uncompilable} */
44
77
  static VALUE rb_mBootsnap;
45
78
  static VALUE rb_mBootsnap_CompileCache;
46
79
  static VALUE rb_mBootsnap_CompileCache_Native;
47
80
  static VALUE rb_eBootsnap_CompileCache_Uncompilable;
48
81
  static ID uncompilable;
49
82
 
50
- /* Real API */
83
+ /* Functions exposed as module functions on Bootsnap::CompileCache::Native */
51
84
  static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
52
85
  static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
53
86
 
54
87
  /* Helpers */
55
- static uint64_t fnv_64a(const char *str);
88
+ static uint64_t fnv1a_64(const char *str);
56
89
  static void bs_cache_path(const char * cachedir, const char * path, char ** cache_path);
57
90
  static int bs_read_key(int fd, struct bs_cache_key * key);
58
91
  static int cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2);
@@ -62,29 +95,27 @@ static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * o
62
95
  static VALUE prot_exception_for_errno(VALUE err);
63
96
  static uint32_t get_os_version(void);
64
97
 
98
+ /*
99
+ * Helper functions to call ruby methods on handler object without crashing on
100
+ * exception.
101
+ */
65
102
  static int bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data);
66
103
  static VALUE prot_storage_to_output(VALUE arg);
67
104
  static VALUE prot_input_to_output(VALUE arg);
68
105
  static void bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag);
69
106
  static VALUE prot_input_to_storage(VALUE arg);
70
107
  static int bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data);
108
+ struct s2o_data;
109
+ struct i2o_data;
110
+ struct i2s_data;
71
111
 
72
- struct s2o_data {
73
- VALUE handler;
74
- VALUE storage_data;
75
- };
76
-
77
- struct i2o_data {
78
- VALUE handler;
79
- VALUE input_data;
80
- };
81
-
82
- struct i2s_data {
83
- VALUE handler;
84
- VALUE input_data;
85
- VALUE pathval;
86
- };
87
-
112
+ /*
113
+ * Ruby C extensions are initialized by calling Init_<extname>.
114
+ *
115
+ * This sets up the module hierarchy and attaches functions as methods.
116
+ *
117
+ * We also populate some semi-static information about the current OS and so on.
118
+ */
88
119
  void
89
120
  Init_bootsnap(void)
90
121
  {
@@ -102,6 +133,10 @@ Init_bootsnap(void)
102
133
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
103
134
  }
104
135
 
136
+ /*
137
+ * Bootsnap's ruby code registers a hook that notifies us via this function
138
+ * when compile_option changes. These changes invalidate all existing caches.
139
+ */
105
140
  static VALUE
106
141
  bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
107
142
  {
@@ -110,11 +145,21 @@ bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
110
145
  return Qnil;
111
146
  }
112
147
 
148
+ /*
149
+ * We use FNV1a-64 to derive cache paths. The choice is somewhat arbitrary but
150
+ * it has several nice properties:
151
+ *
152
+ * - Tiny implementation
153
+ * - No external dependency
154
+ * - Solid performance
155
+ * - Solid randomness
156
+ * - 32 bits doesn't feel collision-resistant enough; 64 is nice.
157
+ */
113
158
  static uint64_t
114
- fnv_64a(const char *str)
159
+ fnv1a_64(const char *str)
115
160
  {
116
161
  unsigned char *s = (unsigned char *)str;
117
- uint64_t h = ((uint64_t)0xcbf29ce484222325ULL);
162
+ uint64_t h = (uint64_t)0xcbf29ce484222325ULL;
118
163
 
119
164
  while (*s) {
120
165
  h ^= (uint64_t)*s++;
@@ -124,36 +169,44 @@ fnv_64a(const char *str)
124
169
  return h;
125
170
  }
126
171
 
127
- /* On Darwin and FreeBSD, this is the kernel version, since that tends to vary
172
+ /*
173
+ * The idea here is that we want a cache key member that changes when the OS
174
+ * changes in such a way as to make existing compiled ISeqs unloadable.
175
+ *
176
+ * On Darwin and FreeBSD, this is the kernel version, since that tends to vary
128
177
  * with whole-system upgrades. What we probably care about more is the libc
129
178
  * version, which is what we explicitly ask for on linux.
130
179
  * (and KERN_OSRELEASE came back empty for me one one linux box, so...?)
180
+ *
181
+ * I'm kind of guessing about the important factors here. We could probably do
182
+ * this better.
131
183
  */
132
184
  static uint32_t
133
185
  get_os_version(void)
134
186
  {
135
187
  size_t len;
136
188
  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
189
+ struct utsname utsname;
190
+
191
+ /* Not worth crashing if this fails; lose cache invalidation potential */
192
+ if (uname(&utsname) < 0) return 0;
193
+
194
+ hash = fnv1a_64(utsname.version);
195
+
150
196
  return (uint32_t)(hash >> 32);
151
197
  }
152
198
 
199
+ /*
200
+ * Given a cache root directory and the full path to a file being cached,
201
+ * generate a path under the cache directory at which the cached artifact will
202
+ * be stored.
203
+ *
204
+ * The path will look something like: <cachedir>/12/34567890abcdef
205
+ */
153
206
  static void
154
207
  bs_cache_path(const char * cachedir, const char * path, char ** cache_path)
155
208
  {
156
- uint64_t hash = fnv_64a(path);
209
+ uint64_t hash = fnv1a_64(path);
157
210
 
158
211
  uint8_t first_byte = (hash >> (64 - 8));
159
212
  uint64_t remainder = hash & 0x00ffffffffffffff;
@@ -161,6 +214,14 @@ bs_cache_path(const char * cachedir, const char * path, char ** cache_path)
161
214
  sprintf(*cache_path, "%s/%02x/%014llx", cachedir, first_byte, remainder);
162
215
  }
163
216
 
217
+ /*
218
+ * Test whether a newly-generated cache key based on the file as it exists on
219
+ * disk matches the one that was generated when the file was cached (or really
220
+ * compare any two keys).
221
+ *
222
+ * The data_size member is not compared, as it serves more of a "header"
223
+ * function.
224
+ */
164
225
  static int
165
226
  cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
166
227
  {
@@ -174,6 +235,11 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
174
235
  );
175
236
  }
176
237
 
238
+ /*
239
+ * Entrypoint for Bootsnap::CompileCache::Native.fetch. The real work is done
240
+ * in bs_fetch; this function just performs some basic typechecks and
241
+ * conversions on the ruby VALUE arguments before passing them along.
242
+ */
177
243
  static VALUE
178
244
  bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
179
245
  {
@@ -196,6 +262,10 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
196
262
  return bs_fetch(path, path_v, cache_path, handler);
197
263
  }
198
264
 
265
+ /*
266
+ * Open the file we want to load/cache and generate a cache key for it if it
267
+ * was loaded.
268
+ */
199
269
  static int
200
270
  open_current_file(char * path, struct bs_cache_key * key)
201
271
  {
@@ -224,8 +294,13 @@ open_current_file(char * path, struct bs_cache_key * key)
224
294
  #define CACHE_MISSING_OR_INVALID -2
225
295
 
226
296
  /*
227
- * @return CACHE_MISSING_OR_INVALID
228
- * @return ERROR_WITH_ERRNO(-1) and errno
297
+ * Read the cache key from the given fd, which must have position 0 (e.g.
298
+ * freshly opened file).
299
+ *
300
+ * Possible return values:
301
+ * - 0 (OK, key was loaded)
302
+ * - CACHE_MISSING_OR_INVALID (-2)
303
+ * - ERROR_WITH_ERRNO (-1, errno is set)
229
304
  */
230
305
  static int
231
306
  bs_read_key(int fd, struct bs_cache_key * key)
@@ -237,8 +312,13 @@ bs_read_key(int fd, struct bs_cache_key * key)
237
312
  }
238
313
 
239
314
  /*
240
- * @return CACHE_MISSING_OR_INVALID
241
- * @return ERROR_WITH_ERRNO(-1) and errno
315
+ * Open the cache file at a given path, if it exists, and read its key into the
316
+ * struct.
317
+ *
318
+ * Possible return values:
319
+ * - 0 (OK, key was loaded)
320
+ * - CACHE_MISSING_OR_INVALID (-2)
321
+ * - ERROR_WITH_ERRNO (-1, errno is set)
242
322
  */
243
323
  static int
244
324
  open_cache_file(const char * path, struct bs_cache_key * key)
@@ -260,10 +340,25 @@ open_cache_file(const char * path, struct bs_cache_key * key)
260
340
  return fd;
261
341
  }
262
342
 
343
+ /*
344
+ * The cache file is laid out like:
345
+ * 0...64 : bs_cache_key
346
+ * 64..-1 : cached artifact
347
+ *
348
+ * This function takes a file descriptor whose position is pre-set to 64, and
349
+ * the data_size (corresponding to the remaining number of bytes) listed in the
350
+ * cache header.
351
+ *
352
+ * We load the text from this file into a buffer, and pass it to the ruby-land
353
+ * handler with exception handling via the exception_tag param.
354
+ *
355
+ * Data is returned via the output_data parameter, which, if there's no error
356
+ * or exception, will be the final data returnable to the user.
357
+ */
263
358
  static int
264
359
  fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag)
265
360
  {
266
- char * data;
361
+ char * data = NULL;
267
362
  ssize_t nread;
268
363
  int ret;
269
364
 
@@ -271,7 +366,8 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
271
366
 
272
367
  if (data_size > 100000000000) {
273
368
  errno = EINVAL; /* because wtf? */
274
- return -1;
369
+ ret = -1;
370
+ goto done;
275
371
  }
276
372
  data = ALLOC_N(char, data_size);
277
373
  nread = read(fd, data, data_size);
@@ -289,17 +385,14 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
289
385
  *exception_tag = bs_storage_to_output(handler, storage_data, output_data);
290
386
  ret = 0;
291
387
  done:
292
- xfree(data);
388
+ if (data != NULL) xfree(data);
293
389
  return ret;
294
390
  }
295
391
 
296
- static ssize_t
297
- bs_read_contents(int fd, size_t size, char ** contents)
298
- {
299
- *contents = ALLOC_N(char, size);
300
- return read(fd, *contents, size);
301
- }
302
-
392
+ /*
393
+ * Like mkdir -p, this recursively creates directory parents of a file. e.g.
394
+ * given /a/b/c, creates /a and /a/b.
395
+ */
303
396
  static int
304
397
  mkpath(char * file_path, mode_t mode)
305
398
  {
@@ -320,6 +413,11 @@ mkpath(char * file_path, mode_t mode)
320
413
  return 0;
321
414
  }
322
415
 
416
+ /*
417
+ * Write a cache header/key and a compiled artifact to a given cache path by
418
+ * writing to a tmpfile and then renaming the tmpfile over top of the final
419
+ * path.
420
+ */
323
421
  static int
324
422
  atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data)
325
423
  {
@@ -359,6 +457,76 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data)
359
457
  return rename(tmp_path, path);
360
458
  }
361
459
 
460
+ /*
461
+ * Given an errno value (converted to a ruby Fixnum), return the corresponding
462
+ * Errno::* constant. If none is found, return StandardError instead.
463
+ */
464
+ static VALUE
465
+ prot_exception_for_errno(VALUE err)
466
+ {
467
+ if (err != INT2FIX(0)) {
468
+ VALUE mErrno = rb_const_get(rb_cObject, rb_intern("Errno"));
469
+ VALUE constants = rb_funcall(mErrno, rb_intern("constants"), 0);
470
+ VALUE which = rb_funcall(constants, rb_intern("[]"), 1, err);
471
+ return rb_funcall(mErrno, rb_intern("const_get"), 1, which);
472
+ }
473
+ return rb_eStandardError;
474
+ }
475
+
476
+
477
+ /* Read contents from an fd, whose contents are asserted to be +size+ bytes
478
+ * long, into a buffer */
479
+ static ssize_t
480
+ bs_read_contents(int fd, size_t size, char ** contents)
481
+ {
482
+ *contents = ALLOC_N(char, size);
483
+ return read(fd, *contents, size);
484
+ }
485
+
486
+ /*
487
+ * This is the meat of the extension. bs_fetch is
488
+ * Bootsnap::CompileCache::Native.fetch.
489
+ *
490
+ * There are three "formats" in use here:
491
+ * 1. "input" fomat, which is what we load from the source file;
492
+ * 2. "storage" format, which we write to the cache;
493
+ * 3. "output" format, which is what we return.
494
+ *
495
+ * E.g., For ISeq compilation:
496
+ * input: ruby source, as text
497
+ * storage: binary string (RubyVM::InstructionSequence#to_binary)
498
+ * output: Instance of RubyVM::InstructionSequence
499
+ *
500
+ * And for YAML:
501
+ * input: yaml as text
502
+ * storage: MessagePack or Marshal text
503
+ * output: ruby object, loaded from yaml/messagepack/marshal
504
+ *
505
+ * The handler passed in must support three messages:
506
+ * * storage_to_output(s) -> o
507
+ * * input_to_output(i) -> o
508
+ * * input_to_storage(i) -> s
509
+ * (input_to_storage may raise Bootsnap::CompileCache::Uncompilable, which
510
+ * will prevent caching and cause output to be generated with
511
+ * input_to_output)
512
+ *
513
+ * The semantics of this function are basically:
514
+ *
515
+ * return storage_to_output(cache[path]) if cache[path]
516
+ * storage = input_to_storage(input)
517
+ * cache[path] = storage
518
+ * return storage_to_output(storage)
519
+ *
520
+ * Or expanded a bit:
521
+ *
522
+ * - Check if the cache file exists and is up to date.
523
+ * - If it is, load this data to storage_data.
524
+ * - return storage_to_output(storage_data)
525
+ * - Read the file to input_data
526
+ * - Generate storage_data using input_to_storage(input_data)
527
+ * - Write storage_data data, with a cache key, to the cache file.
528
+ * - Return storage_to_output(storage_data)
529
+ */
362
530
  static VALUE
363
531
  bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
364
532
  {
@@ -371,14 +539,18 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
371
539
  VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
372
540
  VALUE output_data; /* return data, e.g. ruby hash or loaded iseq */
373
541
 
374
- VALUE exception;
542
+ VALUE exception; /* ruby exception object to raise instead of returning */
375
543
 
544
+ /* Open the source file and generate a cache key for it */
376
545
  current_fd = open_current_file(path, &current_key);
377
546
  if (current_fd < 0) goto fail_errno;
378
547
 
548
+ /* Open the cache key if it exists, and read its cache key in */
379
549
  cache_fd = open_cache_file(cache_path, &cached_key);
380
550
  if (cache_fd < 0 && cache_fd != CACHE_MISSING_OR_INVALID) goto fail_errno;
381
551
 
552
+ /* True if the cache existed and no invalidating changes have occurred since
553
+ * it was generated. */
382
554
  valid_cache = cache_key_equal(&current_key, &cached_key);
383
555
 
384
556
  if (valid_cache) {
@@ -393,30 +565,40 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
393
565
  cache_fd = -1;
394
566
  /* Cache is stale, invalid, or missing. Regenerate and write it out. */
395
567
 
568
+ /* Read the contents of the source file into a buffer */
396
569
  if (bs_read_contents(current_fd, current_key.size, &contents) < 0) goto fail_errno;
397
570
  input_data = rb_str_new_static(contents, current_key.size);
398
571
 
572
+ /* Try to compile the input_data using input_to_storage(input_data) */
399
573
  exception_tag = bs_input_to_storage(handler, input_data, path_v, &storage_data);
400
574
  if (exception_tag != 0) goto raise;
575
+ /* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
576
+ * to cache anything; just return input_to_output(input_data) */
401
577
  if (storage_data == uncompilable) {
402
578
  bs_input_to_output(handler, input_data, &output_data, &exception_tag);
403
579
  if (exception_tag != 0) goto raise;
404
580
  goto succeed;
405
581
  }
582
+ /* If storage_data isn't a string, we can't cache it */
406
583
  if (!RB_TYPE_P(storage_data, T_STRING)) goto invalid_type_storage_data;
407
584
 
585
+ /* Write the cache key and storage_data to the cache directory */
408
586
  res = atomic_write_cache_file(cache_path, &current_key, storage_data);
409
587
  if (res < 0) goto fail_errno;
410
588
 
589
+ /* Having written the cache, now convert storage_data to output_data */
411
590
  exception_tag = bs_storage_to_output(handler, storage_data, &output_data);
412
591
  if (exception_tag != 0) goto raise;
413
592
 
593
+ /* If output_data is nil, delete the cache entry and generate the output
594
+ * using input_to_output */
414
595
  if (NIL_P(output_data)) {
415
596
  if (unlink(cache_path) < 0) goto fail_errno;
416
597
  bs_input_to_output(handler, input_data, &output_data, &exception_tag);
417
598
  if (exception_tag != 0) goto raise;
418
599
  }
419
- // output_data is now the correct return; cascade into succeed:
600
+
601
+ goto succeed; /* output_data is now the correct return. */
420
602
 
421
603
  #define CLEANUP \
422
604
  if (contents != NULL) xfree(contents); \
@@ -444,22 +626,28 @@ invalid_type_storage_data:
444
626
  #undef CLEANUP
445
627
  }
446
628
 
447
- static VALUE
448
- prot_exception_for_errno(VALUE err)
449
- {
450
- if (err != INT2FIX(0)) {
451
- VALUE mErrno = rb_const_get(rb_cObject, rb_intern("Errno"));
452
- VALUE constants = rb_funcall(mErrno, rb_intern("constants"), 0);
453
- VALUE which = rb_funcall(constants, rb_intern("[]"), 1, err);
454
- return rb_funcall(mErrno, rb_intern("const_get"), 1, which);
455
- }
456
- return rb_eStandardError;
457
- }
458
-
459
-
460
629
  /*****************************************************************************/
461
630
  /********************* Handler Wrappers **************************************/
462
- /*****************************************************************************/
631
+ /*****************************************************************************
632
+ * Everything after this point in the file is just wrappers to deal with ruby's
633
+ * clunky method of handling exceptions from ruby methods invoked from C.
634
+ */
635
+
636
+ struct s2o_data {
637
+ VALUE handler;
638
+ VALUE storage_data;
639
+ };
640
+
641
+ struct i2o_data {
642
+ VALUE handler;
643
+ VALUE input_data;
644
+ };
645
+
646
+ struct i2s_data {
647
+ VALUE handler;
648
+ VALUE input_data;
649
+ VALUE pathval;
650
+ };
463
651
 
464
652
  static VALUE
465
653
  prot_storage_to_output(VALUE arg)
@@ -1,3 +1,3 @@
1
1
  module Bootsnap
2
- VERSION = "0.3.0.pre"
2
+ VERSION = "0.3.0.pre2"
3
3
  end
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.3.0.pre
4
+ version: 0.3.0.pre2
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-25 00:00:00.000000000 Z
11
+ date: 2017-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler