Ещё одно применение shared_ptr
От: Alexander G Украина  
Дата: 26.12.09 13:47
Оценка: 7 (1) :)
Один тред запускает другой тред для выполнения долгой операции и периодически опрашивает флаг завершения.
При установке флага — получает результаты.

boost::shared_ptr может быть использован как такой флаг.

void lengthy_work(boost::shared_ptr<void> completion_flag) { ... }

void caller()
{
  boost::shared_ptr<void> completion_flag(new int);
  boost::weak_ptr<void> completion_check(completion_flag);
  boost::thread worker(lengthy_work, completion_flag);
  completion_flag.reset();

  ...
  case WM_TIMER:
    if (completion_check.expired()) 
    {
      OperationComplete();
    }
  ...
}


Преимущества:
1. Переносимо настолько, насколько потокобезопасность boost::shared_ptr переносима, при этом она реализована по возможности на атомарных операциях (см boost/smart_ptr/detail/ ).
2. Флаг завершения взводится неявно при возврате из потоковой функции, но при этом можно взвести его заранее явно.
Русский военный корабль идёт ко дну!
shared_ptr weak_ptr threads
Re: Ещё одно применение shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 26.12.09 14:32
Оценка: 50 (3)
Здравствуйте, Alexander G, Вы писали:

AG>Один тред запускает другой тред для выполнения долгой операции и периодически опрашивает флаг завершения.

AG>При установке флага — получает результаты.


А вот с "получает результаты" заминочка. Предложенный вариант не будет работать, т.к. weak_ptr<>::expired() не передаёт видимость данных между потоками, которые освободили ассоциированный shared_ptr, и потоком которому expired() вернул true (кто-то обещал?).
Это можно видеть например в sp_counted_base_gcc_sparc.hpp:
    long sp_counted_base::use_count() const // nothrow
    {
        return const_cast< int32_t const volatile & >( use_count_ );
    }

inline int32_t compare_and_swap( int32_t * dest_, int32_t compare_, int32_t swap_ )
{
    __asm__ __volatile__( "cas %0, %2, %1"
                        : "+m" (*dest_), "+r" (swap_)
                        : "r" (compare_)
                        : "memory" );

    return swap_;
}



Более корректно будет использовать поллинг напрямую завершения потока — обозрение завершения потока гарантирует передачу видимости над всеми данными:
  case WM_TIMER:
    if (worker.timed_join(0)) 
    {
      OperationComplete();
    }



Да и вообще необходимость использования поллинга как-то смущает, почему не сделать просто:
boost::thread worker ([]()
{
  lengthy_work(...);
  PostMessage(current_thread_id, WM_WORK_ACCOMPLISHED, work_id, 0);
});



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Ещё одно применение shared_ptr
От: Alexander G Украина  
Дата: 27.12.09 11:40
Оценка:
Здравствуйте, remark, Вы писали:

R>А вот с "получает результаты" заминочка. Предложенный вариант не будет работать, т.к. weak_ptr<>::expired() не передаёт видимость данных между потоками, которые освободили ассоциированный shared_ptr, и потоком которому expired() вернул true (кто-то обещал?).


угу, понял.


R>Да и вообще необходимость использования поллинга как-то смущает, почему не сделать просто:

R>
R>boost::thread worker ([]()
R>{
R>  lengthy_work(...);
R>  PostMessage(current_thread_id, WM_WORK_ACCOMPLISHED, work_id, 0);
R>});
R>


В таком варианте смущает возможность удаления адресата PostMessage до вызова PostMessage, во время вызова PostMessage, между PostMessage и получением сообщения — прийдётся предусмотреть проверку "существует ли адресат" и продление жизни адресата на время отправки сообщения.

Русский военный корабль идёт ко дну!
Re[2]: Ещё одно применение shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 27.12.09 12:21
Оценка: 6 (1)
Здравствуйте, remark, Вы писали:

