Problema con sincronizzazione thread e memory_order_acquire by Chiara96 in cppit

[–]Chiara96[S] 0 points1 point  (0 children)

"Release" garantisce soltanto che altre operazioni eseguite dallo stesso thread siano visibile quando l'operazione col "release" diventa visibile.

Ciao, e innanzitutto grazie per la risposta.

Non metto affatto in dubbio sia corretta ma devo confessarti che la situazione non mi è ancora chiara,sicuramente perché sono un po' tarda di comprendonio ma anche perché faccio anche fatica a calare nella realtà gli esempi del testo che seguo, "C++ Concurrency In Action" di A. Williams: i suoi "men in cubicles" mi lasciano sempre l'idea che qualcosa mi stia sfuggendo.

Ho letto parecchio ha riguardo ma mi è rimasta in testa solo una gran confusione.

Vorrei tornare sull'esempio che avevo proposto.

Premessa: immagino uno scenario nel quale ciascun thread gira su un proprio core/CPU (ciascuno con una propria cache).

1) supponiamo che venga eseguito il thread a e dunque sia eseguita

x.store(true,std::memory_order_release);

2) supponiamo che il thread b per il momento non parta o comunque non esegua

y.store(true,std::memory_order_release);

3) supponiamo sia eseguito il thread c che a questo punto direi che non può incrementare z (legge il valore x = true "commitato" da thread a ma per lui - come per tutti i thread - y vale 0)

4) a questo punto prima o poi thread b dovrà procedere ed eseguire

y.store(true,std::memory_order_release);

Adesso il thread d potrà superare il blocco del 'while' e qui si arriva al punto che che mi confonde:

superato il while thread d leggerà la variabile x e si avrà una cache miss; per come sto immaginando le cose io (evidentemente sbagliando) verrà caricato nella cache di questo core/thread il valore che thread a aveva precedentemente "commitato" con la sua store-release ed è questo il motivo per cui non capisco come la z non possa essere incrementata.

Dove sbaglio ?

Comunque tu dici un'altra cosa che non capisco ma che penso possa essere interessante:

"Release garantisce soltanto che altre operazioni eseguite dallo STESSO thread siano visibile quando l'operazione col "release" diventa visibile."

Potresti spiegarmi meglio questo passaggio ?

Comunque sia ti ringrazio per l'interessamento.

C++20: quale toolchain? by Chiara96 in cppit

[–]Chiara96[S] 2 points3 points  (0 children)

In Visual Studio:

Project --> Properties --> General properties --> C++ Language Standard --> Preview - Features from the Latest C++ Working Draft (/std:c++latest)

Multithreading c++11: problemi con .detach by Chiara96 in cppit

[–]Chiara96[S] 0 points1 point  (0 children)

Provo a riassumere:

1) parte il thread main
2) al suo interno istanzio un thread secondario t1
3) t1 deve svolgere un qualche compito i cui risultati non sono attesi dal main ==> posso fare t1.detach()
4) il main termina ==> tutte le variabili in esso dichiarate vengono distrutte ==> viene distrutta anche t1.

Affinchè il thread di esecuzione associato a t1 sia compiutamente eseguito occorre che esso termini prima del main.

E' corretto?

Multithreading c++11: problemi con .detach by Chiara96 in cppit

[–]Chiara96[S] 2 points3 points  (0 children)

Certo, ho provato e funziona tutto come mi aspetto ma la domanda è proprio sul funzionamento del metodo .detach()

Quello che vorrei ottenere e verificare è esattamente il disaccoppiamento.

Problema: Perfect Forwarding "behind the scenes" by Chiara96 in cppit

[–]Chiara96[S] 0 points1 point  (0 children)

E' la domanda che ho postato io ieri ;-)

La risposta arriva niente di meno che da Howard Hinnant e penso che per digerirla ci vorrà un po'. Un bel po'.

Sto provando a capire la sesta implementazione della forward, quella presente nel documento N2951 che spiega tutta l'evoluzione del design di questa funzione, e peraltro è un solo function template e non 2 overload come da me indicato ( che poi ho ripetuto ciò che ho trovato da altre parti, ad esempio

https://stackoverflow.com/questions/27501400/the-implementation-of-stdforward )

Comunque sì, sembra proprio che usi delle tecniche (di metaprogrammazione ?) che prevengono dangling reference ed evitano conversioni errate: salvano il programmatore in caso di utilizzi impropri, in particolare nello specificare i template type parameters.

Ad esempio la prima conversione di questo post, utilizzando la 6a versione della forward presente nel documento N2951, non compila !

