hilpers


  hilpers > comp.lang.* > comp.lang.c

 #1  
28.11.2009, 19:33
Bronek Kozicki
W ramach prywatnych cwiczen sprawnosci umyslowej napisalem co ponizej.
Zadanie dla chetnych to:

1. odgadnac co ten kod robi i do czego moze byc przydatny

2. (opcjonalne) znalezc i poprawic bledy

3. zmienic na jedno (albo oba) z
a) (opcjonalne) poprawny
b) przenosny
c) prostszy
d) szybszy *)

Nagroda to wlasna satysfakcja i dopisanie do Copyright. Im wyzej na
liscie a) b) c) d), tym satyfakcja powinna byc wieksza. Program musi
dzialac na obu platformach sprzetowych : ia32 oraz x64.

*)Poprawienie wydajnosci liczy sie tylko w jednej sciezce wykonania :
uzyskanie dostepu do wczesniej utworzonego obiektu. Inne nie musza byc
optymalne.


B.


// Copyright (C) 2009, Bronislaw (Bronek) Kozicki
//
// Use, modification, and distribution is subject to the Boost Software
// License, Version 1.0. , see http://www.boost.org/LICENSE_1_0.txt

namespace util
{
template <typename T>
class single_ptr
{
enum {Absent, Coming, Present, Dying};
static __declspec(align(8)) volatile long ref_counter_;
static __declspec(align(8)) volatile long ref_state_;
static __declspec(align(8)) const T * volatile ref_impl_;

const T * impl_;

public:
single_ptr()
{
for(;;)
{
if (_InterlockedIncrement(&ref_counter_) == 1)
{
while (_InterlockedCompareExchange(&ref_state_, Coming,
Absent) != Absent)
::Sleep(1);

try
{
impl_ = new T();
}
catch(...)
{
_InterlockedExchange(&ref_state_, Absent);
_InterlockedDecrement(&ref_counter_);
throw;
}

InterlockedExchangePointer((void* volatile*) &ref_impl_,
(void*)impl_);
_InterlockedExchange(&ref_state_, Present);
break;
}
else
{
if (
::InterlockedCompareExchange(&ref_state_, Present, Present)
== Present
&& (
impl_ = (const
T*)InterlockedCompareExchangePointer((void* volatile*) &ref_impl_, 0, 0)
) != NULL
)
break;

_InterlockedDecrement(&ref_counter_);
::Sleep(1);
}
}
}

const T * operator-> () const
{
return impl_;
}

const T& operator* () const
{
return *impl_;
}

~single_ptr()
{
if (_InterlockedDecrement(&ref_counter_) == 0)
{
while (_InterlockedCompareExchange(&ref_state_, Dying, Present)
!= Present)
::Sleep(1);

InterlockedExchangePointer((void* volatile*) &ref_impl_, 0);
_InterlockedExchange(&ref_state_, Absent);

delete impl_;
}
}
};

template <typename T>
__declspec(align(8)) volatile long single_ptr<T>::ref_counter_ = 0;

template <typename T>
__declspec(align(8)) volatile long single_ptr<T>::ref_state_ =
single_ptr<T>::Absent;

template <typename T>
__declspec(align(8)) const T * volatile single_ptr<T>::ref_impl_ = NULL;
}
 #2  
28.11.2009, 22:43
arturbac
Bronek Kozicki pisze:
> W ramach prywatnych cwiczen sprawnosci umyslowej napisalem co ponizej.
> Zadanie dla chetnych to:
>
> 1. odgadnac co ten kod robi i do czego moze byc przydatny


Singelton LF.

>
> 2. (opcjonalne) znalezc i poprawic bledy


