Boost logo

Boost Users :

Subject: [Boost-users] [boost][sync?] "Distributed" value (or something...)
From: Klaim - Joël Lamotte (mjklaim_at_[hidden])
Date: 2014-07-29 15:44:13


I would like to ask Boost developers and users:

 1. Am I the only one seeing the following pattern in my concurrent systems
code?
 2. Is there already some tools available in Boost that does what I
describe here and that I am not aware of?
 3. If not, would there be any interest in the tool I'm describing here or
not? Is it a dumb idea?
 (I keep getting back to this idea but I'm surprised not finding anything
similar online)

The pattern that I am seeing occurs when:
 1. I have a value or a set of values in an object or several objects which
value(s) describe some kind of configuration that several other systems
will read and use.
 2. The object(s) will be updated only by one thread (at a time).
 3. When the object's value is modified, all the systems that rely on the
object's value have to be notified
    so that they update themselves using that value.
 4. The systems I am refering to can use different threads for updating and
we can assume that they are running concurrently.
 5. It is not important that the source object's value is synched with the
reader's values at all time,
    however it is important that the sequence of changes to the source
object's value is signaled to the readers
    exactly in the same order and "as fast as possible", but delay is ok.

The simplest solution to solve this is to have a mutex locking the value
and the systems checking the value
regularly. (that is, using syncrhonized_value for example)
However the locking and looping reads might not be necessary if it is done
in another way (if I am correct, I still lack experience in the domain).

The solution that I am thinking about would look like this.

   /////////
   // The object containing the value used by the other systems.
   class System_A
   {
       distributed_source<int> m_foo_count; // changes to this object
will be sent to "readers"...
   public:
       System_A() : m_foo_count(42) ...
       {}
       //...

       // thread-safe read-only access
       distributed_value<int> make_foo_count_reader() const {
           return distributed_value<int>{ m_foo_count };
       }
   };

   /////////
   // The different systems acquire a read access to the value.

   class System_B
   {
       distributed_value<int> foo_count; // read-only value reflecting
System_A::m_foo_count's value since last update.
   public:
       SystemB( const System_A& system_a, ... )
           : m_foo_count( system_a.make_foo_count_reader() )
        {
            // setup the initial config...
            on_foo_count_changed( *m_foo_count );

            // trigger some processing when SystemA::foo_count's value have
been changed:
            m_foo_count.on_changed( [this]( int new_value ){ // called
after the value have been changed
                m_work_queue.push( [=]{ // will be executed on the next
update cycle for this system...
                    on_foo_count_changed( new_value ); /
                });
            });

        }

        //...

    private:

        void update_cycle( const UpdateInfo& info )
        {
            // ...
            // the following is just to clarify:
            const int foo_before = *m_foo_count; // the value is not
updated yet, but we can still use it
            m_work_queue.execute();
            const int foo_after = *m_foo_count; // the value might have
been udpated
            if( foo_before != foo_after )
            {
                // the value have been updated (though you don't need to do
this if you used the callback instead).
            }
            //...
        };
   };

   ////////
   // At some point the value is changed by the system owning the source
object...

   System_A::change_foo( int new_value )
   {
        m_work_queue.push( [=]{ // will be executed on the next update
cycle of this system...
            // ...
            *m_foo_count = new_value; // assign the new value then send the
new value to the readers...
        });
   }

   System_A::something_complex( std::function<void(int&)> some_work )
   {
        m_work_queue.push( [=]{
            // ...
            m_foo_count.update( some_work ); // execute on the current
value then send the resulting value to the readers
        });
   }

Some notes:

 a. The names are just invented while I write this email.
 b. I suppose that the distributed_source might have an interface similar
to synchronized_value,
    anyway I didn't get farther than this idea.
 c. I see several different ways to implement this idea, some might be more
efficient than others but
    I didn't do any tests yet (by lack of time).

Thoughts?



Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net