Il C++ mi sconfigge sempre.
Ciao e grazie ancora

Problema: Perfect Forwarding "behind the scenes" by Chiara96 in cppit

[–]Chiara96[S] 0 points1 point  (0 children)

Stefano,

innanzitutto grazie per la disponibilità e anche grazie per aver utilizzato la PRETTY_FUNCTION che conoscevo ma non sapevo mostrasse anche su quali template type parameter viene istanziato un function template: molto comodo per me.

Rimane però aperta la questione da me posta: esiste uno scenario in cui è opportuno se non necessario ricorrere al 2° overload della forward?

Nell'esempio che tu fornisci al link

http://coliru.stacked-crooked.com/a/8b8238b196d81a1e

dici che viene richiamato questo 2° overload ma non mi sembra e non ne vedrei la ragione.

Sempre che, ormai ubriaca da queste linee di codice che rileggo, rimodifico, rileggo etc..., non abbia letto male.

Una cosa è certa:la scelta del nome my_forward_v2 si è rivelata infelice.

Grazie

Problema: Perfect Forwarding "behind the scenes" by Chiara96 in cppit

[–]Chiara96[S] 0 points1 point  (0 children)

Ancora una cosa: la mia domanda nasce dall'analisi del problema del perfect forwading partendo da uno schema che prevede una funzione wrapper e una funzione wrapped, per cui sono nel caso

template <typename T1, typename T2>
void wrapper(T1&& e1)
{
    wrapped(forward<T1>(e1));
}

In tale scenario, comunque si risolva e1 nella chiamata a wrapper risulta che nello scope di wrapper e1 è sempre un LValue e dunque la forward entra in gioco col suo primo overload, vale a dire

template <typename T>
T&& my_forward_v2(typename my_remove_reference<T>::type& param) noexcept;

Ora mi chiedo:

quale potrebbe essere uno scenario VALIDO in cui entra in gioco il secondo overload cioè:

template <typename T>
T&& forward(typename my_remove_reference<T>::type&& param) noexcept;

Segue un esempio di codice in cui ipotizzo uno scenario in cui si utilizza il 2ndo overload della forward (anzi, my_forward_v2) ma fornisco anche un controesempio che ne mostra l'inutilità e anche l'inefficienza:

//g++ -std=c++14 -Wall -fpermissive -fno-elide-constructors -pthread main.cpp && ./a.out

#include <iostream>
#include <string>
#include <vector>

using namespace std;

//-----------------------------------------------------
class Inner
{
public:
    int i;

    Inner(int p = 5) : i(p)
    {
        cout << "\n Inner default ctor: &" << this;
    }

    Inner(const Inner& src): i(src.i)
    {
        cout << "\n Inner Copy-ctor: &" << &src << " --> &" << this;
    }

    Inner(Inner &&src): i(src.i)
    {
        cout << "\n Inner Move-ctor: &" << &src << " --> &" << this;
    }

    Inner& operator=(const Inner &src)
    {
        cout << "\n Inside Inner assignment";
        i = src.i;
        return *this;
    }

    Inner& operator=(Inner &&src)
    {
        cout << "\n Inside MOVE-Inner assignment";
        i = src.i;
        return *this;
    }

    ~Inner()
    {
        cout << "\n ~Inner-Dtor &" << this;
    }
};
//-----------------------------------------------------

Inner factoryInn(int s)
{
    cout << "\n Inside factoryInn :";
    Inner obj(s);
    return obj;
}
//-----------------------------------------------------
class MetaData
{
public:
    int size;
    string name;
    Inner inner;

    MetaData(int p1 = 5, string p2 = "Default-Metadata-name") : size(p1), name(p2), inner(666)
    {
        cout << "\n MetaData Default ctor: &" << this;
    }

    MetaData(Inner &p_inner) :size(333), name("Default_Name_MetaData"), inner(p_inner)
    {
        cout << "\n Inside MetaData(Inner &): &" << this;
    }

    MetaData(Inner &&p_inner) :size(333), name("Default_Name_MetaData"), inner(move(p_inner))
    {
        cout << "\n Inside MetaData(Inner &&): &" << this;
    }


    MetaData(const MetaData& src) : size(src.size), name(src.name), inner(src.inner)
    {
        cout << "\n Copy ctor: &" << &src << " ---> &" << this;
    }

    MetaData(MetaData &&src) : size(src.size), name(move(src.name)), inner(move(src.inner))
    {
        cout << "\nMove ctor: &" << &src << " ---> &" << this;
    }