Pomiedzy Coming a Present 1-ki mija czas w ktorym moze zostac utworzona
instancja w innym watku ktora wejdzie w else a na warunku
if( ::InterlockedCompareExchange(&ref_state_, Present, Present) == Present
nie wejdzie w break wiec zrobi _InterlockedDecrement(&ref_counter_);

Bediesz miec 2 instancje opakowan i licznik 1 a destruktor zawsze
zmniejsza licznik, wiec wyglada na to ze moze wystapic delete 0;

1 instancja Coming
2 instancja else Decrement(&ref_counter_);
1 instancja Present
2 ~single_ptr()
2 if (Decrement(&ref_counter_) == 0)
2 Exchange(&ref_state_, Dying, Present)
2 delete impl_; // delete 0


po co 'Sleep' po _InterlockedDecrement(&ref_counter_); ?

Czyli w ogolnosci niezaleznie od konstrukcji kazda instancja zmniejsza
licznik w destruktorze mimo iz moze zrobic to w konstruktorze gdy sie
jej nie uda.

~single_ptr()
if (_InterlockedDecrement(&ref_counter_) == 0)

Dzis pozno a jutro mam laborki rano wiec wiecej nie napisze, ale zadanie
fajne zmusza do myslenia, wydaje mi sie ze zamotales troche tj
przynajmiej w momencie gdy czas zycia otoczek bedzie krótki.

Co do przenosnosci, nie uzywam bezposrednio Interlocked a pisze funkcje
inline cas( where , old_val, new_val ) z kolejnoscia zgodna z przyjeta
nomenklatura czyli inna niz ma M$
a te customizuje zaleznie od gcc, msvc etc.

http://gcc.gnu.org/onlinedocs/gcc-4....-Builtins.html

NP:

#if defined(WIN32)

#pragma intrinsic (_InterlockedCompareExchange64)
#pragma intrinsic (_InterlockedCompareExchange)
#pragma intrinsic (_InterlockedIncrement)
#pragma intrinsic (_InterlockedDecrement)
#pragma intrinsic (_InterlockedExchangeAdd)

extern "C" {
long _InterlockedIncrement( volatile long * ptr );
long _InterlockedDecrement( volatile long * ptr );
long _InterlockedOr( volatile long * ptr, long value );
long _InterlockedCompareExchange( volatile long * dst, long new_val,
long old_val );
__int64 _InterlockedCompareExchange64( volatile __int64 * dst, __int64
new_val, __int64 old_val );
}
#endif

namespace mt
{


#if !defined(WIN32)
typedef int32_t mt_int32_t;
inline int32_t interlocked_increment( volatile int32_t * ptr ) {
return __sync_fetch_and_add ( ptr, 1); }
inline int32_t interlocked_decrement( volatile int32_t * ptr ) {
return __sync_fetch_and_sub ( ptr, 1); }

inline int64_t cas( volatile int64_t * loc, int64_t comparand, int64_t
value ){
return __sync_val_compare_and_swap( loc, comparand, value);
}
inline int32_t cas( volatile int32_t * loc, int32_t comparand, int32_t
value ){
return __sync_val_compare_and_swap( loc, comparand, value);
}

inline int32_t interlocked_add ( volatile int32_t * ptr, int32_t value ) {
return __sync_fetch_and_add ( ptr, value );
}
#else //_MSC_VER

typedef long mt_int32_t;

inline long interlocked_increment( volatile long * ptr ) { return
_InterlockedIncrement ( ptr ); }
inline long interlocked_decrement( volatile long * ptr ) { return
_InterlockedDecrement ( ptr ); }
inline __int64 cas( volatile __int64 * loc, __int64 comparand, __int64
value ){
return _InterlockedCompareExchange64( loc, value, comparand);
}
inline long cas( volatile long * loc, long comparand, long value ){
return _InterlockedCompareExchange( loc, value, comparand);
}
inline long interlocked_add ( volatile long * ptr, long value ) {
return _InterlockedExchangeAdd( ptr, value );
}
#endif

}

A.
 #3  
28.11.2009, 22:50
Sebastian Bialy
Bronek Kozicki wrote:
> a) (opcjonalne) poprawny


Slabo znam sie na exchange, ale zaryzykuje taka wapliwosc, przynajmniej
sie czegos naucze:

Watek A wykonuje fragment:

