68#include "ruby/internal/config.h"
75#include "internal/class.h"
76#include "internal/cmdlineopt.h"
77#include "internal/cont.h"
78#include "internal/file.h"
79#include "internal/hash.h"
80#include "internal/process.h"
81#include "internal/warnings.h"
83#include "ractor_core.h"
86#define __EXTENSIONS__ 1
90#include "vm_callinfo.h"
94#include "ruby_assert.h"
100#include "insns_info.inc"
101#include "internal/compile.h"
110#ifdef HAVE_SYS_PARAM_H
111# include <sys/param.h>
119# define MAXPATHLEN 1024
123#define MJIT_ATOMIC_SET(var, val) (void)ATOMIC_PTR_EXCHANGE(var, val)
125#define MJIT_TMP_PREFIX "_ruby_mjit_"
133extern void mjit_add_waiting_pid(
rb_vm_t *vm, rb_pid_t pid);
141bool mjit_enabled =
false;
144bool mjit_call_p =
false;
146bool mjit_cancel_p =
false;
149static bool mjit_compile_p =
false;
151static int active_units_length = 0;
155static struct rb_mjit_unit_list unit_queue = { CCAN_LIST_HEAD_INIT(unit_queue.head) };
157static struct rb_mjit_unit_list active_units = { CCAN_LIST_HEAD_INIT(active_units.head) };
159static struct rb_mjit_unit_list compact_units = { CCAN_LIST_HEAD_INIT(compact_units.head) };
161static struct rb_mjit_unit_list stale_units = { CCAN_LIST_HEAD_INIT(stale_units.head) };
163static int current_unit_num;
167static bool stop_worker_p;
169static bool worker_stopped =
true;
175static const char *cc_path;
177static const char **cc_common_args;
179static char **cc_added_args;
181static char *pch_file;
183static rb_pid_t pch_owner_pid;
186static enum {PCH_NOT_READY, PCH_FAILED, PCH_SUCCESS} pch_status;
189static double current_cc_ms = 0.0;
193static pid_t current_cc_pid = 0;
196static char *header_file;
198#include "mjit_config.h"
200#if defined(__GNUC__) && \
201 (!defined(__clang__) || \
202 (defined(__clang__) && (defined(__FreeBSD__) || defined(__GLIBC__))))
203# define GCC_PIC_FLAGS "-Wfatal-errors", "-fPIC", "-shared", "-w", "-pipe",
204# define MJIT_CFLAGS_PIPE 1
206# define GCC_PIC_FLAGS
207# define MJIT_CFLAGS_PIPE 0
212#if defined __GNUC__ && !defined __clang__ && !defined(__CYGWIN__) && !defined(_AIX) && !defined(__OpenBSD__)
213# define GCC_NOSTDLIB_FLAGS "-nodefaultlibs", "-nostdlib",
215# define GCC_NOSTDLIB_FLAGS
218static const char *
const CC_COMMON_ARGS[] = {
219 MJIT_CC_COMMON MJIT_CFLAGS GCC_PIC_FLAGS
223static const char *
const CC_DEBUG_ARGS[] = {MJIT_DEBUGFLAGS NULL};
224static const char *
const CC_OPTIMIZE_ARGS[] = {MJIT_OPTFLAGS NULL};
226static const char *
const CC_LDSHARED_ARGS[] = {MJIT_LDSHARED MJIT_CFLAGS GCC_PIC_FLAGS NULL};
227static const char *
const CC_DLDFLAGS_ARGS[] = {MJIT_DLDFLAGS NULL};
229static const char *
const CC_LINKER_ARGS[] = {
230#if defined __GNUC__ && !defined __clang__ && !defined(__OpenBSD__)
233 GCC_NOSTDLIB_FLAGS NULL
236static const char *
const CC_LIBS[] = {
237#if defined(__CYGWIN__)
240#if defined __GNUC__ && !defined __clang__
243#if defined __ANDROID__
249#define CC_CODEFLAG_ARGS (mjit_opts.debug ? CC_DEBUG_ARGS : CC_OPTIMIZE_ARGS)
253PRINTF_ARGS(
static void, 2, 3)
254verbose(
int level, const
char *format, ...)
256 if (mjit_opts.verbose >= level) {
258 size_t len = strlen(format);
259 char *full_format = alloca(
sizeof(
char) * (len + 2));
262 memcpy(full_format, format, len);
263 full_format[len] =
'\n';
264 full_format[len+1] =
'\0';
266 va_start(args, format);
267 vfprintf(stderr, full_format, args);
272PRINTF_ARGS(
static void, 1, 2)
273mjit_warning(const
char *format, ...)
275 if (mjit_opts.warnings || mjit_opts.verbose) {
278 fprintf(stderr,
"MJIT warning: ");
279 va_start(args, format);
280 vfprintf(stderr, format, args);
282 fprintf(stderr,
"\n");
291 ccan_list_add_tail(&list->head, &unit->unode);
298 ccan_list_del(&unit->unode);
303remove_file(
const char *filename)
305 if (remove(filename)) {
306 mjit_warning(
"failed to remove \"%s\": %s", filename, strerror(errno));
322 ISEQ_BODY(unit->iseq)->jit_func = (jit_func_t)MJIT_FUNC_FAILED;
323 ISEQ_BODY(unit->iseq)->mjit_unit = NULL;
325 if (unit->cc_entries) {
326 void *entries = (
void *)unit->cc_entries;
329 if (unit->handle && dlclose(unit->handle)) {
330 mjit_warning(
"failed to close handle for u%d: %s", unit->id, dlerror());
337CRITICAL_SECTION_START(
int level,
const char *msg)
339 verbose(level,
"Locking %s", msg);
341 verbose(level,
"Locked %s", msg);
347CRITICAL_SECTION_FINISH(
int level,
const char *msg)
349 verbose(level,
"Unlocked %s", msg);
353static pid_t mjit_pid = 0;
356sprint_uniq_filename(
char *str,
size_t size,
unsigned long id,
const char *prefix,
const char *suffix)
358 return snprintf(str, size,
"%s/%sp%"PRI_PIDT_PREFIX"uu%lu%s", tmp_dir, prefix, mjit_pid,
id, suffix);
363double ruby_real_ms_time(
void);
364# define real_ms_time() ruby_real_ms_time()
369# ifdef HAVE_CLOCK_GETTIME
371# ifdef CLOCK_MONOTONIC
372 const clockid_t c = CLOCK_MONOTONIC;
374 const clockid_t c = CLOCK_REALTIME;
377 clock_gettime(c, &tv);
378 return tv.tv_nsec / 1000000.0 + tv.tv_sec * 1000.0;
382 gettimeofday(&tv, NULL);
383 return tv.tv_usec / 1000.0 + tv.tv_sec * 1000.0;
396 ccan_list_for_each_safe(&list->head, unit, next, unode) {
397 if (unit->iseq == NULL) {
398 remove_from_list(unit, list);
403 if (best == NULL || ISEQ_BODY(best->iseq)->total_calls < ISEQ_BODY(unit->iseq)->total_calls) {
409 remove_from_list(best, list);
416args_len(
char *
const *args)
420 for (i = 0; (args[i]) != NULL;i++)
428form_args(
int num, ...)
433 char **args, **res, **tmp;
437 for (i = len = 0; i < num; i++) {
438 args = va_arg(argp,
char **);
440 if ((tmp = (
char **)realloc(res,
sizeof(
char *) * (len + n + 1))) == NULL) {
446 MEMCPY(res + len, args,
char *, n + 1);
454#if __has_warning("-Wdeprecated-declarations") || RBIMPL_COMPILER_IS(GCC)
455COMPILER_WARNING_IGNORED(-Wdeprecated-declarations)
460start_process(
const char *abspath,
char *
const *argv)
466 verbose(1,
"MJIT: Failed to open a null device: %s", strerror(errno));
469 if (mjit_opts.verbose >= 2) {
471 fprintf(stderr,
"Starting process: %s", abspath);
472 for (
int i = 0; (arg = argv[i]) != NULL; i++)
473 fprintf(stderr,
" %s", arg);
474 fprintf(stderr,
"\n");
478 if ((pid = vfork()) == 0) {
480 if (mjit_opts.verbose == 0) {
484 dup2(dev_null, STDERR_FILENO);
485 dup2(dev_null, STDOUT_FILENO);
487 (void)close(dev_null);
488 pid = execv(abspath, argv);
492 verbose(1,
"MJIT: Error in execv: %s", abspath);
495 (void)close(dev_null);
504exec_process(
const char *path,
char *
const argv[])
506 int stat, exit_code = -2;
507 pid_t pid = start_process(path, argv);
509 pid_t r = waitpid(pid, &
stat, 0);
511 if (errno == EINTR)
continue;
512 fprintf(stderr,
"[%"PRI_PIDT_PREFIX"d] waitpid(%lu): %s (SIGCHLD=%d,%u)\n",
513 getpid(), (
unsigned long)pid, strerror(errno),
514 RUBY_SIGCHLD, SIGCHLD_LOSSY);
518 if (WIFEXITED(
stat)) {
519 exit_code = WEXITSTATUS(
stat);
522 else if (WIFSIGNALED(
stat)) {
532remove_so_file(
const char *so_file,
struct rb_mjit_unit *unit)
534 remove_file(so_file);
539sprint_funcname(
char *funcname,
size_t funcname_size,
const struct rb_mjit_unit *unit)
542 if (iseq == NULL || (!mjit_opts.debug && !mjit_opts.debug_flags)) {
543 snprintf(funcname, funcname_size,
"_mjit%d", unit->id);
548 const char *path = RSTRING_PTR(rb_iseq_path(iseq));
549 const char *lib =
"/lib/";
551 while (strstr(path, lib))
552 path = strstr(path, lib) + strlen(lib);
553 while (strstr(path, version))
554 path = strstr(path, version) + strlen(version);
557 const char *method = RSTRING_PTR(ISEQ_BODY(iseq)->location.label);
558 if (!strcmp(method,
"[]")) method =
"AREF";
559 if (!strcmp(method,
"[]=")) method =
"ASET";
562 snprintf(funcname, funcname_size,
"_mjit%d_%s_%s", unit->id, path, method);
563 for (
size_t i = 0; i < strlen(funcname); i++) {
564 char c = funcname[i];
565 if (!((
'a' <= c && c <=
'z') || (
'A' <= c && c <=
'Z') || (
'0' <= c && c <=
'9') || c ==
'_')) {
571static const int c_file_access_mode =
575 O_WRONLY|O_EXCL|O_CREAT;
577#define append_str2(p, str, len) ((char *)memcpy((p), str, (len))+(len))
578#define append_str(p, str) append_str2(p, str, sizeof(str)-1)
579#define append_lit(p, str) append_str2(p, str, rb_strlen_lit(str))
585 const char *rest_args[] = {
593 "-o", pch_file, header_file,
597 verbose(2,
"Creating precompiled header");
598 char **args = form_args(4, cc_common_args, CC_CODEFLAG_ARGS, cc_added_args, rest_args);
600 mjit_warning(
"making precompiled header failed on forming args");
601 pch_status = PCH_FAILED;
605 int exit_code = exec_process(cc_path, args);
608 if (exit_code == 0) {
609 pch_status = PCH_SUCCESS;
612 mjit_warning(
"Making precompiled header failed on compilation. Stopping MJIT worker...");
613 pch_status = PCH_FAILED;
618c_compile(
const char *c_file,
const char *so_file)
620 const char *so_args[] = {
623 "-include-pch", pch_file,
628# if defined(__MACH__)
629 extern VALUE rb_libruby_selfpath;
630 const char *loader_args[] = {
"-bundle_loader",
StringValuePtr(rb_libruby_selfpath), NULL};
632 const char *loader_args[] = {NULL};
635 char **args = form_args(8, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS, cc_added_args,
636 so_args, loader_args, CC_LIBS, CC_DLDFLAGS_ARGS, CC_LINKER_ARGS);
637 if (args == NULL)
return 1;
639 int exit_code = exec_process(cc_path, args);
640 if (!mjit_opts.save_temps)
650 static const char c_ext[] =
".c";
651 static const char so_ext[] = DLEXT;
652 char c_file[MAXPATHLEN], so_file[MAXPATHLEN];
654 sprint_uniq_filename(c_file, (
int)
sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
655 sprint_uniq_filename(so_file, (
int)
sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
657 return c_compile(c_file, so_file);
660static void compile_prelude(
FILE *f);
665 VM_ASSERT(unit->type == MJIT_UNIT_BATCH);
666 static const char c_ext[] =
".c";
667 static const char so_ext[] = DLEXT;
668 char c_file[MAXPATHLEN], so_file[MAXPATHLEN];
670 sprint_uniq_filename(c_file, (
int)
sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
671 sprint_uniq_filename(so_file, (
int)
sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
675 if (fd < 0 || (f = fdopen(fd,
"w")) == NULL) {
677 if (fd >= 0) (void)close(fd);
678 verbose(1,
"Failed to fopen '%s', giving up JIT for it (%s)", c_file, strerror(e));
686 ccan_list_for_each(&unit->units.head, child_unit, unode) {
687 if (!success)
continue;
688 if (child_unit->iseq == NULL)
continue;
690 char funcname[MAXPATHLEN];
691 sprint_funcname(funcname,
sizeof(funcname), child_unit);
693 int iseq_lineno = ISEQ_BODY(child_unit->iseq)->location.first_lineno;
694 const char *sep =
"@";
695 const char *iseq_label = RSTRING_PTR(ISEQ_BODY(child_unit->iseq)->location.label);
696 const char *iseq_path = RSTRING_PTR(rb_iseq_path(child_unit->iseq));
697 if (!iseq_label) iseq_label = sep =
"";
698 fprintf(f,
"\n/* %s%s%s:%d */\n", iseq_label, sep, iseq_path, iseq_lineno);
699 success &= mjit_compile(f, child_unit->iseq, funcname, child_unit->id);
711 VM_ASSERT(unit->type == MJIT_UNIT_COMPACT);
712 static const char c_ext[] =
".c";
713 static const char so_ext[] = DLEXT;
714 char c_file[MAXPATHLEN], so_file[MAXPATHLEN];
716 sprint_uniq_filename(c_file, (
int)
sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
717 sprint_uniq_filename(so_file, (
int)
sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
721 if (fd < 0 || (f = fdopen(fd,
"w")) == NULL) {
723 if (fd >= 0) (void)close(fd);
724 verbose(1,
"Failed to fopen '%s', giving up JIT for it (%s)", c_file, strerror(e));
732 ccan_list_for_each(&active_units.head, batch_unit, unode) {
733 ccan_list_for_each(&batch_unit->units.head, child_unit, unode) {
734 if (!success)
continue;
735 if (child_unit->iseq == NULL)
continue;
737 char funcname[MAXPATHLEN];
738 sprint_funcname(funcname,
sizeof(funcname), child_unit);
740 int iseq_lineno = ISEQ_BODY(child_unit->iseq)->location.first_lineno;
741 const char *sep =
"@";
742 const char *iseq_label = RSTRING_PTR(ISEQ_BODY(child_unit->iseq)->location.label);
743 const char *iseq_path = RSTRING_PTR(rb_iseq_path(child_unit->iseq));
744 if (!iseq_label) iseq_label = sep =
"";
745 fprintf(f,
"\n/* %s%s%s:%d */\n", iseq_label, sep, iseq_path, iseq_lineno);
746 success &= mjit_compile(f, child_unit->iseq, funcname, child_unit->id);
755load_batch_funcs_from_so(
struct rb_mjit_unit *unit,
char *c_file,
char *so_file)
757 double end_time = real_ms_time();
759 void *handle = dlopen(so_file, RTLD_NOW);
760 if (handle == NULL) {
761 mjit_warning(
"failure in loading code from batched '%s': %s", so_file, dlerror());
765 unit->handle = handle;
768 add_to_list(unit, &active_units);
769 active_units_length += unit->units.length;
771 if (!mjit_opts.save_temps)
772 remove_so_file(so_file, unit);
775 ccan_list_for_each(&unit->units.head, child_unit, unode) {
776 char funcname[MAXPATHLEN];
777 sprint_funcname(funcname,
sizeof(funcname), child_unit);
780 if ((func = dlsym(handle, funcname)) == NULL) {
781 mjit_warning(
"skipping to load '%s' from '%s': %s", funcname, so_file, dlerror());
785 if (child_unit->iseq) {
787 const rb_iseq_t *iseq = child_unit->iseq;
788 MJIT_ATOMIC_SET(ISEQ_BODY(iseq)->jit_func, (jit_func_t)func);
790 verbose(1,
"JIT success: %s@%s:%d",
791 RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
792 RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno);
795 verbose(1,
"JIT skip: A compiled method has been GCed");
798 verbose(1,
"JIT batch (%.1fms): Batched %d methods %s -> %s", end_time - current_cc_ms, unit->units.length, c_file, so_file);
802load_compact_funcs_from_so(
struct rb_mjit_unit *unit,
char *c_file,
char *so_file)
804 double end_time = real_ms_time();
806 void *handle = dlopen(so_file, RTLD_NOW);
807 if (handle == NULL) {
808 mjit_warning(
"failure in loading code from compacted '%s': %s", so_file, dlerror());
812 unit->handle = handle;
815 add_to_list(unit, &compact_units);
817 if (!mjit_opts.save_temps)
818 remove_so_file(so_file, unit);
821 ccan_list_for_each(&active_units.head, batch_unit, unode) {
822 ccan_list_for_each(&batch_unit->units.head, child_unit, unode) {
823 if (child_unit->iseq == NULL)
continue;
825 char funcname[MAXPATHLEN];
826 sprint_funcname(funcname,
sizeof(funcname), child_unit);
829 if ((func = dlsym(handle, funcname)) == NULL) {
830 mjit_warning(
"skipping to reload '%s' from '%s': %s", funcname, so_file, dlerror());
834 if (child_unit->iseq) {
836 MJIT_ATOMIC_SET(ISEQ_BODY(child_unit->iseq)->jit_func, (jit_func_t)func);
840 verbose(1,
"JIT compaction (%.1fms): Compacted %d methods %s -> %s", end_time - current_cc_ms, active_units_length, c_file, so_file);
845header_name_end(
const char *s)
847 const char *e = s + strlen(s);
849 static const char suffix[] =
".gch";
852 if (e > s+
sizeof(suffix)-1 && strcmp(e-
sizeof(suffix)+1, suffix) == 0) {
853 e -=
sizeof(suffix)-1;
862compile_prelude(
FILE *f)
865 const char *s = pch_file;
866 const char *e = header_name_end(s);
868 fprintf(f,
"#include \"");
884 extern pid_t rb_mjit_fork();
885 pid_t pid = rb_mjit_fork();
887 int exit_code = c_compile_unit(unit);
905 VM_ASSERT(compiled_iseq != NULL);
906 VM_ASSERT(compiled_iseq->mjit_unit != NULL);
907 VM_ASSERT(captured_iseq != NULL);
910 unsigned int new_entries_size = unit->cc_entries_size + captured_iseq->ci_size;
911 VM_ASSERT(captured_iseq->ci_size > 0);
915 int cc_entries_index = unit->cc_entries_size;
916 if (unit->cc_entries_size == 0) {
917 VM_ASSERT(unit->cc_entries == NULL);
918 unit->cc_entries = cc_entries = malloc(
sizeof(
struct rb_callcache *) * new_entries_size);
919 if (cc_entries == NULL)
return -1;
922 void *cc_ptr = (
void *)unit->cc_entries;
923 cc_entries = realloc(cc_ptr,
sizeof(
struct rb_callcache *) * new_entries_size);
924 if (cc_entries == NULL)
return -1;
925 unit->cc_entries = cc_entries;
926 cc_entries += cc_entries_index;
928 unit->cc_entries_size = new_entries_size;
931 for (
unsigned int i = 0; i < captured_iseq->ci_size; i++) {
932 cc_entries[i] = captured_iseq->call_data[i].cc;
935 return cc_entries_index;
944get_uniq_filename(
unsigned long id,
const char *prefix,
const char *suffix)
946 char buff[70], *str = buff;
947 int size = sprint_uniq_filename(buff,
sizeof(buff),
id, prefix, suffix);
951 if (size <= (
int)
sizeof(buff)) {
952 memcpy(str, buff, size);
955 sprint_uniq_filename(str, size,
id, prefix, suffix);
962mjit_cancel_all(
const char *reason)
968 mjit_cancel_p =
true;
969 if (mjit_opts.warnings || mjit_opts.verbose) {
970 fprintf(stderr,
"JIT cancel: Disabled JIT-ed code because %s\n", reason);
976mjit_update_references(
const rb_iseq_t *iseq)
981 CRITICAL_SECTION_START(4,
"mjit_update_references");
982 if (ISEQ_BODY(iseq)->mjit_unit) {
983 ISEQ_BODY(iseq)->mjit_unit->iseq = (
rb_iseq_t *)rb_gc_location((
VALUE)ISEQ_BODY(iseq)->mjit_unit->iseq);
987 mjit_cancel_all(
"GC.compact is used");
994 ccan_list_for_each(&stale_units.head, unit, unode) {
995 if (unit->iseq == iseq) {
999 CRITICAL_SECTION_FINISH(4,
"mjit_update_references");
1010 if (ISEQ_BODY(iseq)->mjit_unit) {
1013 ISEQ_BODY(iseq)->mjit_unit->iseq = NULL;
1019 ccan_list_for_each(&stale_units.head, unit, unode) {
1020 if (unit->iseq == iseq) {
1034 ccan_list_for_each_safe(&list->head, unit, next, unode) {
1035 ccan_list_del(&unit->unode);
1036 if (!close_handle_p) unit->handle = NULL;
1038 if (list == &stale_units) {
1043 if (unit->handle && dlclose(unit->handle)) {
1044 mjit_warning(
"failed to close handle for u%d: %s", unit->id, dlerror());
1056create_unit(
enum rb_mjit_unit_type type)
1059 unit->id = current_unit_num++;
1061 if (type == MJIT_UNIT_BATCH) {
1062 ccan_list_head_init(&unit->units.head);
1070 struct rb_mjit_unit *unit = create_unit(MJIT_UNIT_ISEQ);
1072 ISEQ_BODY(iseq)->mjit_unit = unit;
1080check_unit_queue(
void)
1082 if (mjit_opts.custom)
return;
1083 if (worker_stopped)
return;
1084 if (current_cc_pid != 0)
return;
1087 if (active_units_length >= mjit_opts.max_cache_size)
return;
1090 if (!mjit_compile_p)
return;
1091 mjit_compile_p =
false;
1094 struct rb_mjit_unit *unit = create_unit(MJIT_UNIT_BATCH);
1096 VM_ASSERT(unit_queue.length > 0);
1097 while ((child_unit = get_from_list(&unit_queue)) != NULL && (active_units_length + unit->units.length) < mjit_opts.max_cache_size) {
1098 add_to_list(child_unit, &unit->units);
1099 ISEQ_BODY(child_unit->iseq)->jit_func = (jit_func_t)MJIT_FUNC_COMPILING;
1103 current_cc_ms = real_ms_time();
1104 current_cc_unit = unit;
1105 bool success = mjit_batch(unit);
1107 mjit_notify_waitpid(1);
1112 if (mjit_opts.wait) {
1113 int exit_code = c_compile_unit(unit);
1114 mjit_notify_waitpid(exit_code);
1117 current_cc_pid = start_c_compile_unit(unit);
1118 if (current_cc_pid == -1) {
1119 mjit_notify_waitpid(1);
1126check_compaction(
void)
1130 int max_compact_size = mjit_opts.max_cache_size / 100;
1131 if (max_compact_size < 10) max_compact_size = 10;
1133 if (active_units_length == mjit_opts.max_cache_size) {
1134 struct rb_mjit_unit *unit = create_unit(MJIT_UNIT_COMPACT);
1137 current_cc_ms = real_ms_time();
1138 current_cc_unit = unit;
1139 bool success = mjit_compact(unit);
1141 mjit_notify_waitpid(1);
1146 if (mjit_opts.wait) {
1147 int exit_code = c_compile_unit(unit);
1148 mjit_notify_waitpid(exit_code);
1151 current_cc_pid = start_c_compile_unit(unit);
1152 if (current_cc_pid == -1) {
1153 mjit_notify_waitpid(1);
1161mjit_notify_waitpid(
int exit_code)
1163 VM_ASSERT(mjit_opts.wait || current_cc_pid != 0);
1167 char c_file[MAXPATHLEN];
1168 sprint_uniq_filename(c_file, (
int)
sizeof(c_file), current_cc_unit->id, MJIT_TMP_PREFIX,
".c");
1171 if (exit_code != 0) {
1172 verbose(2,
"Failed to generate so");
1175 free_unit(current_cc_unit);
1176 current_cc_unit = NULL;
1181 char so_file[MAXPATHLEN];
1182 sprint_uniq_filename(so_file, (
int)
sizeof(so_file), current_cc_unit->id, MJIT_TMP_PREFIX, DLEXT);
1183 switch (current_cc_unit->type) {
1184 case MJIT_UNIT_ISEQ:
1185 rb_bug(
"unreachable: current_cc_unit->type must not be MJIT_UNIT_ISEQ");
1186 case MJIT_UNIT_BATCH:
1187 load_batch_funcs_from_so(current_cc_unit, c_file, so_file);
1188 current_cc_unit = NULL;
1191 if (!stop_worker_p) {
1195 case MJIT_UNIT_COMPACT:
1196 load_compact_funcs_from_so(current_cc_unit, c_file, so_file);
1197 current_cc_unit = NULL;
1202 if (!stop_worker_p) {
1210mjit_target_iseq_p(
const rb_iseq_t *iseq)
1213 return (body->type == ISEQ_TYPE_METHOD || body->type == ISEQ_TYPE_BLOCK)
1214 && !body->builtin_inline_p
1215 && strcmp(
"<internal:mjit>", RSTRING_PTR(rb_iseq_path(iseq))) != 0;
1219static VALUE rb_mMJIT = 0;
1221static VALUE rb_mMJITC = 0;
1223static VALUE rb_cMJITCompiler = 0;
1225static VALUE rb_cMJITIseqPtr = 0;
1227static VALUE rb_cMJITICPtr = 0;
1229static VALUE rb_mMJITHooks = 0;
1231#define WITH_MJIT_DISABLED(stmt) do { \
1232 bool original_call_p = mjit_call_p; \
1233 mjit_call_p = false; \
1235 mjit_call_p = original_call_p; \
1236 if (mjit_cancel_p) mjit_call_p = false; \
1240MJIT_FUNC_EXPORTED
void
1241rb_mjit_bop_redefined(
int redefined_flag,
enum ruby_basic_operators bop)
1243 if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks)
return;
1244 WITH_MJIT_DISABLED({
1245 rb_funcall(rb_mMJITHooks, rb_intern(
"on_bop_redefined"), 2,
INT2NUM(redefined_flag),
INT2NUM((
int)bop));
1250MJIT_FUNC_EXPORTED
void
1253 if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks)
return;
1254 WITH_MJIT_DISABLED({
1255 VALUE cme_klass = rb_funcall(rb_mMJITC, rb_intern(
"rb_callable_method_entry_struct"), 0);
1256 VALUE cme_ptr = rb_funcall(cme_klass, rb_intern(
"new"), 1,
SIZET2NUM((
size_t)cme));
1257 rb_funcall(rb_mMJITHooks, rb_intern(
"on_cme_invalidate"), 1, cme_ptr);
1263rb_mjit_before_ractor_spawn(
void)
1265 if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks)
return;
1266 WITH_MJIT_DISABLED({
1267 rb_funcall(rb_mMJITHooks, rb_intern(
"on_ractor_spawn"), 0);
1272mjit_constant_state_changed(
void *data)
1274 if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks)
return;
1276 WITH_MJIT_DISABLED({
1277 rb_funcall(rb_mMJITHooks, rb_intern(
"on_constant_state_changed"), 1,
ID2SYM(
id));
1282MJIT_FUNC_EXPORTED
void
1283rb_mjit_constant_state_changed(
ID id)
1285 if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks)
return;
1288 rb_workqueue_register(0, mjit_constant_state_changed, (
void *)
id);
1292MJIT_FUNC_EXPORTED
void
1293rb_mjit_constant_ic_update(
const rb_iseq_t *
const iseq,
IC ic,
unsigned insn_idx)
1295 if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks)
return;
1296 WITH_MJIT_DISABLED({
1297 VALUE iseq_ptr = rb_funcall(rb_cMJITIseqPtr, rb_intern(
"new"), 1,
SIZET2NUM((
size_t)iseq));
1298 VALUE ic_ptr = rb_funcall(rb_cMJITICPtr, rb_intern(
"new"), 1,
SIZET2NUM((
size_t)ic));
1299 rb_funcall(rb_mMJITHooks, rb_intern(
"on_constant_ic_update"), 3, iseq_ptr, ic_ptr,
UINT2NUM(insn_idx));
1304MJIT_FUNC_EXPORTED
void
1307 if (!mjit_enabled || !mjit_call_p || !rb_mMJITHooks)
return;
1308 WITH_MJIT_DISABLED({
1309 rb_funcall(rb_mMJITHooks, rb_intern(
"on_tracing_invalidate_all"), 1,
UINT2NUM(new_iseq_events));
1315mjit_hook_custom_compile(
const rb_iseq_t *iseq)
1317 WITH_MJIT_DISABLED({
1318 VALUE iseq_class = rb_funcall(rb_mMJITC, rb_intern(
"rb_iseq_t"), 0);
1319 VALUE iseq_ptr = rb_funcall(iseq_class, rb_intern(
"new"), 1,
ULONG2NUM((
size_t)iseq));
1320 VALUE jit_func = rb_funcall(rb_mMJIT, rb_intern(
"compile"), 1, iseq_ptr);
1321 ISEQ_BODY(iseq)->jit_func = (jit_func_t)
NUM2ULONG(jit_func);
1328 if (!mjit_enabled)
return;
1329 if (mjit_opts.custom) {
1330 mjit_hook_custom_compile(iseq);
1333 if (pch_status != PCH_SUCCESS || !rb_ractor_main_p())
1335 if (!mjit_target_iseq_p(iseq)) {
1336 ISEQ_BODY(iseq)->jit_func = (jit_func_t)MJIT_FUNC_FAILED;
1342 bool recompile_p = !MJIT_FUNC_STATE_P(ISEQ_BODY(iseq)->jit_func);
1343 if (!ISEQ_BODY(iseq)->mjit_unit || recompile_p) {
1346 ISEQ_BODY(iseq)->mjit_unit->iseq = NULL;
1347 ISEQ_BODY(iseq)->jit_func = (jit_func_t)MJIT_FUNC_NOT_COMPILED;
1348 active_units_length--;
1354 VM_ASSERT(compile_info != NULL);
1355 unit->compile_info = *compile_info;
1357 add_to_list(unit, &unit_queue);
1358 ISEQ_BODY(iseq)->total_calls = 0;
1361 VM_ASSERT(compile_info == NULL);
1362 mjit_compile_p =
true;
1369rb_mjit_add_iseq_to_process(
const rb_iseq_t *iseq)
1371 mjit_add_iseq_to_process(iseq, NULL);
1376#define MJIT_WAIT_TIMEOUT_SECONDS 5
1381 pid_t initial_pid = current_cc_pid;
1382 if (initial_pid == 0) {
1383 mjit_warning(
"initial_pid was 0 on mjit_wait");
1386 if (pch_status == PCH_FAILED)
return;
1389 struct timeval tv = { .tv_sec = 0, .tv_usec = 1000 };
1390 while (current_cc_pid == initial_pid) {
1392 if (tries / 1000 > MJIT_WAIT_TIMEOUT_SECONDS) {
1393 if (unit->type == MJIT_UNIT_ISEQ) {
1394 unit->iseq->body->jit_func = (jit_func_t)MJIT_FUNC_FAILED;
1396 mjit_warning(
"timed out to wait for JIT finish");
1407 VM_ASSERT(body->mjit_unit != NULL);
1408 return &body->mjit_unit->compile_info;
1414 if (MJIT_FUNC_STATE_P(ISEQ_BODY(iseq)->jit_func))
1417 verbose(1,
"JIT recompile: %s@%s:%d", RSTRING_PTR(ISEQ_BODY(iseq)->location.label),
1418 RSTRING_PTR(rb_iseq_path(iseq)), ISEQ_BODY(iseq)->location.first_lineno);
1419 VM_ASSERT(ISEQ_BODY(iseq)->mjit_unit != NULL);
1421 mjit_add_iseq_to_process(iseq, &ISEQ_BODY(iseq)->mjit_unit->compile_info);
1427rb_mjit_recompile_send(
const rb_iseq_t *iseq)
1429 rb_mjit_iseq_compile_info(ISEQ_BODY(iseq))->disable_send_cache =
true;
1430 mjit_recompile(iseq);
1435rb_mjit_recompile_ivar(
const rb_iseq_t *iseq)
1437 rb_mjit_iseq_compile_info(ISEQ_BODY(iseq))->disable_ivar_cache =
true;
1438 mjit_recompile(iseq);
1443rb_mjit_recompile_exivar(
const rb_iseq_t *iseq)
1445 rb_mjit_iseq_compile_info(ISEQ_BODY(iseq))->disable_exivar_cache =
true;
1446 mjit_recompile(iseq);
1451rb_mjit_recompile_inlining(
const rb_iseq_t *iseq)
1453 rb_mjit_iseq_compile_info(ISEQ_BODY(iseq))->disable_inlining =
true;
1454 mjit_recompile(iseq);
1459rb_mjit_recompile_const(
const rb_iseq_t *iseq)
1461 rb_mjit_iseq_compile_info(ISEQ_BODY(iseq))->disable_const_cache =
true;
1462 mjit_recompile(iseq);
1465extern VALUE ruby_archlibdir_path, ruby_prefix_path;
1469init_header_filename(
void)
1476 const char *basedir =
"";
1481 basedir_val = ruby_prefix_path;
1483 baselen = RSTRING_LEN(basedir_val);
1485 if (getenv(
"MJIT_SEARCH_BUILD_DIR")) {
1491 const char *hdr = dlsym(RTLD_DEFAULT,
"MJIT_HEADER");
1493 verbose(1,
"No MJIT_HEADER");
1495 else if (hdr[0] !=
'/') {
1496 verbose(1,
"Non-absolute header file path: %s", hdr);
1498 else if (
stat(hdr, &st) || !S_ISREG(st.st_mode)) {
1499 verbose(1,
"Non-file header file path: %s", hdr);
1501 else if ((st.st_uid != getuid()) || (st.st_mode & 022) ||
1502 !rb_path_check(hdr)) {
1503 verbose(1,
"Unsafe header file: uid=%ld mode=%#o %s",
1504 (
long)st.st_uid, (
unsigned)st.st_mode, hdr);
1510 verbose(3,
"PRELOADENV("PRELOADENV
")=%s", getenv(PRELOADENV));
1512 unsetenv(PRELOADENV);
1513 verbose(3,
"MJIT_HEADER: %s", hdr);
1515 if (!header_file)
return false;
1522 static const char header_name[] = MJIT_HEADER_INSTALL_DIR
"/" MJIT_MIN_HEADER_NAME;
1523 const size_t header_name_len =
sizeof(header_name) - 1;
1525 header_file =
xmalloc(baselen + header_name_len + 1);
1526 p = append_str2(header_file, basedir, baselen);
1527 p = append_str2(p, header_name, header_name_len + 1);
1530 verbose(1,
"Cannot access header file: %s", header_file);
1538 pch_file = get_uniq_filename(0, MJIT_TMP_PREFIX
"h",
".h.gch");
1544system_default_tmpdir(
void)
1547#if defined _CS_DARWIN_USER_TEMP_DIR
1548 char path[MAXPATHLEN];
1549 size_t len = confstr(_CS_DARWIN_USER_TEMP_DIR, path,
sizeof(path));
1552 if (len >
sizeof(path)) {
1553 confstr(_CS_DARWIN_USER_TEMP_DIR, tmpdir, len);
1556 memcpy(tmpdir, path, len);
1565check_tmpdir(
const char *dir)
1569 if (!dir)
return FALSE;
1570 if (
stat(dir, &st))
return FALSE;
1572# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1574 if (!S_ISDIR(st.st_mode))
return FALSE;
1578 if (st.st_mode & S_IWOTH) {
1580 if (!(st.st_mode & S_ISVTX))
return FALSE;
1585 if (access(dir, W_OK))
return FALSE;
1593# define RETURN_ENV(name) \
1594 if (check_tmpdir(tmpdir = getenv(name))) return ruby_strdup(tmpdir)
1595 RETURN_ENV(
"TMPDIR");
1597 tmpdir = system_default_tmpdir();
1598 if (check_tmpdir(tmpdir))
return tmpdir;
1604#define MIN_CACHE_SIZE 10
1606#define DEFAULT_MAX_CACHE_SIZE 100
1608#define DEFAULT_CALL_THRESHOLD 10000
1614 stop_worker_p =
false;
1615 worker_stopped =
false;
1621ruby_strndup(
const char *str,
size_t n)
1624 memcpy(ret, str, n);
1632split_flags(
const char *flags)
1634 char *buf[MAXPATHLEN];
1637 for (; flags != NULL; flags = next) {
1638 next = strchr(flags,
' ');
1640 if (strlen(flags) > 0)
1641 buf[i++] =
strdup(flags);
1645 buf[i++] = ruby_strndup(flags, next - flags);
1650 char **ret =
xmalloc(
sizeof(
char *) * (i + 1));
1651 memcpy(ret, buf,
sizeof(
char *) * i);
1656#define opt_match_noarg(s, l, name) \
1657 opt_match(s, l, name) && (*(s) ? (rb_warn("argument to --mjit-" name " is ignored"), 1) : 1)
1658#define opt_match_arg(s, l, name) \
1659 opt_match(s, l, name) && (*(s) ? 1 : (rb_raise(rb_eRuntimeError, "--mjit-" name " needs an argument"), 0))
1662mjit_setup_options(
const char *s,
struct mjit_options *mjit_opt)
1664 const size_t l = strlen(s);
1668 else if (opt_match_noarg(s, l,
"warnings")) {
1669 mjit_opt->warnings =
true;
1671 else if (opt_match(s, l,
"debug")) {
1673 mjit_opt->debug_flags =
strdup(s + 1);
1675 mjit_opt->debug =
true;
1677 else if (opt_match_noarg(s, l,
"wait")) {
1678 mjit_opt->wait =
true;
1680 else if (opt_match_noarg(s, l,
"save-temps")) {
1681 mjit_opt->save_temps =
true;
1683 else if (opt_match(s, l,
"verbose")) {
1684 mjit_opt->verbose = *s ? atoi(s + 1) : 1;
1686 else if (opt_match_arg(s, l,
"max-cache")) {
1687 mjit_opt->max_cache_size = atoi(s + 1);
1689 else if (opt_match_arg(s, l,
"call-threshold")) {
1690 mjit_opt->call_threshold = atoi(s + 1);
1693 else if (opt_match_noarg(s, l,
"pause")) {
1694 mjit_opt->pause =
true;
1698 "invalid MJIT option `%s' (--help will show valid MJIT options)", s);
1702#define M(shortopt, longopt, desc) RUBY_OPT_MESSAGE(shortopt, longopt, desc)
1704 M(
"--mjit-warnings",
"",
"Enable printing JIT warnings"),
1705 M(
"--mjit-debug",
"",
"Enable JIT debugging (very slow), or add cflags if specified"),
1706 M(
"--mjit-wait",
"",
"Wait until JIT compilation finishes every time (for testing)"),
1707 M(
"--mjit-save-temps",
"",
"Save JIT temporary files in $TMP or /tmp (for testing)"),
1708 M(
"--mjit-verbose=num",
"",
"Print JIT logs of level num or less to stderr (default: 0)"),
1709 M(
"--mjit-max-cache=num",
"",
"Max number of methods to be JIT-ed in a cache (default: "
1710 STRINGIZE(DEFAULT_MAX_CACHE_SIZE)
")"),
1711 M(
"--mjit-call-threshold=num",
"",
"Number of calls to trigger JIT (for testing, default: "
1712 STRINGIZE(DEFAULT_CALL_THRESHOLD)
")"),
1723 VM_ASSERT(mjit_enabled);
1727 rb_mMJIT =
rb_const_get(rb_cRubyVM, rb_intern(
"MJIT"));
1729 verbose(1,
"Disabling MJIT because RubyVM::MJIT::Compiler is not defined");
1730 mjit_enabled =
false;
1734 rb_cMJITCompiler = rb_funcall(
rb_const_get(rb_mMJIT, rb_intern(
"Compiler")), rb_intern(
"new"), 0);
1735 rb_cMJITIseqPtr = rb_funcall(rb_mMJITC, rb_intern(
"rb_iseq_t"), 0);
1736 rb_cMJITICPtr = rb_funcall(rb_mMJITC, rb_intern(
"IC"), 0);
1737 rb_funcall(rb_cMJITICPtr, rb_intern(
"new"), 1,
SIZET2NUM(0));
1738 rb_mMJITHooks =
rb_const_get(rb_mMJIT, rb_intern(
"Hooks"));
1741 mjit_pid = getpid();
1744 if (mjit_opts.call_threshold == 0)
1745 mjit_opts.call_threshold = DEFAULT_CALL_THRESHOLD;
1746 if (mjit_opts.call_threshold % 2 == 1) {
1747 mjit_opts.call_threshold += 1;
1748 mjit_warning(
"--mjit-call-threshold must be an even number. Using %d instead.", mjit_opts.call_threshold);
1750 mjit_opts.call_threshold /= 2;
1751 if (mjit_opts.max_cache_size <= 0)
1752 mjit_opts.max_cache_size = DEFAULT_MAX_CACHE_SIZE;
1753 if (mjit_opts.max_cache_size < MIN_CACHE_SIZE)
1754 mjit_opts.max_cache_size = MIN_CACHE_SIZE;
1757 pch_status = PCH_NOT_READY;
1758 cc_path = CC_COMMON_ARGS[0];
1759 verbose(2,
"MJIT: CC defaults to %s", cc_path);
1760 cc_common_args =
xmalloc(
sizeof(CC_COMMON_ARGS));
1761 memcpy((
void *)cc_common_args, CC_COMMON_ARGS,
sizeof(CC_COMMON_ARGS));
1762 cc_added_args = split_flags(opts->debug_flags);
1763 xfree(opts->debug_flags);
1766 for (
size_t i = 0, j = 0; i <
sizeof(CC_COMMON_ARGS) /
sizeof(
char *); i++) {
1767 if (CC_COMMON_ARGS[i] && strncmp(
"-save-temps", CC_COMMON_ARGS[i], strlen(
"-save-temps")) == 0)
1769 cc_common_args[j] = CC_COMMON_ARGS[i];
1774 tmp_dir = system_tmpdir();
1775 verbose(2,
"MJIT: tmp_dir is %s", tmp_dir);
1777 if (!init_header_filename()) {
1778 mjit_enabled =
false;
1779 verbose(1,
"Failure in MJIT header file name initialization\n");
1782 pch_owner_pid = getpid();
1789 if (!mjit_opts.pause) {
1801 stop_worker_p =
true;
1802 if (current_cc_unit != NULL) {
1803 mjit_wait(current_cc_unit);
1805 worker_stopped =
true;
1810mjit_pause(
bool wait_p)
1812 if (!mjit_enabled) {
1815 if (worker_stopped) {
1821 while (current_cc_unit != NULL) {
1822 mjit_wait(current_cc_unit);
1834 if (!mjit_enabled) {
1837 if (!worker_stopped) {
1842 if (pch_status == PCH_NOT_READY) {
1845 mjit_opts.custom =
true;
1846 pch_status = PCH_SUCCESS;
1854 if (!start_worker()) {
1876mjit_child_after_fork(
void)
1892mjit_finish(
bool close_handle_p)
1898 verbose(2,
"Stopping worker thread");
1903 if (!mjit_opts.save_temps && getpid() == pch_owner_pid && pch_status == PCH_SUCCESS && !mjit_opts.custom)
1904 remove_file(pch_file);
1906 xfree(header_file); header_file = NULL;
1907 xfree((
void *)cc_common_args); cc_common_args = NULL;
1908 for (
char **flag = cc_added_args; *flag != NULL; flag++)
1910 xfree((
void *)cc_added_args); cc_added_args = NULL;
1911 xfree(tmp_dir); tmp_dir = NULL;
1912 xfree(pch_file); pch_file = NULL;
1914 mjit_call_p =
false;
1915 free_list(&unit_queue, close_handle_p);
1916 free_list(&active_units, close_handle_p);
1917 free_list(&compact_units, close_handle_p);
1918 free_list(&stale_units, close_handle_p);
1920 mjit_enabled =
false;
1921 verbose(1,
"Successful MJIT finish");
1933 RUBY_MARK_ENTER(
"mjit");
1936 rb_gc_mark(rb_cMJITCompiler);
1937 rb_gc_mark(rb_cMJITIseqPtr);
1938 rb_gc_mark(rb_cMJITICPtr);
1939 rb_gc_mark(rb_mMJITHooks);
1943 ccan_list_for_each(&active_units.head, unit, unode) {
1944 rb_gc_mark((
VALUE)unit->iseq);
1947 RUBY_MARK_LEAVE(
"mjit");
1955 if (body->mjit_unit && (cc_entries = body->mjit_unit->cc_entries) != NULL) {
1957 for (
unsigned int i = 0; i < body->mjit_unit->cc_entries_size; i++) {
1959 if (cc != NULL && vm_cc_markable(cc)) {
1961 rb_gc_mark((
VALUE)cc);
1962 rb_gc_mark((
VALUE)vm_cc_cme(cc));
1970mjit_compile(
FILE *f,
const rb_iseq_t *iseq,
const char *funcname,
int id)
1972 bool original_call_p = mjit_call_p;
1973 mjit_call_p =
false;
1975 VALUE iseq_ptr = rb_funcall(rb_cMJITIseqPtr, rb_intern(
"new"), 1,
ULONG2NUM((
size_t)iseq));
1976 VALUE src = rb_funcall(rb_cMJITCompiler, rb_intern(
"compile"), 3,
1979 fprintf(f,
"%s", RSTRING_PTR(src));
1982 mjit_call_p = original_call_p;
1986#include "mjit.rbinc"
void(* rb_postponed_job_func_t)(void *arg)
Type of postponed jobs.
uint32_t rb_event_flag_t
Represents event(s).
#define NUM2ULONG
Old name of RB_NUM2ULONG.
#define xfree
Old name of ruby_xfree.
#define ID2SYM
Old name of RB_ID2SYM.
#define ULONG2NUM
Old name of RB_ULONG2NUM.
#define SIZET2NUM
Old name of RB_SIZE2NUM.
#define xmalloc
Old name of ruby_xmalloc.
#define ZALLOC_N
Old name of RB_ZALLOC_N.
#define Qtrue
Old name of RUBY_Qtrue.
#define INT2NUM
Old name of RB_INT2NUM.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define UINT2NUM
Old name of RB_UINT2NUM.
void rb_raise(VALUE exc, const char *fmt,...)
Exception entry point.
void rb_bug(const char *fmt,...)
Interpreter panic switch.
VALUE rb_eRuntimeError
RuntimeError exception.
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Opens a file that closes on exec.
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
void rb_thread_wait_for(struct timeval time)
Identical to rb_thread_sleep(), except it takes struct timeval instead.
VALUE rb_const_get(VALUE space, ID name)
Identical to rb_const_defined(), except it returns the actual defined value.
int rb_const_defined(VALUE space, ID name)
Queries if the constant is defined at the namespace.
int rb_respond_to(VALUE obj, ID mid)
Queries if the object responds to the method.
char * ruby_strdup(const char *str)
This is our own version of strdup(3) that uses ruby_xmalloc() instead of system malloc (benefits our ...
#define strdup(s)
Just another name of ruby_strdup.
#define RUBY_API_VERSION_TEENY
Teeny version.
#define RUBY_API_VERSION_MAJOR
Major version.
#define RUBY_API_VERSION_MINOR
Minor version.
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
VALUE type(ANYARGS)
ANYARGS-ed function type.
#define PRI_PIDT_PREFIX
A rb_sprintf() format prefix to be used for a pid_t parameter.
#define StringValuePtr(v)
Identical to StringValue, except it returns a char*.
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock.
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_initialize.
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_unlock.
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_destroy.
uintptr_t VALUE
Type that represents a Ruby object.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.