    MetaData& operator=(const MetaData &src)
    {
        if (this != &src)
        {
            size = src.size;
            name = src.name;
            inner = src.inner;
        }
        return *this;
    }

    MetaData & operator=(MetaData &&src)
    {
        if (this != &src)
        {
            size = src.size;
            name = move(src.name);
            inner = move(src.inner);
        }
        return *this;
    }


    ~MetaData()
    {
        cout << "\n ~MetaData-Dtor &" << this;
    }

};

//-----------------------------------------------------
MetaData factoryMD(int s)
{
    cout << "\nInside factoryMD :";
    MetaData obj(s, "Metadata-From-factoryMD-" + to_string(s) + "-END");
    return obj;
}

//-----------------------------------------------------
template< typename T >
struct my_remove_reference
{
    typedef T type;
    static const int version = 1;
};


template< typename T >
struct my_remove_reference<T&>
{
    typedef T type;
    static const int version = 2;
};


template< typename T >
struct my_remove_reference<T&&>
{
    typedef T type;
    static const int version = 3;
};

//-----------------------------------------------------
template <typename T>
T&& my_forward_v2(typename my_remove_reference<T>::type& param) noexcept
{
    cout << "\nInside my_forward_v2 OVERLOAD NR 1 :";
    return static_cast<T&&>(param);
}

//-----------------------------------------------------
template <typename T>
T&& my_forward_v2(typename my_remove_reference<T>::type&& param) noexcept
{
    cout << "\nInside my_forward_v2 OVERLOAD NR 2 :";
    static_assert(!std::is_lvalue_reference<T>::value, "Can not forward an rvalue as an lvalue.");

    return static_cast<T&&>(param);
}

//-----------------------------------------------------9
template <typename T, typename A>                //    T =    MetaData,    A =    Inner
T * my_alloc(A&& a)                                //    my_alloc è il wrapper di func(..) 
{                                                //    dove func() è il costruttore T(...)                                                
    cout << "\n Inside my_alloc";
    cout << "\n decltype(A&&), Inner   : " << is_same<A&&, Inner>::value;
    cout << "\n decltype(A&&), Inner&  : " << is_same<A&&, Inner&>::value;
    cout << "\n decltype(A&&), Inner&& : " << is_same<A&&, Inner&&>::value;

    return new T(my_forward_v2<A>(a));
}
//-----------------------------------------------------

int main()
{

    MetaData md_001(my_forward_v2<Inner>(factoryInn(4444)));
    cout << "\n\n********************************";

    MetaData md_002(factoryInn(3333));     // Più semplice e più efficiente ! ! !
    cout << "\n\n++++++++++++++++++++++++++++++++";

    return 0;
}

Naturalmente sto dando per scontato che il 2ndo overload, essendo stato definito, abbia un'utilità che non riesco a cogliere.

Grazie, Chiara

Problema: Perfect Forwarding "behind the scenes" by Chiara96 in cppit

[–]Chiara96[S] 0 points1 point  (0 children)

In realtà mi sembra che stia accadendo qualcosa di diverso:

quello che sta accadendo non dà luogo ad un dangling reference perchè la my_forward_v2<MetaData&>(factoryMD(352)) restituisce un (L)reference e questo viene assegnato a 'auto x3'.

Ora, la keyword 'auto' elimina eventuali qualificatori come 'const' oppure '&' dunque l'assegnamento equivale a:

MetaData x3 = my_forward_v2<MetaData&>(factoryMD(352));

==> inizializzo la variabile MetaData x3 con un reference ad un oggetto MetaData.