~single_ptr()
{
if (_InterlockedDecrement(&ref_counter_) == 0)
{


Watek B:

single_ptr()
....
if (::InterlockedCompareExchange(&ref_state_, Present, Present) == Present
&& ( impl_ = (const T*)InterlockedCompareExchangePointer((void*
volatile*) &ref_impl_, 0, 0)
) != NULL



Watek A (kontynuacja ~single_ptr()):

InterlockedExchangePointer((void* volatile*) &ref_impl_, 0);
_InterlockedExchange(&ref_state_, Absent);

delete impl_;

Watek B wychodzi z konstruktora i ma skasowany pointer w impl_ - BUM.

Licze na lagodny wymiar kary :D
 #4  
29.11.2009, 11:36
Bronek Kozicki
On 28/11/2009 23:50, Sebastian Bialy wrote:
[..]
>>

> Watek A (kontynuacja ~single_ptr()):
>
> InterlockedExchangePointer((void* volatile*) &ref_impl_, 0);
> _InterlockedExchange(&ref_state_, Absent);
>
> delete impl_;
>


nie grozi.

watek A.

~single_ptr()
{
if (_InterlockedDecrement(&ref_counter_) == 0)


tutaj licznik zostal atomowo zmniejszony.


watek B.

single_ptr()
{
for(;;)
{
if (_InterlockedIncrement(&ref_counter_) == 1)
{

jezeli wykonalo sie *przed* zmniejszeniem licznika w destruktorze, to
wynikiem zmniejszenia w destruktorze nie jest zero, a wiec delete sie
nie wykona. Jezeli wykonalo sie *po* zmniejszeniu w destruktorze (które
zwrócilo 0, powodujace delete) to wynikiem zwiekszenia jest 1, a wiec
wykona sie new.

Kolejnosc wykonania new i delete jest regulowana przez czekanie na stan
"present" w destruktorze , oraz na stan "absent" w konstruktorze. Ten
drugi jest ustawiony statycznie oraz w destruktorze , po wyzerowaniu
wskaznika na implementacje. To znaczy ze wskaznik najpierw musi zostac
wyzerowany, zanim konstruktor ustawi go znowu.

Ale kod ma inny, dosyc oczywisty, blad - brak konstruktora kopiujacego.
Ponizej poprawiona i nieco uproszczona wersja


B.



// Copyright (C) 2009, Bronislaw (Bronek) Kozicki
//
// Use, modification, and distribution is subject to the Boost Software
// License, Version 1.0. , see http://www.boost.org/LICENSE_1_0.txt

namespace util
{
template <typename T>
class single_ptr
{
enum {Empty, Alive, Trans};
static __declspec(align(8)) volatile long ref_counter_;
static __declspec(align(8)) volatile long ref_state_;
static const T * ref_impl_;

public:
single_ptr()
{
for(;;)
{
if (_InterlockedIncrement(&ref_counter_) == 1)
{
while (_InterlockedCompareExchange(&ref_state_, Trans, Empty)
!= Empty)
::Sleep(1);

const T* impl = NULL;
try
{
impl = new T();
}
catch(...)
{
_InterlockedExchange(&ref_state_, Empty);
_InterlockedDecrement(&ref_counter_);
throw;
}

ref_impl_ = impl;
_InterlockedExchange(&ref_state_, Alive);
break;
}
else
{
if (_InterlockedCompareExchange(&ref_state_, Alive, Alive) ==
Alive)
break;

_InterlockedDecrement(&ref_counter_);
::Sleep(1);
}
}
}

single_ptr(const single_ptr& src)
{
_InterlockedIncrement(&ref_counter_);
}

// No need to declare = , default generated is just fine
// single_ptr& operator=(const single_ptr& src);

const T * operator-> () const
{
return ref_impl_;
}

const T& operator* () const
{
return *ref_impl_;
}

~single_ptr()
{
if (_InterlockedDecrement(&ref_counter_) == 0)
{
while (_InterlockedCompareExchange(&ref_state_, Trans, Alive)
!= Alive)
::Sleep(1);

const T* impl = ref_impl_;
ref_impl_ = NULL;
_InterlockedExchange(&ref_state_, Empty);

delete impl;
}
}
};

template <typename T>
__declspec(align(8)) volatile long single_ptr<T>::ref_counter_ = 0;

template <typename T>
__declspec(align(8)) volatile long single_ptr<T>::ref_state_ =
single_ptr<T>::Empty;

template <typename T>
const T * single_ptr<T>::ref_impl_ = NULL;
}
 #5  
29.11.2009, 11:57
Bronek Kozicki
On 28/11/2009 23:43, arturbac wrote:
> Bronek Kozicki pisze:
>
> Singelton LF.
>> Pomiedzy Coming a Present 1-ki mija czas w ktorym moze zostac utworzona

> instancja w innym watku ktora wejdzie w else a na warunku
> if( ::InterlockedCompareExchange(&ref_state_, Present, Present) == Present
> nie wejdzie w break wiec zrobi _InterlockedDecrement(&ref_counter_);


zgadza sie , a parze do InterlockedIncrement na poczatku petli. Drugi
watek nie zrobi drugiej instancji, druga instancja zostanie utworzona
tylko jezeli wynikiem atomowego zwiekszenia jest 1.

> 1 instancja Coming

ta instancja ustawila licznik na 1

> 2 instancja else Decrement(&ref_counter_);

ta instancja ustawila wczesniej licznik na 2, a teraz z powrotem na 1
przed powtórzeniem iteracji

> 1 instancja Present

acha, czyli wskaznik juz jest ustawiony

> 2 ~single_ptr()

zaraz, a kiedy 2 wyszlo z konstruktora? Bo jezeli wyszlo to licznik
byl 2.

> po co 'Sleep' po _InterlockedDecrement(&ref_counter_); ?

czekanie az inny watek ustawi Present. Pozyteczne jezeli masz jedna
jednostke wykonania (CPU z 1 core, albo po prostu bardzo zajety watkami
o wyzszym priorytecie) i jezeli watek ustawiajacy Present ma *nizszy*
priorytet od czekajacego na Present.

> Czyli w ogolnosci niezaleznie od konstrukcji kazda instancja zmniejsza
> licznik w destruktorze mimo iz moze zrobic to w konstruktorze gdy sie
> jej nie uda.


przeczytaj uwaznie (czy przeoczyles petle w konstruktorze?), moze te
nowsza uproszczona wersje, w innym moim poscie. Pamietaj ze kazda
instrukcja Interlocked* wymusza pelna (load i release) bariere pamieci.


B.
 #6  
29.11.2009, 15:39
arturbac
Mea culpa przeoczylem petle.
 #7  
29.11.2009, 20:30
Bronek Kozicki
On 29/11/2009 12:36, Bronek Kozicki wrote:
> On 28/11/2009 23:50, Sebastian Bialy wrote:
>> Bronek Kozicki wrote:



i jeszcze dodana obsluga typów bez domyslnego konstruktora (z pomoca
type erasure). Trzeba wykonac init() zanim mozna bedzie utworzyc
pierwszy obiekt - przyklady na koncu. Mozna stosowac zamiast singletonów ;)


B.


PS. ten kod jest dla zabawy. Jezeli ktos musi go na powaznie stosowac w
projekcie, to moze bedzie dzialal, a moze nie. Zaleznie od wysilku
wlozonego w jego zrozumienie oraz szczescia. Signletony to brudny biznes
i takie pozostaja, nawet jezeli je ladnie opakowac i troche ograniczyc.

PS2. Jezeli do deklaracji statycznych skladowych dopisac
__declspec(thread), to zrobi sie "single_tls_ptr". Byc moze bardziej
pozyteczny.



// Copyright (C) 2009, Bronislaw (Bronek) Kozicki
//
// Use, modification, and distribution is subject to the Boost Software
// License, Version 1.0. , see http://www.boost.org/LICENSE_1_0.txt
//
// NO WARRANTY OF ANY KIND, EITHER IMPLIED OR EXPRESS, ETC. ETC.
//

#include <stdexcept>

namespace util
{
template <typename T>
class single_ptr
{
enum {Undef, Trans1, Empty, Trans2, Alive};
static __declspec(align(8)) volatile long ref_counter_;
static __declspec(align(8)) volatile long ref_state_;
static const T * ref_impl_;

typedef const T* (*acquire_t)(const void*);
typedef void (*release_t)(const void*, const T*);
static acquire_t acquire_;
static release_t release_;
static const void* parameter_;

template <typename Def>
static const T* acquire(const void* p)
{
Def* dp = (Def *)p;
return dp->acquire();
}

template <typename Def>
static void release(const void* p, const T* tp)
{
const Def* dp = (Def *)p;
dp->release(tp);
}

struct def_default
{
const T* acquire() const {return new T();}
void release(const T* p) const {delete p;}
};

template <typename Arg1>
struct def_arg1
{
const Arg1 arg1;
const T* acquire() const {return new T(arg1);}
void release(const T* p) const {delete p;}
};

template <typename Arg1, typename Arg2>
struct def_arg2
{
const Arg1 arg1;
const Arg2 arg2;
const T* acquire() const {return new T(arg1, arg2);}
void release(const T* p) const {delete p;}
};

template <typename Arg1, typename Arg2, typename Arg3>
struct def_arg3
{
const Arg1 arg1;
const Arg2 arg2;
const Arg3 arg3;
const T* acquire() const {return new T(arg1, arg2, arg3);}
void release(const T* p) const {delete p;}
};

template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
struct def_arg4
{
const Arg1 arg1;
const Arg2 arg2;
const Arg3 arg3;
const Arg4 arg4;
const T* acquire() const {return new T(arg1, arg2, arg3, arg4);}
void release(const T* p) const {delete p;}
};

template <typename Arg1, typename Arg2, typename Arg3, typename
Arg4, typename Arg5>
struct def_arg5
{
const Arg1 arg1;
const Arg2 arg2;
const Arg3 arg3;
const Arg4 arg4;
const Arg5 arg5;
const T* acquire() const {return new T(arg1, arg2, arg3, arg4,
arg5);}
void release(const T* p) const {delete p;}
};

public:
struct error : std::logic_error
{
enum {Undef, Redef};
explicit error(int c)
: logic_error(c == Undef ? "init() has not been called" :
"init() called more than once")
, code(c)
{}
const int code;
};

single_ptr()
{
for(;;)
{
if (_InterlockedIncrement(&ref_counter_) == 1)
{
for (;;)
{
const int e = _InterlockedCompareExchange(&ref_state_,
Trans2, Empty);
if (e == Empty)
break;
else if (e == Undef)
throw error(error::Undef);

::Sleep(1);
}

const T* impl = NULL;
try
{
impl = acquire_(parameter_);
}
catch(...)
{
_InterlockedExchange(&ref_state_, Empty);
_InterlockedDecrement(&ref_counter_);
throw;
}

ref_impl_ = impl;
_InterlockedExchange(&ref_state_, Alive);
break;
}
else
{
if (_InterlockedCompareExchange(&ref_state_, Alive, Alive) ==
Alive)
break;

_InterlockedDecrement(&ref_counter_);
::Sleep(1);
}
}
}

single_ptr(const single_ptr& src)
{
_InterlockedIncrement(&ref_counter_);
}

// No need to declare = , default generated is just fine
// single_ptr& operator=(const single_ptr& src);

const T * operator-> () const
{
return ref_impl_;
}

const T& get() const
{
return *ref_impl_;
}

const T& operator* () const
{
return get();
}

static void init()
{
if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
throw error(error::Redef);

static const def_default d;
acquire_ = &acquire<def_default>;
release_ = &release<def_default>;
parameter_ = (const void*) &d;

_InterlockedExchange(&ref_state_, Empty);
}

template <typename Arg1>
static void init(const Arg1& a1)
{
if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
throw error(error::Redef);

static const def_arg1<Arg1> d = {a1};
acquire_ = &acquire<def_arg1<Arg1> >;
release_ = &release<def_arg1<Arg1> >;
parameter_ = (const void*) &d;

_InterlockedExchange(&ref_state_, Empty);
}

template <typename Arg1, typename Arg2>
static void init(const Arg1& a1, const Arg2& a2)
{
if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
throw error(error::Redef);

static const def_arg2<Arg1, Arg2> d = {a1, a2};
acquire_ = &acquire<def_arg2<Arg1, Arg2> >;
release_ = &release<def_arg2<Arg1, Arg2> >;
parameter_ = (const void*) &d;

_InterlockedExchange(&ref_state_, Empty);
}

template <typename Arg1, typename Arg2, typename Arg3>
static void init(const Arg1& a1, const Arg2& a2, const Arg3& a3)
{
if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
throw error(error::Redef);

static const def_arg3<Arg1, Arg2, Arg3> d = {a1, a2, a3};
acquire_ = &acquire<def_arg3<Arg1, Arg2, Arg3> >;
release_ = &release<def_arg3<Arg1, Arg2, Arg3> >;
parameter_ = (const void*) &d;

_InterlockedExchange(&ref_state_, Empty);
}

template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
static void init(const Arg1& a1, const Arg2& a2, const Arg3& a3,
const Arg4& a4)
{
if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
throw error(error::Redef);

static const def_arg4<Arg1, Arg2, Arg3, Arg4> d = {a1, a2, a3, a4};
acquire_ = &acquire<def_arg4<Arg1, Arg2, Arg3, Arg4> >;
release_ = &release<def_arg4<Arg1, Arg2, Arg3, Arg4> >;
parameter_ = (const void*) &d;

_InterlockedExchange(&ref_state_, Empty);
}

template <typename Arg1, typename Arg2, typename Arg3, typename
Arg4, typename Arg5>
static void init(const Arg1& a1, const Arg2& a2, const Arg3& a3,
const Arg4& a4, const Arg5& a5)
{
if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
throw error(error::Redef);

static const def_arg5<Arg1, Arg2, Arg3, Arg4, Arg5> d = {a1, a2,
a3, a4, a5};
acquire_ = &acquire<def_arg5<Arg1, Arg2, Arg3, Arg4, Arg5> >;
release_ = &release<def_arg5<Arg1, Arg2, Arg3, Arg4, Arg5> >;
parameter_ = (const void*) &d;

_InterlockedExchange(&ref_state_, Empty);
}

template <typename Def>
static void init(const Def* pd)
{
if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
throw error(error::Redef);

acquire_ = &acquire<Def>;
release_ = &release<Def>;
parameter_ = (const void*) pd;

_InterlockedExchange(&ref_state_, Empty);
}

~single_ptr()
{
if (_InterlockedDecrement(&ref_counter_) == 0)
{
while (_InterlockedCompareExchange(&ref_state_, Trans2, Alive)
!= Alive)
::Sleep(1);

const T* impl = ref_impl_;
ref_impl_ = NULL;
_InterlockedExchange(&ref_state_, Empty);

release_(parameter_, impl);
}
}
};

template <typename T>
__declspec(align(8)) volatile long single_ptr<T>::ref_counter_ = 0;

template <typename T>
__declspec(align(8)) volatile long single_ptr<T>::ref_state_ =
single_ptr<T>::Undef;

template <typename T>
const T * single_ptr<T>::ref_impl_ = NULL;

template <typename T>
typename single_ptr<T>::acquire_t single_ptr<T>::acquire_ = NULL;

template <typename T>
typename single_ptr<T>::release_t single_ptr<T>::release_ = NULL;

template <typename T>
const void* single_ptr<T>::parameter_ = NULL;
}



/////// sample use



struct Test : boost::noncopyable
{
const std::string str;
Test(std::string::size_type i, char t, const std::string& before,
const std::string& after, char t2)
: str(std::string(i, t) + before + after + t2 + std::string(i, t))
{}
};

class IGulp : boost::noncopyable
{
public:
virtual const std::string& how() const = 0;
virtual ~IGulp() {}
};

class Gulp : public IGulp
{
explicit Gulp(const std::string& t) : str(t) {}
friend class Throat;
const std::string str;
public:
virtual const std::string& how() const {return str;}
};

class Throat : boost::noncopyable
{
std::string str;
public:
explicit Throat(const char* t) : str(t) {}
const IGulp* acquire() const {return new Gulp(str);}
void release(const IGulp* p) const {delete p;}
};

int main(int argc, char** argv)
{
util::single_ptr<double>::init();
util::single_ptr<double> d;
std::printf("%f\n", *d);

util::single_ptr<int>::init(42);
util::single_ptr<int> i;
std::printf("%i\n", *i);

util::single_ptr<Test>::init(3, '-', std::string("hello "),
std::string("world"), '!');
util::single_ptr<Test> t;
std::printf("%s\n", t->str.c_str());

static const Throat throat("gulp!");
util::single_ptr<IGulp>::init(&throat);
util::single_ptr<IGulp> g;
std::printf("%s\n", g->how().c_str());
}
 #8  
29.11.2009, 21:11
arturbac
Bronek Kozicki pisze:
> PS2. Jezeli do deklaracji statycznych skladowych dopisac
> __declspec(thread), to zrobi sie "single_tls_ptr". Byc moze bardziej
> pozyteczny.


Bedziemy miec w tym przypadku statyczna zmienna per thread wiec po co
nam synchronizacja skoro wszystkie odwolania do danej zmiennej statyczna
beda pochodzic z tego samego watku ?
 #9  
30.11.2009, 08:04
Krzysiek Kowaliczek
Uzytkownik Bronek Kozicki napisal:

> template <typename T>
> class single_ptr
> {
> enum {Absent, Coming, Present, Dying};
> static __declspec(align(8)) volatile long ref_counter_;
> static __declspec(align(8)) volatile long ref_state_;
> static __declspec(align(8)) const T * volatile ref_impl_;


Volatile nie jest potrzebne. Interlockedxxxx zapewniaja pelna
bariere pamieci.

> while (_InterlockedCompareExchange(&ref_state_, Coming,
> Absent) != Absent)
> ::Sleep(1);


Sleep ( 0 ).
Podanie 0 do Sleep spowoduje, ze watek zrezygnuje z czasu jaki
zostal mu przyznany, ale pozostanie aktywny do wykonania.

> const T * operator-> () const
> {
> return impl_;
> }


I tutaj najbardziej watpliwy fragment implementacji, jezeli chodzi
o sens uzycia tego:
1. Jezeli chcialbym uzyc LF do implementacji singletonu to po to aby,
utworzyc go w momencie uzycia:
const T * operator-> () const
{
// inicjacja lock-free.
}

2. Moga wystapic wielokrotnie serie tworzenia i niszczenia singletona,
zmniejszajac wydajnosc aplikacji:

void compute_data ( std::vector<char>& data )
{
util::single_ptr<JakisCiezkiObiekt> o;
o-> get_data ( data );
// cos tam robimy
}
// watek 1.
for ( int i = 0; i < 10000; ++i )
{
compute_data ( ...

// watek 2.
for ( int i = 10000; i < 20000; ++i )
{
compute_data ( ...

Jezeli mialbym ochote zastosowania singletonu to zrobilbym to tak:
// naglowek:
singleton<JakisCiezkiObiekt>& get_instance ();

// implementacja
namespace
{
singleton<JakisCiezkiObiekt> object;
}

singleton<JakisCiezkiObiekt>& get_instance ();

Zapewnia mi to utworzenie obiektu przy ladowaniu biblioteki.

Jak chce obiekt zainicjowac:
// poczatek przetwarzania ( ewentualnie boost::once ).
get_instance ().init ( ....

// koniec:
get_instance ().release ();

Z ta uwaga, ze init tworzy nowy obiekt a release go niszczy.

Pozdrawiam
KK
 #10  
30.11.2009, 09:43
arturbac
Krzysiek Kowaliczek pisze:
> Uzytkownik Bronek Kozicki napisal:
>
>> template <typename T>
>> class single_ptr
>> {
>> enum {Absent, Coming, Present, Dying};
>> static __declspec(align(8)) volatile long ref_counter_;
>> static __declspec(align(8)) volatile long ref_state_;
>> static __declspec(align(8)) const T * volatile ref_impl_;

>
> Volatile nie jest potrzebne. Interlockedxxxx zapewniaja pelna
> bariere pamieci.


Interlocked wymagaja adresu volatile * long [__int64]
i ma to drugie dno w msvc w zakresie zapewnienia kolejnosci zapisow do
pamieci.

LONG __cdecl InterlockedExchange(
__inout LONG volatile *Target,
__in LONG Value
);
 #11  
30.11.2009, 12:47
Piotr Wyderski
Bronek Kozicki wrote:

> 1. odgadnac co ten kod robi i do czego moze byc przydatny


Leniwy singleton lock-free, a przynajmniej jego czesc inicjalizacyjna.
Bo czesc sprzatajaca zawiera w sobie stara dobra bombe nieokreslonosci
kasowania zmiennych statycznych miedzy jednostkami translacji.

> 3. zmienic na jedno (albo oba) z
> a) (opcjonalne) poprawny


Skoro uzywasz wariantu DCLP, to po co ten singleton ma byc lock-free?

> b) przenosny


Jak klepniecie std::atomic, to bedzie :-)

> c) prostszy


Tutaj prostota nie jest wskazana. Lepsza jest poprawnosc.
Ale jesli mozna sobie na to pozwolic, to ja bym uzyl optymistycznej
kontroli wspólbieznosci -- niech kazdy watek w ciemno tworzy
swój singleton, a tylko jednemu uda sie podstawienie globalne,
reszta poniszczy nadmiarowe kopie. W praktyce czesto taniej
i latwiej zrobic cos kilka razy i potem posprzatac, niz wymyslac
zlozony protokól zabezpieczajacy. Dokladnie z tego powodu
chcialbym miec metody reset_if() oraz transfer_if() w std::unique_ptr,
ale w cuda nie wierze, wiec stosowny wskazniczek napisalem sobie sam.

Pozdrawiam
Piotr Wyderski
 #12  
30.11.2009, 14:15
Piotr Wyderski
Krzysiek Kowaliczek wrote:

> Sleep ( 0 ).
> Podanie 0 do Sleep spowoduje, ze watek zrezygnuje z czasu jaki
> zostal mu przyznany, ale pozostanie aktywny do wykonania.


Ja wole SwitchToThread().

> 2. Moga wystapic wielokrotnie serie tworzenia i niszczenia singletona,
> zmniejszajac wydajnosc aplikacji:


Prawda, ale to czesciej bywa zaleta niz wada. Tylko trzeba
sobie przestroic punkt widzenia. ;-)

Pozdrawiam
Piotr Wyderski
 #13  
30.11.2009, 14:23
arturbac
Piotr Wyderski pisze:
> Bronek Kozicki wrote:
>> c) prostszy

>
> Tutaj prostota nie jest wskazana. Lepsza jest poprawnosc.
> Ale jesli mozna sobie na to pozwolic, to ja bym uzyl optymistycznej
> kontroli wspólbieznosci -- niech kazdy watek w ciemno tworzy
> swój singleton, a tylko jednemu uda sie podstawienie globalne,
> reszta poniszczy nadmiarowe kopie. W praktyce czesto taniej
> i latwiej zrobic cos kilka razy i potem posprzatac, niz wymyslac
> zlozony protokól zabezpieczajacy. Dokladnie z tego powodu
> chcialbym miec metody reset_if() oraz transfer_if() w std::unique_ptr,
> ale w cuda nie wierze, wiec stosowny wskazniczek napisalem sobie sam.



Tu to samo pomyslalem ale doszedlem do wniosku ze autorowi moze zalezec
na fakcie iz powstaje dokladnie jedna instancja T otwierajaca np ten sam
port IP.
 #14  
30.11.2009, 17:02
Bronek Kozicki
On 29/11/2009 22:11, arturbac wrote:
> Bronek Kozicki pisze:
>> PS2. Jezeli do deklaracji statycznych skladowych dopisac
>> __declspec(thread), to zrobi sie "single_tls_ptr". Byc moze bardziej
>> pozyteczny.

>
> Bedziemy miec w tym przypadku statyczna zmienna per thread wiec po co
> nam synchronizacja skoro wszystkie odwolania do danej zmiennej statyczna
> beda pochodzic z tego samego watku ?


sluszna uwaga. Oczywiscie "single_tls_ptr" moze byc znacznie prostszy


B.
 #15  
30.11.2009, 17:34
Bronek Kozicki
On 30/11/2009 09:04, Krzysiek Kowaliczek wrote:
> Volatile nie jest potrzebne. Interlockedxxxx zapewniaja pelna
> bariere pamieci.


naglówki Win32 tego wymagaja i nie mam ochoty sie z nimi klócic.

>> while (_InterlockedCompareExchange(&ref_state_, Coming, Absent) !=
>> Absent)
>> ::Sleep(1);

>
> Sleep ( 0 ).
> Podanie 0 do Sleep spowoduje, ze watek zrezygnuje z czasu jaki
> zostal mu przyznany, ale pozostanie aktywny do wykonania.


a jezeli czekam na watek o niskim priorytecie? Z tego co pamietam, tylko
Sleep(1) odda mu scheduler.

> 1. Jezeli chcialbym uzyc LF do implementacji singletonu to po to aby,
> utworzyc go w momencie uzycia:


to jest funkcja konstruktora, a dokladniej licznika referencji. Tworzysz
raz (pierwsze zawolanie konstruktora), uzywasz kiedy potrzebujesz
(kolejne zawolanie konstruktora), i nawet (woohoo! singletony tego nie
maja!) przy rozsadnym projekcie, masz przewidywalna destrukcje.

> 2. Moga wystapic wielokrotnie serie tworzenia i niszczenia singletona,
> zmniejszajac wydajnosc aplikacji:


wlasnie dlatego sugeruje utworzyc go wczesniej.

> singleton<JakisCiezkiObiekt>& get_instance ();


bleee. jakie paskudne slowo "singleton".

> // poczatek przetwarzania ( ewentualnie boost::once ).
> get_instance ().init ( ....
>
> // koniec:
> get_instance ().release ();
>
> Z ta uwaga, ze init tworzy nowy obiekt a release go niszczy.


jezeli teraz zastosujesz RAII i opakujesz init() w konstruktor a release
w destruktor, to uzyskasz klase podobna do single_ptr . Z tym ze ja
dodatkowo rozbilem wolanie konstruktora od przekazywania mu parametrów.


B.

Podobne wątki
biblioteka swt-M20070212-1330-win32-win32-x86

Witam! Mogę prosić o krutką acz kolwiek konkretną isnstrukcję instalacji biblioteki swt w eclipse 3.2.2.Chodzi mi o to co i gdzie wklejić!? Z góry dziękuję!!

dla chetnych v500

Witam.Dla chetnych posiadaczy motorola v300,V500 i wyzej podaje linka do strony (znalazlem w necie) z dzwonkami w formacie mp3,tapetami itd.Pozdrawiam

win32 api, win32.hlp, pliki naglowkowe - dziwy

witam zapragnalem popisac sobie troche "niestandardowych" rzeczy i pojawily sie problemy: - w pomocy win32.hlp brakuje mi opisu niektorych struktur np. LPNMTVCUSTOMDRAW oraz...

Biznes dla chetnych

Biznes mailingowy dla chetnych i ambitnych ludzi ktorzy chca zarobic troche grosza wiecej informacji na priv :)


Czasy w strefie GMT. Teraz jest 22:02. | Privacy Policy