Как избежать нарушения перекрестных потоков в расширении Ruby?


Я пишу расширение C, обеспечивающее интерфейс между Ruby и асинхронной библиотекой ввода-вывода. При выполнении тестов над моим кодом, я часто получаю ошибки, включая (но не ограничиваясь):

[BUG] cross-thread violation in rb_thread_schedule()

Асинхронный ввод-вывод означает, что мое расширение C должно будет доставлять сообщения в ruby из нескольких потоков (не основного потока интерпретатора). Как мне избежать этих нарушений безопасности потока в процессе?

1 5

1 ответ:

Для ruby 1.8.x единственный способ избежать ошибки очевиден - только вызвать Ruby / C API из основного потока интерпретатора. Я считаю, что это относится и к ruby 1.9.x также, но я не работал с ним и не знаю, как его собственная поддержка потоков может изменить ситуацию. Вместо того чтобы иметь несколько собственных потоков, непосредственно вызывающих API, вам нужно использовать шаблон производитель / потребитель для доставки запросов от ваших вторичных собственных потоков к вашему коду в главном потоке интерпретатора. И в идеале сделайте это, не блокируя излишне другие рубиново-зеленые нити. Если вы посмотрите на реализацию ruby, планировщик потоков ruby green по сути является циклом select(). Это предполагает следующую общую структуру:

  • создайте канал или другой механизм IPC, который предоставляет реальный файловый дескриптор select().
  • порождает собственные потоки и предоставляет им конец канала для записи.
  • в главном потоке интерпретатора введите цикл событий, который вызывает rb_thread_wait_fd() на читайте конец трубы. Это позволит рубиновый зеленый планировщик потоков для выполнения других зеленых нитей.
  • Когда ваши вторичные собственные потоки имеют запросы к основному потоку, они помещают их в очередь и также записывают в канал, пробуждая зеленый поток, выполняющий цикл событий.

Смотрите rb_io_sysread() (реализация IO#sysread) для того, что, вероятно, является самой простой чистой функцией IO-using в базе кода ruby.