R>А вот с "получает результаты" заминочка. Предложенный вариант не будет работать, т.к. weak_ptr<>::expired() не передаёт видимость данных между потоками, которые освободили ассоциированный shared_ptr, и потоком которому expired() вернул true (кто-то обещал?).

R>Это можно видеть например в sp_counted_base_gcc_sparc.hpp:
R>
R>    long sp_counted_base::use_count() const // nothrow
R>    {
R>        return const_cast< int32_t const volatile & >( use_count_ );
R>    }

R>inline int32_t compare_and_swap( int32_t * dest_, int32_t compare_, int32_t swap_ )
R>{
R>    __asm__ __volatile__( "cas %0, %2, %1"
R>                        : "+m" (*dest_), "+r" (swap_)
R>                        : "r" (compare_)
R>                        : "memory" );

R>    return swap_;
R>}
R>



Кстати, по-моему, та же проблема и в самом shared_ptr, т.е. они не обеспечивают и синхронизацию доступа к самому объекту, их sp_counted_base вообще ничего не гарантирует LOL

    // boost\smart_ptr\detail\sp_counted_base_solaris.hpp
    void release() // nothrow
    {
        if( atomic_dec_32_nv( &use_count_ ) == 0 )
        {
            dispose();
            weak_release();
        }
    }


$man atomic_ops

Standard C Library Functions atomic_ops(3C)

NAME
atomic_ops — atomic operations

SYNOPSIS
#include <atomic.h>

DESCRIPTION
This collection of functions provides atomic memory opera-
tions. There are 8 different classes of atomic operations:

atomic_add(3C) These functions provide an atomic addition
of a signed value to a variable.

atomic_and(3C) These functions provide an atomic logical
'and' of a value to a variable.

atomic_bits(3C) These functions provide atomic bit setting
and clearing within a variable.

atomic_cas(3C) These functions provide an atomic comparison
of a value with a variable. If the com-
parison is equal, then swap in a new value
for the variable, returning the old value of
the variable in either case.

atomic_dec(3C) These functions provide an atomic decrement
on a variable.

atomic_inc(3C) These functions provide an atomic increment
on a variable.

atomic_or(3C) These functions provide an atomic logical
'or' of a value to a variable.

atomic_swap(3C) These functions provide an atomic swap of a
value with a variable, returning the old
value of the variable.

SunOS 5.10 Last change: 12 Aug 2004 1

Standard C Library Functions atomic_ops(3C)

ATTRIBUTES
See attributes(5) for descriptions of the following attri-
butes:

____________________________________________________________
| ATTRIBUTE TYPE | ATTRIBUTE VALUE |
|_____________________________|_____________________________|
| Interface Stability | Stable |
|_____________________________|_____________________________|
| MT-Level | MT-Safe |
|_____________________________|_____________________________|

SEE ALSO
atomic_add(3C), atomic_and(3C), atomic_bits(3C),
atomic_cas(3C), atomic_dec(3C), atomic_inc(3C),
atomic_or(3C), atomic_swap(3C), membar_ops(3C), attri-
butes(5)

NOTES
Atomic instructions ensure global visibility of atomically-
modified variables on completion. In a relaxed store order
system, this does not guarantee that the visibility of other
variables will be synchronized with the completion of the
atomic instruction. If such synchronization is required,
memory barrier instructions must be used.
See
membar_ops(3C).

Atomic instructions can be expensive. since they require
synchronization to occur at a hardware level. This means
they should be used with care to ensure that forcing
hardware level synchronization occurs a minimum number of
times. For example, if you have several variables that need
to be incremented as a group, and each needs to be done
atomically, then do so with a mutex lock protecting all of
them being incremented rather than using the atomic_inc(3C)
operation on each of them.

SunOS 5.10 Last change: 12 Aug 2004 2


Т.е. там должно быть что-то типа:
    // boost\smart_ptr\detail\sp_counted_base_solaris.hpp
    void release() // nothrow
    {
        membar_exit();
        if( atomic_dec_32_nv( &use_count_ ) == 0 )
        {
            membar_enter();
            dispose();
            weak_release();
        }
    }


