2011-02-01 10 views
6

Da esempi che ho visto COM IUnknown::Release() implementazione della funzione è qualcosa di simile:Perché questa implementazione di COM IUnknown :: Release funziona?

ULONG Release() 
{ 
    InterlockedDecrement(&m_count); 
    if(m_count == 0) { 
     delete this; 
    } 
    return m_count; 
} 

Quindi, se m_count è 0, quindi stiamo cancellando "questo" oggetto, e restituendo il conteggio ref. Quello che non capisco è perché funziona?!?!

  1. eliminazione l'oggetto non avrebbe rovinato lo stack di chiamata o è bene perché è detenuta dal filo, quindi non ha nulla a che fare con l'oggetto ???

  2. Se l'oggetto è stato eliminato, come è possibile che possiamo restituire m_count, avrebbe dovuto essere eliminato. Avrei potuto convincermi che va bene se dopo l'eliminazione il codice restituisse lo 0 codificato, ma come mai può restituire il membro?!?!

Grazie mille per il vostro aiuto! :-)

+1

+1 Hai ragione - o si tratta di un codice bacato o ci sono alcune più sfumature al lavoro qui. Sono curioso di sapere se qualcuno con più esperienza con COM può rispondere a questo, ma il mio gutshot è che questo è completamente sbagliato. – templatetypedef

risposta

15

Questo codice è fasullo. Non si può mai fidarsi di m_count dopo il del decremento. Il codice corretto è sempre così:

ULONG Release() 
{ 
    ULONG count = InterlockedDecrement(&m_count); 
    if(count == 0){ delete this; } 
    return count; 
} 
+0

Intendevi "** dopo ** il * cancella *"? –

+2

No, intendeva dopo il decremento. Se più thread accedono a questo, è possibile che un altro thread chiami anche Release, rendendo negativo il valore della variabile membro se viene fatto tra il decremento e il condizionale. –

+6

No, intendo davvero dopo il 'Decremento'. Il problema del dereferenziamento 'questo' dopo 'elimina' è ovvio e non ha bisogno di ripetere. Ma c'è un problema più sottile di concorrenza. Il tuo decremento potrebbe portare il conteggio a, diciamo, 2. Nel momento in cui tocchi nuovamente m_count, dal momento che hai già rilasciato il tuo conteggio, * altri * thread potrebbero aver raggiunto 0 e rilasciato l'oggetto, e lo slot potrebbe essere stato addirittura riallocato qualcos'altro. Quindi non toccare m_count dopo il decremento * hic sunt leones *. –

1

ciò che si osserva è un comportamento indefinito. Lo stack di chiamate non viene modificato da delete this; e delete this by itself is always safe ma renders this pointer invalid, il che significa che non è più possibile dereferenziarlo.

Ci sono due possibili spiegazioni di ciò che osservate. O l'implementazione in questione non effettua il dereferenziamento del puntatore this per ottenere m_count al ritorno dalla funzione, lo ha caricato su un registro e utilizza solo quel valore e quindi this non è dereferenziato e non si verificano problemi o quando delete termina la memoria occupata dall'oggetto è ancora mappato nello spazio di indirizzamento del processo e rimane tecnicamente accessibile e quindi il dereferencing this ha esito positivo e m_count viene letto correttamente. Suppongo che quest'ultimo sia più probabile.

Qualunque sia la spiegazione di questo comportamento indefinito, non è possibile fare affidamento su quello, utilizzare ciò che user Remus Rusanu suggerisce in his answer.

Problemi correlati