È sicuro chiamare un RCW da un finalizzatore?

Ho un object gestito che chiama un server COM per allocare memoria. L’object gestito deve chiamare nuovamente il server COM per liberare quella memoria prima che l’object gestito si allontani per evitare una perdita di memoria. Questo object implementa IDisposable per garantire che venga effettuata la chiamata COM di rilascio memoria corretta.

Nel caso in cui il metodo Dispose non venga chiamato, vorrei che il finalizzatore dell’object liberasse la memoria. Il guaio è che le regole di finalizzazione sono che non devi accedere a nessun riferimento perché non sai quali altri oggetti sono già stati sottoposti a GC e / o finalizzati prima di te. Questo lascia l’unico stato dell’object tangibile come campi (le maniglie sono le più comuni).

Ma chiamare un server COM implica passare attraverso un runtime callable wrapper (RCW) per liberare la memoria che ho un cookie da memorizzare in un campo. È sicuro che RCW è in grado di chiamare da un finalizzatore (è garantito che non sia stato eseguito da GC o finalizzato a questo punto)?

Per chi non ha familiarità con la finalizzazione, anche se il thread di finalizzazione viene eseguito sullo sfondo di un appdomain gestito mentre è in esecuzione, per quei casi che toccano i riferimenti sarebbe teoricamente OK, la finalizzazione si verifica anche all’arresto dell’appdomain e in qualsiasi ordine – non solo nell’ordine delle relazioni di riferimento. Questo limita ciò che si può ritenere sia sicuro da toccare dal finalizzatore. Qualsiasi riferimento a un object gestito potrebbe essere “cattivo” (memoria raccolta) anche se il riferimento non è nullo.

Aggiornamento : ho appena provato e ottenuto questo:

    Si è verificata un’eccezione non gestita di tipo “System.Runtime.InteropServices.InvalidComObjectException” in myassembly.dll

    Ulteriori informazioni: non è ansible utilizzare l’object COM che è stato separato dal relativo RCW sottostante.

    Ho scoperto personalmente dal team CLR che in realtà non è sicuro – a meno che non si assegni un GCHandle sull’RCW mentre è ancora sicuro farlo (quando si acquisisce per la prima volta RCW). Ciò garantisce che GC e finalizzatore non abbiano totalizzato RCW prima che l’object gestito che deve richiamarlo sia finalizzato.

     class MyManagedObject : IDisposable { private ISomeObject comServer; private GCHandle rcwHandle; private IServiceProvider serviceProvider; private uint cookie; public MyManagedObject(IServiceProvider serviceProvider) { this.serviceProvider = serviceProvider; this.comServer = this. serviceProvider.GetService(/*some service*/) as ISomeObject; this.rcwHandle = GCHandle.Alloc(this.comServer, GCHandleType.Normal); this.cookie = comServer.GetCookie(); } ~MyManagedObject() { this.Dispose(false); } public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { // dispose owned managed objects here. } if (this.rcwHandle.IsAllocated) { // calling this RCW is safe because we have a GC handle to it. this.comServer.ReleaseCookie(this.cookie); // Now release the GC handle on the RCW so it can be freed as well this.rcwHandle.Free(); } } } 

    Nel mio caso particolare , la mia app ospita il CLR stesso. Pertanto, chiama mscoree! CoEEShutdownCOM prima che il thread del finalizzatore venga eseguito, il che uccide RCW e genera l’errore InvalidComObjectException che stavo vedendo.

    Ma nei casi normali in cui il CLR non è in hosting, mi viene detto che dovrebbe funzionare.

    No, non è sicuro accedere a un RCW dal thread del finalizzatore. Una volta raggiunto il thread del finalizzatore, non si ha alcuna garanzia che RCW sia ancora vivo. È ansible che sia davanti al tuo object nella coda del finalizzatore e quindi rilasciato dal momento in cui il distruttore viene eseguito sul thread del finalizzatore.