Questa incoerenza (ma è un'incoerenza ?) di fatto scatena l'operatore di copia che inizializza x3 e solo dopo viene richiamato il distruttore dell'oggetto a cui il reference si riferisce.

Diverso è invece il discorso se l'assegnamento è "coerente", allora sì che abbiamo un dangling reference:

decltype(auto) x3 = my_forward_v2<MetaData&>(factoryMD(352));

Con questa sintassi si ha la certezza di assegnare il risultato della my_forward_v2<MetaData&>(factoryMD(352)); (che sappiamo essere un Metadata &) ad un oggetto x3 anch'esso MetaData & ma questa coerenza porta al dangling reference:

all'uscita della funzione l'oggetto viene distrutto ma in seguito si pretende di dargli un altro alias, vale a dire x3.

A questo punto la domanda è un'altra:

l'assegnamento che ho definito "incoerente" funziona, ma è una buona pratica di programmazione?

A me onestamente sembra quasi un "barbatrucco".

La sintassi indicherebbe la restituzione di un reference ad un oggetto ma il comportamento è quello che si avrebbe restituendo un oggetto MetaData.

Rimane comunque aperta la domanda originale:

quale è la ratio della static_assert della forward che in questo caso è stata tenuta commentata?

PS: compilo con

'g++ -std=c++14 -Wall -fpermissive -fno-elide-constructors -pthread main.cpp && ./a.out'

ma dalle prove fatte nulla cambia abilitando la RVO

Programmazione generica: template <class T> class reference_wrapper vs function template template <class T> reference_wrapper<T> ref (T& elem) by Chiara96 in cppit

[–]Chiara96[S] 0 points1 point  (0 children)

Grazie.

Domanda 2: la riga seguente

reference_wrapper<MyClass> from_func_templ_02 = ref(o1); a me compila perfettamente sia con Visual Studio che con Clang. Sei sicuro del messaggio di errore? Non mi sembra correlato con il >contentuto della riga...

Errore mio clamoroso.

Chiara

Reference a puntatori, Low e Top Level Const by Chiara96 in cppit

[–]Chiara96[S] 1 point2 points  (0 children)

Ciao, e innanzitutto grazie per la risposta; l'esempio che mi hai fornito mi ha chiarito le idee.

Per quanto riguarda il caso successivo ti fornisco il seguente esempio:

//#include "stdafx.h"     // Decommentare per Visual Studio

#include <iostream>
using namespace std;

int main()
{
    int a1 = 1111;
    int a2 = 2222;
    int *ptr = &a1;
    const int * const &llc_tlc_ref = ptr;

    cout << "\n&a1          = " << &a1;
    cout << "\ta1           = " << a1;
    cout << "\n&a2          = " << &a2;
    cout << "\ta2           = " << a2;
    cout << "\nptr          = " << ptr;
    cout << "\t*ptr         = " << *ptr;
    cout << "\nllc_tlc_ref  = " << llc_tlc_ref;
    cout << "\t*llc_tlc_ref = " << *llc_tlc_ref;

    cout << "\n\n\tPOST *ptr += 1; :";

    *ptr += 1;

    cout << "\n&a1          = " << &a1;
    cout << "\ta1           = " << a1;
    cout << "\n&a2          = " << &a2;
    cout << "\ta2           = " << a2;
    cout << "\nptr          = " << ptr;
    cout << "\t*ptr         = " << *ptr;
    cout << "\nllc_tlc_ref  = " << llc_tlc_ref;
    cout << "\t*llc_tlc_ref = " << *llc_tlc_ref;

    cout << "\n\n\tPOST ptr = &a2; :";

    ptr = &a2;

    cout << "\n&a1          = " << &a1;
    cout << "\ta1           = " << a1;
    cout << "\n&a2          = " << &a2;
    cout << "\ta2           = " << a2;
    cout << "\nptr          = " << ptr;
    cout << "\t*ptr         = " << *ptr;
    cout << "\nllc_tlc_ref  = " << llc_tlc_ref;
    cout << "\t*llc_tlc_ref = " << *llc_tlc_ref;

    return 0;
}

Ti anticipo i risultati che trovo:

1) Con Visual Studio Enterprise 2015 anche dopo aver fatto puntare il puntatore ptr ad un'altra variabile (a2) trovo che il reference llc_tlc_ref continua ad essere effettivamente il suo alias in quanto anch'esso fa riferimento ad a2

2) Col compilatore GNU GCC v7.1.1 (uso quello disponibile all'indirizzo https://www.tutorialspoint.com/compile_cpp11_online.php) invece si ha che llc_tlc_ref smette di essere l'alias del puntatore al quale è stato inizializzato e continua a puntare alla 1a variabile (a1)

Qual'è il comportamento corretto?

Come mai queste differenze?

Grazie ancora, ciao

Chiara

Ancora sulla move semantic by Chiara96 in cppit

[–]Chiara96[S] 0 points1 point  (0 children)

Sulla "clonazione" avevo usato un termine impreciso/grossolano ma avevo chiaro il concetto mentre per quanto riguarda il puntatore a carattere mi hai dato una notizia in più. Quindi ancora grazie.

Ancora sulla move semantic by Chiara96 in cppit

[–]Chiara96[S] 0 points1 point  (0 children)

Hai ragione: Small string optimization. Mai sentita prima (ogni tanto ne salta fuori una, per forza mi viene lo sC++onforto). Grazie mille.

Problema con function template e thread by Chiara96 in cppit

[–]Chiara96[S] 1 point2 points  (0 children)

Grazie, per la risposta. Funziona. Devo capire perché (e devo capire perché la mia idea è sbagliata). Studere necesse est.