Come evitare le violazioni cross-thread in un'estensione Ruby?
-
04-10-2019 - |
Domanda
Sto scrivendo un estensione C, che fornisce un'interfaccia tra Ruby e una libreria I / O asincrono. Durante l'esecuzione dei test oltre il mio codice, ho spesso si verificano errori compresi (ma non limitati a):
[BUG] cross-thread violation in rb_thread_schedule()
asincrona IO significa la mia estensione C avrà bisogno di consegnare i messaggi a rubino da più thread (non il filo interprete principale). Come evitare queste violazioni thread-sicurezza, nel processo?
Soluzione
Per rubino 1.8.x l'unico modo per evitare l'errore è la più ovvia - solo invocare l'API rubino / C dal thread principale interprete. Credo che questo si applica al rubino 1.9.x pure, ma non ho lavorato con lui e non so come il suo sostegno thread nativo potrebbe cambiare le cose. Invece di avere più thread nativi direttamente invocare l'API, è necessario utilizzare il modello produttore / consumatore per consegnare le richieste dei tuoi thread nativi secondari al codice nel thread interprete principale. E idealmente fare questo mentre non inutilmente bloccando altri fili verdi Ruby. Se guardate l'attuazione rubino, il rubino filo verde scheduler è essentialy un ciclo select()
. Questo suggerisce la seguente struttura generale:
- Crea un tubo o altro meccanismo IPC che fornisce un vero e proprio descrittore di file
select()
-grado. - Progenie fili nativi e fornire loro dell'estremità di scrittura del tubo.
- Nel thread principale interprete, immettere un ciclo evento che chiama
rb_thread_wait_fd()
sull'estremità lettura del tubo. In questo modo il rubino scheduler filo verde per eseguire altri thread verdi. - Quando i thread nativi secondari hanno richieste per il thread principale, li fila e anche scrivere al tubo, svegliarsi il filo verde che esegue il ciclo di eventi.
See rb_io_sysread()
(attuazione della IO#sysread
) per quello che è probabilmente il più semplice pulito IO-usando la funzione nella base di codice Ruby.