Archived

This topic is now archived and is closed to further replies.

shared data

This topic is 4975 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

i''m trying to design a number of objects that would take care of problems, which arise when using shared data on multiple threads. and i need help most of these objects will be COM-like. they''ll have reference counting which will in combination with smart pointers take care of automatic cleanup. so, the first object to create is a base which will provide ref counting. since these will be shared data, normal ref counting does not provide everything i want. with the help of a GD community i came up with this: pseudocode
class MTRefCountedObject
{
   public:
       void AddRef    (const ThreadID&);
       void Release   (const ThreadID&);
   protected:
        map<ThreadID, uint>       mdRefMap;
        status_bitset             mdStatus;         
};
so, when using addref and release, we need to provide the "users" thread id. if object is used in nonMT enviroment, only one item is created in the map and the status is having the "notshared" flag. if used by multiple threads, more items are created and the status flag is chaneged to "shared". now, this class provides a base for smart pointer templated class. the next thing is the actuall data wrapper. as i see it, we need accessor functions which work based on the status. if shared flag is set, these functions will use internal locking, if not, "normal" way will be used. pseudocode
template
class SharedData<TYPE>: public MTRefCountedObject
{
    public:
      TYPE  ReadData     ();
            WriteData    (TYPE);
    protected:
    private:
        TYPE         mdData;    // actuall data

};
1) however, one thing is giving me troubles. how should read data be returned? as a temporary? is it logical to assume that "users" have their local copy of that object? what bout writing? 2) one idea is also to make a double buffered SharedData. now, this gives me an idea to make a base class (abstract?) "SharedData", on which SharedDataSimple and SharedDataDoubleBuf would be implemented. this makes any sense? Abnormal behaviour of abnormal brain makes me normal...

Share this post


Link to post
Share on other sites
A couple of things you might find useful:

* The C++ volatile keyword. A must-know when you have shared data.
* Loki''s smart pointer code, which includes thread safety policy classes.

Share this post


Link to post
Share on other sites
heh, u were out for two minutes? that was funny

my ref counter is not thread safe? well, i wrote in front of it that it''s just pseudocode.

and as for the second question: is there any other way for an object to know if it''s being used by a single thread or by multiple? and yes, i know that objects should not care about this, but this one is special

Abnormal behaviour of abnormal brain makes me normal...

Share this post


Link to post
Share on other sites
You need to pessemistically lock access to the map and the static_bit just-in-case it is accessed by more than one thread at a time (you will have a race condition if you don't). So you /always/ must handle it in the multi-threaded manor. So the presence of status_bitset means it cannot be a correct design.

You could tell it at compile time if it lives in a STA or a MTA (single-thread or multi-thread apartment). You could get fancy and make a multi-threaded policy. If you keep the map, you will need a mutex (use a process-local mutex, aka Win32 critical section). It you drop the map, you can use an atomic increment and decrement on an integer - on Win32 these functions are called InterlockedIncrement/Decrement - and then you don't need a mutex.



template<bool MultiThreaded=true>
class RefCountedObject
{
//...

};




Classic reference counting works fairly well in a multi-threaded environment. What are you doing that's special?

[edited by - Magmai Kai Holmlor on April 23, 2004 1:06:41 AM]

Share this post


Link to post
Share on other sites
IMHO the concept of having ownership of some data shared between 2 threads is kinda flawed from start. Personally I think building a system that allows it easily, is promoting bad practice. Maybe there is some isolted case when is the only solution, I just cannot see any right now.

ciao
Alberto

Share this post


Link to post
Share on other sites
first, tnx for the replies.

@mkh

i know that in above example i need a lock to protect data, allways. but why not try to avoid it higher in the object hierarchy?

as for the compile time solution, i dont like, coz the enviroment could change in a dinamic fashion.


@fagiano

i''m not seeing it as ownership, only as an access point.

Abnormal behaviour of abnormal brain makes me normal...

Share this post


Link to post
Share on other sites
ur a true poet

ok, to review:
- need locking on ref counting base
- this gives us status flag indicating enviroment type
- based on this flag, get/set functions for our data can work with or without internal locking, so the unneeded overhead is actually gone, right?


one more thing:
if one piece of data is too popular and is geting a lot of race conditions, would it be wise to make a second copy of, so that "work" would distribute?

Abnormal behaviour of abnormal brain makes me normal...

Share this post


Link to post
Share on other sites
quote:

ur a true poet

ok, to review:
- need locking on ref counting base
- this gives us status flag indicating enviroment type
- based on this flag, get/set functions for our data can work with or without internal locking, so the unneeded overhead is actually gone, right?



If you set the flag in the ctor and it''s constant, then yes. If the flag can change, then this won''t work (race condition).

If you set the flag in the ctor and it''s constant (cannot change in the object''s lifetime), then you can implement it the way you are talking about with only the overhead of a couple of if statements.

If the flag can change, then you must always lock anyways, just in case the flag is changed while you are accessing it. Changing the flag also needs to be locked so you can''t have an inconsistent state! (someone using it in a STA, and someone ready to use it in a MTA)

quote:

one more thing:
if one piece of data is too popular and is geting a lot of race conditions, would it be wise to make a second copy of, so that "work" would distribute?



If it''s read-only access then you can lock, make a local copy, and unlock. If you mutate the data, then you need to hold the lock the whole time. You have to take care that you don''t introduce race-conditions trying to optimize the performance.

Share this post


Link to post
Share on other sites