Run callbacks without GVL

This commit is contained in:
Kitaiti Makoto 2026-04-26 08:43:56 +09:00
parent 83237e2303
commit bd5507d7f6
2 changed files with 158 additions and 48 deletions

View File

@ -129,8 +129,18 @@ abort_ruby_whisper_callback_container_is_present(ruby_whisper_abort_callback_con
return !NIL_P(container->callback) || !NIL_P(container->callbacks);
}
static void new_segment_callback(struct whisper_context *ctx, struct whisper_state *state, int n_new, void *user_data) {
const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data;
typedef struct {
const ruby_whisper_callback_container *container;
struct whisper_state *state;
int n_new;
} call_new_segment_callbacks_args;
static void*
call_new_segment_callbacks(void *v_args) {
call_new_segment_callbacks_args *args = (call_new_segment_callbacks_args *)v_args;
const ruby_whisper_callback_container *container = args->container;
struct whisper_state *state = args->state;
int n_new = args->n_new;
// Currently, doesn't support state because
// those require to resolve GC-related problems.
@ -138,11 +148,11 @@ static void new_segment_callback(struct whisper_context *ctx, struct whisper_sta
rb_funcall(container->callback, id_call, 4, *container->context, Qnil, INT2NUM(n_new), container->user_data);
}
if (NIL_P(container->callbacks)) {
return;
return NULL;
}
const long callbacks_len = RARRAY_LEN(container->callbacks);
if (0 == callbacks_len) {
return;
return NULL;
}
const int n_segments = whisper_full_n_segments_from_state(state);
for (int i = n_new; i > 0; i--) {
@ -153,56 +163,165 @@ static void new_segment_callback(struct whisper_context *ctx, struct whisper_sta
rb_funcall(cb, id_call, 1, segment);
}
}
return NULL;
}
static void new_segment_callback(struct whisper_context *ctx, struct whisper_state *state, int n_new, void *user_data) {
const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data;
if (!ruby_whisper_callback_container_is_present(container)) {
return;
}
call_new_segment_callbacks_args args = {
container,
state,
n_new
};
rb_thread_call_with_gvl(call_new_segment_callbacks, (void *)&args);
}
typedef struct {
const ruby_whisper_callback_container *container;
struct whisper_state *state;
int progress_cur;
} call_progress_callbacks_args;
static void*
call_progress_callbacks(void *v_args) {
call_progress_callbacks_args *args = (call_progress_callbacks_args *)v_args;
const ruby_whisper_callback_container *container = args->container;
int progress_cur = args->progress_cur;
// Currently, doesn't support state because
// those require to resolve GC-related problems.
if (!NIL_P(args->container->callback)) {
rb_funcall(container->callback, id_call, 4, *container->context, Qnil, INT2NUM(progress_cur), container->user_data);
}
if (NIL_P(container->callbacks)) {
return NULL;
}
const long callbacks_len = RARRAY_LEN(container->callbacks);
if (0 == callbacks_len) {
return NULL;
}
for (int j = 0; j < callbacks_len; j++) {
VALUE cb = rb_ary_entry(container->callbacks, j);
rb_funcall(cb, id_call, 1, INT2NUM(progress_cur));
}
return NULL;
}
static void progress_callback(struct whisper_context *ctx, struct whisper_state *state, int progress_cur, void *user_data) {
const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data;
const VALUE progress = INT2NUM(progress_cur);
// Currently, doesn't support state because
// those require to resolve GC-related problems.
if (!NIL_P(container->callback)) {
rb_funcall(container->callback, id_call, 4, *container->context, Qnil, progress, container->user_data);
}
if (NIL_P(container->callbacks)) {
if (!ruby_whisper_callback_container_is_present(container)) {
return;
}
const long callbacks_len = RARRAY_LEN(container->callbacks);
if (0 == callbacks_len) {
return;
}
for (int j = 0; j < callbacks_len; j++) {
VALUE cb = rb_ary_entry(container->callbacks, j);
rb_funcall(cb, id_call, 1, progress);
}
call_progress_callbacks_args args = {
container,
state,
progress_cur
};
rb_thread_call_with_gvl(call_progress_callbacks, (void *)&args);
}
static bool encoder_begin_callback(struct whisper_context *ctx, struct whisper_state *state, void *user_data) {
const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data;
bool is_aborted = false;
VALUE result;
typedef struct {
const ruby_whisper_callback_container *container;
struct whisper_state *state;
bool is_continued;
} call_encoder_begin_callbacks_args;
static void*
call_encoder_begin_callbacks(void *v_args) {
call_encoder_begin_callbacks_args *args = (call_encoder_begin_callbacks_args *)v_args;
const ruby_whisper_callback_container *container = args->container;
VALUE result = Qnil;
// Currently, doesn't support state because
// those require to resolve GC-related problems.
if (!NIL_P(container->callback)) {
result = rb_funcall(container->callback, id_call, 3, *container->context, Qnil, container->user_data);
if (result == Qfalse) {
is_aborted = true;
args->is_continued = false;
return NULL;
}
}
if (!NIL_P(container->callbacks)) {
const long callbacks_len = RARRAY_LEN(container->callbacks);
if (0 == callbacks_len) {
return !is_aborted;
return NULL;
}
for (int j = 0; j < callbacks_len; j++) {
VALUE cb = rb_ary_entry(container->callbacks, j);
result = rb_funcall(cb, id_call, 0);
if (result == Qfalse) {
is_aborted = true;
args->is_continued = false;
return NULL;
}
}
}
return !is_aborted;
return NULL;
}
static bool encoder_begin_callback(struct whisper_context *ctx, struct whisper_state *state, void *user_data) {
const ruby_whisper_callback_container *container = (ruby_whisper_callback_container *)user_data;
if (!ruby_whisper_callback_container_is_present(container)) {
return false;
}
call_encoder_begin_callbacks_args args = {
container,
state,
true
};
rb_thread_call_with_gvl(call_encoder_begin_callbacks, (void *)&args);
return args.is_continued;
}
typedef struct {
const ruby_whisper_abort_callback_container *container;
struct whisper_state *state;
bool is_interrupted;
} call_abort_callbacks_args;
static void*
call_abort_callbacks(void *v_args) {
call_abort_callbacks_args *args = (call_abort_callbacks_args *)v_args;
const ruby_whisper_abort_callback_container *container = args->container;
if (container->is_interrupted) {
args->is_interrupted = true;
return NULL;
}
if (!NIL_P(container->callback)) {
VALUE result = rb_funcall(container->callback, id_call, 1, container->user_data);
if (!NIL_P(result) && Qfalse != result) {
args->is_interrupted = true;
return NULL;
}
}
if (NIL_P(container->callbacks)) {
return NULL;
}
const long callbacks_len = RARRAY_LEN(container->callbacks);
if (0 == callbacks_len) {
return NULL;
}
for (int j = 0; j < callbacks_len; j++) {
VALUE cb = rb_ary_entry(container->callbacks, j);
VALUE result = rb_funcall(cb, id_call, 1, container->user_data);
if (!NIL_P(result) && Qfalse != result) {
args->is_interrupted = true;
return NULL;
}
}
return NULL;
}
static bool abort_callback(void * user_data) {
@ -212,27 +331,14 @@ static bool abort_callback(void * user_data) {
return true;
}
if (!NIL_P(container->callback)) {
VALUE result = rb_funcall(container->callback, id_call, 1, container->user_data);
if (!NIL_P(result) && Qfalse != result) {
return true;
}
}
if (NIL_P(container->callbacks)) {
return false;
}
const long callbacks_len = RARRAY_LEN(container->callbacks);
if (0 == callbacks_len) {
return false;
}
for (int j = 0; j < callbacks_len; j++) {
VALUE cb = rb_ary_entry(container->callbacks, j);
VALUE result = rb_funcall(cb, id_call, 1, container->user_data);
if (!NIL_P(result) && Qfalse != result) {
return true;
}
}
return false;
call_abort_callbacks_args args = {
container,
NULL,
false
};
rb_thread_call_with_gvl(call_abort_callbacks, (void *)&args);
return args.is_interrupted;
}
static void

View File

@ -1,3 +1,7 @@
interface _MemoryView
def initialize: (string) -> void
end
module Whisper
interface _Samples
def length: () -> Integer