2011-11-26 11 views
6

Ho bisogno di scrivere una classe che carichi le librerie condivise. La sequenza dlopen()/dlerror() ha bisogno di un lock per essere thread-safe.Scrittura di una classe C++ che può essere utilizzata come statica, ma richiede un blocco

class LibLoader { 
    public: 
    LibLoader(string whichLib); 
    bool Load() { Wait(lock); ... dlopen() ... dlerror() ... } 
    bool Unload() { Wait(lock); ... dlclose() ... dlerror() ... } 
    bool IsLoaded() {...} 
    // ... access to symbols... 
    private: 
    static Lock lock; 
} 
Lock Lock::lock; 

Gli utenti di questa classe (ci saranno molteplici allo stesso tempo) vorrà per renderlo un membro statico di questa classe per evitare il caricamento di una libreria condivisa di tempo multiplo per ogni oggetto della classe:

class NeedsALib { 
public: 
NeedsALib() { if (!myLib.IsLoaded()) { myLib.Load(); } } 
private: 
static LibLoader myLib; 
} 
LibLoader::myLib; 

Il problema con questo codice è che si potrebbe arrestare/si interromperà, poiché si basa sull'ordine di statica che viene distrutto al termine del programma. Se il blocco è scomparso prima che myLib si blocca ....

Come può essere scritto in modo sicuro e sicuro e non si basa sull'ordine di distruzione statica?

+0

... e la tua domanda è? –

+0

@Jeremy: non mi piace che si arresti. ;-) Come può essere fatto meglio? –

+0

Hai oggetti statici che devono caricare librerie condivise? –

risposta

3

Sfortunatamente, penso che l'unico modo per evitarlo sia utilizzare direttive di inizializzazione di una volta non portabili e evitare di distruggere il blocco. Ci sono due problemi di base necessarie ad affrontare:

  1. Cosa succede se due thread gara per accedere al blocco per la prima volta? [cioè, non è possibile creare il blocco pigro]
  2. Cosa succede se il blocco viene distrutto troppo presto? [cioè, non è possibile creare staticamente il blocco]

La combinazione di questi vincoli costringe a utilizzare un meccanismo non portatile per creare il blocco.

Sul pthreads, il modo più semplice per gestire questo è con l'PTHREAD_MUTEX_INITIALIZER, che consente di inizializzare staticamente serrature:

class LibLoader{ 
    static pthread_mutex_t mutex; 
// ... 
}; 

// never destroyed 
pthread_mutex_t LibLoader::mutex = PTHREAD_MUTEX_INITIALIZER; 

su Windows, è possibile utilizzare synchronous one-time initialization.

In alternativa, se si può Garantiamo che non ci sarà un solo thread prima di piste principali, è possibile utilizzare il pattern Singleton senza distruzione, e basta forzare la serratura di essere toccato prima del main():

class LibLoader { 
    class init_helper { 
    init_helper() { LibLoader::getLock(); } 
    }; 

    static init_helper _ih; 
    static Lock *_theLock; 

    static Lock *getLock() { 
    if (!_theLock) 
     _theLock = new Lock(); 
    return _theLock; 
    } 
    // ... 
}; 

static init_helper LibLoader::_ih; 
static Lock *LibLoader::_theLock; 

Si noti che questo rende l'ipotesi probabilmente non portabile (ma altamente probabile che sia vera) che gli oggetti statici di tipo POD non vengano distrutti finché tutti gli oggetti statici non POD sono stati distrutti. Non sono a conoscenza di alcuna piattaforma in cui questo non sia il caso.

+0

Nel primo esempio: Perché LibLoader :: mutex non può essere disattivato come qualsiasi altro membro statico? –

+1

@GeneVincent, pthread_mutex_t è un tipo POD e non viene distrutto fino a quando tutti i distruttori non hanno terminato l'esecuzione e il programma termina (questo non si applica necessariamente a qualcosa di diverso da Linux, sebbene sia molto probabile che si applichi su tutti i sistemi pthreads) dal – bdonlan

+2

IIRC, dal 'pthread_mutex_t' è un tipo POD (dato che è letteralmente una struttura C), e quindi non ha costruttore o distruttore, è inizializzato prima che avvenga l'inizializzazione statica. Non ho lo standard, o lo citerei, ma posso dire che ogni architettura che ho visto (ARM v9, ARM v7, ARM v7m, x86, x86_64, ppc, 68k, 68xx, MSP430 e AVR) , in questo modo, in particolare: 1) installazione del processo, 2) inizializzazione del POD (la sezione '.data' viene inizializzata dall'immagine del programma e la sezione' .bss' viene azzerata), 3) vengono chiamati gli inizializzatori statici e infine 4) viene chiamato 'main'. –

1

Avvolgimento dei requisiti: sono necessarie più istanze LibLoader, ciascuna per una libreria diversa, ma deve esistere un singolo blocco per garantire che non sovrascrivano i codici di errore reciproci.

Un modo sarebbe affidarsi all'inizializzazione statica e all'ordine di distruzione all'interno di un file.

Il modo migliore sarebbe non rendere il campo statico LibLoader in NeedsALib (e simili). Sembra che queste classi client possano essere passate un'istanza di destra LibLoader nel costruttore.

Se la creazione di istanze LibLoader al di fuori delle sue classi client non è conveniente, è possibile rendere tutti i puntatori di campi statici (il blocco e i caricatori) e utilizzare il modello singleton con inizializzazione pigra. Quindi, quando si crea il primo caricatore, si finisce per creare anche il blocco. Singleton stesso richiederebbe il blocco qui, ma potresti forse eseguirlo prima di generare i tuoi thread. La distruzione sarebbe anche esplicita e sotto il tuo controllo. Puoi farlo anche con i caricatori (mantenendo il blocco statico).

Inoltre, se LibLoader non dispone di molto stato da memorizzare, è possibile rendere ogni classe client (NeedsALib ecc.) Un'istanza propria LibLoader. Questo è certamente abbastanza dispendioso, però.

+1

Sfortunatamente il blocco deve essere statico poiché ci saranno più oggetti di LibLoader e quando un altro oggetto chiamerà dlopen() cancellerà l'errore da recuperare con dlerror(). –

+0

Quello che stai cercando di fare con il blocco statico sta applicando quella e una sola istanza di blocco esiste. Tuttavia, il tuo problema richiede che LibLoader sia un singleton e abbia una sola istanza. Invece di avere più caricatori che condividono un blocco, dovresti avere un singolo caricatore con un singolo blocco. –

+0

Ho bisogno di più oggetti di LibLoader, ciascuno per una diversa libreria condivisa, ma gli oggetti potrebbero non caricarsi allo stesso tempo per non sovrascrivere i codici di errore. –

Problemi correlati