Вот вам и буст... пойду уточню на c.p.t


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Ещё одно применение shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 27.12.09 12:25
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, remark, Вы писали:


R>>А вот с "получает результаты" заминочка. Предложенный вариант не будет работать, т.к. weak_ptr<>::expired() не передаёт видимость данных между потоками, которые освободили ассоциированный shared_ptr, и потоком которому expired() вернул true (кто-то обещал?).


AG>угу, понял.


Этот флаг можно было бы легко реализовать самому... если бы не отсутствие портируемых атомарных операций. С приходом великого и могучего C++0x жизнь станет гораздо проще:

std::atomic<bool> completion;

// on thread end, or manually by thread function:
completion.store(true, std:memory_order_release);

// completion polling
if (completion.load(std:memory_order_acquire) == true))
  ...




R>>Да и вообще необходимость использования поллинга как-то смущает, почему не сделать просто:

R>>
R>>boost::thread worker ([]()
R>>{
R>>  lengthy_work(...);
R>>  PostMessage(current_thread_id, WM_WORK_ACCOMPLISHED, work_id, 0);
R>>});
R>>


AG>В таком варианте смущает возможность удаления адресата PostMessage до вызова PostMessage, во время вызова PostMessage, между PostMessage и получением сообщения — прийдётся предусмотреть проверку "существует ли адресат" и продление жизни адресата на время отправки сообщения.


Ну тут я подразумевал, что получатель WM_WORK_ACCOMPLISHED живёт столько же, сколько в твоём коде жил обработчик WM_TIMER. Если получатель может удалиться, не дождавшишь получения, то надо наворачивать что-то ещё.


AG>


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Ещё одно применение shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 27.12.09 12:35
Оценка:
Здравствуйте, remark, Вы писали:

R>Т.е. там должно быть что-то типа:

R>
R>    // boost\smart_ptr\detail\sp_counted_base_solaris.hpp
R>    void release() // nothrow
R>    {
R>        membar_exit();
R>        if( atomic_dec_32_nv( &use_count_ ) == 0 )
R>        {
R>            membar_enter();
R>            dispose();
R>            weak_release();
R>        }
R>    }
R>


R>Вот вам и буст... пойду уточню на c.p.t


http://groups.google.com/group/comp.programming.threads/browse_frm/thread/9cce4454db522705#
Посмотрим, что скажет совет стаи

Но я бы скрестил пальцы, если бы я использовал boost на SPARC RMO. Не знаю, экспуатирует ли Solaris SPARC в RMO режиме, но 64-битных Linux должен.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[4]: Ещё одно применение shared_ptr
От: Alexander G Украина  
Дата: 27.12.09 12:48
Оценка:
Здравствуйте, remark,

Правильно ли я понимаю, что:

1. weak_ptr должен быть готов к любым операциям с другими shared_ptr и weak_ptr в любом потоке?
2. если в сбросе shared_ptr будет всё корректно, то его синхронизация может быть выполнена таким образом, что как барьер для своих данных её использовать всё равно нельзя?

Русский военный корабль идёт ко дну!
Re[5]: Ещё одно применение shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 27.12.09 13:09
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Правильно ли я понимаю, что:


AG>1. weak_ptr должен быть готов к любым операциям с другими shared_ptr и weak_ptr в любом потоке?


да

AG>2. если в сбросе shared_ptr будет всё корректно, то его синхронизация может быть выполнена таким образом, что как барьер для своих данных её использовать всё равно нельзя?


Сложно сказать. Во-первых, это ничего не документировано, поэтому что они подразумевают сказать сложно. Во-вторых, может зависеть от того как именно использовать, например, если попробовать соорудить shared_ptr из weak_ptr и это провалится, то синхронизация будет обеспечена. Но expired() для этого всё-таки видимо не предназначен.

AG>


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.