Skip to main content
gamedev.net
🔒 Locked

double buffer

Started by vesoljc Jul 4, 2004 at 10:51 AM 6 replies 1.2k views
Original Post
vesoljc
vesoljc
how would a double buffer work in a multi process enviroment? ie: a piece of data would have two copies, one for reading one for writing, so that two operations would not interfere. since one piece of data is read-only, multiple processes can use it at once. when a write happens, the two pieces are swapped. thing that bothers me is how to implement get/set functions for it. passing data around is not really smart, so it should be done with pointers or references, right? let's start with this:

template
class DoubleBuffer<class TYPE>
{
private:
         TYPE      mdOne;
         TYPE      mdTwo;
};

// and use this for thread safety:
template
class ScopedLock<class TYPE>
{};
Abnormal behaviour of abnormal brain makes me normal...www.zootfly.com
felisandria
felisandria
Pointers are the usual method of passing around double buffers, because otherwise they're not really double buffers, just copied pieces of data.

I suppose the thing to point out to help you understand it is "what does read only really mean?" It's not like it's being stuck into off-limits memory, the fact that it's "read only" is due merely to a construct, generally a flag or enum noting state, which instructs the objects messing with it to view it as off-limits. So, basically your flags need to be smart. You can use mutexing or thread locks if you'd like, for instance, which are basically just flags with a little extra baggage, for these purposes at least. Also, "what does flipping really mean?" Really, the memory hasn't moved, the buffers only "flip" in the sense that the reader(s) (or writer(s), depending on which direction you're looking from) have flipped from being concerned with one buffer to being concerned with another.

So, basically your objects need to know what they're currently connected to, your data has to know what state it's in to know when it's allowed to flip, and your logic has to be smart enough to know when it wants to flip, and when it can flip, so as not to have your reader flipped to something the writer's only half done with.

Does that make sense?

-fel

~ The opinions stated by this individual are the opinions of this individual and not the opinions of her company, any organization she might be part of, her parrot, or anyone else. ~
vesoljc
vesoljc
some of it does ;)

so, objects basically have a virtual connection to a piece of data via a pointer (smart?). when the flip happens, the pointers are internally swaped. but a swap can happen only when the pointers are not in use.

an object/user also has to provide a sort of an indicator, which shows our doublebuffer what operation is under way?

templateclass DoubleBuffer<class TYPE>{ public:      DoubleBuffer(const TYPE& lrData)      {          mdOne = mdTwo = lrData;          mpReader = &mdOne          mpWritet = &mdTwo      } private:      TYPE     mdOne;      TYPE     mdTwo;      TYPE*    mpReader;      TYPE*    mpWriter;      bool     mdReading;      bool     mdWriting;      bool     mdWaiting;  };


mdReading and mdWriting are operation indicators, while mdWaiting is an indicator showing, that write has happend and the swap needs to happen. none new read operations must not start until then.

on the right track? ;)


ps: in know that many GD users say that reinventing the wheel is waste of time and in this case i urge them to show me an example of this wheel :)

Abnormal behaviour of abnormal brain makes me normal...www.zootfly.com
DrPizza
DrPizza
I wonder if a slightly fancy read/write lock would work well for what you want (a read/write lock allows multiple concurrent readers but only a single writer). On acquiring either kind of lock you could return the pointer to the relevant buffer, and on releasing the write lock you could perform the flip.

Implementations of read/write locks can be found around the Internet, and some platforms (e.g. .NET, Solaris) provide them as built-in synchronization objects.
char a[99999],*p=a;int main(int c,char**V){char*v=c>0?1[V]:(char*)V;if(c>=0)for(;*v&&93!=*v;){62==*v&&++p||60==*v&&--p||43==*v&&++*p||45==*v&&--*p||44==*v&&(*p=getchar())||46==*v&&putchar(*p)||91==*v&&(*p&&main(0,(char**)(--v+2))||(v=(char*)main(-1,(char**)++v)-1));++v;}else for(c=1;c;c+=(91==*v)-(93==*v),++v);return(int)v;} /*** drpizza@battleaxe.net ***/
felisandria
felisandria
Smart pointers are usually used to ensure that memory allocation is done properly. As such, it's not really necessary if you only intend to have 2 pointers (one per buffer) basically forever.

Extremely simple double buffering would look something like this, with the buffers being named A and B:

1. (A) m_isWriting flag set to true
2. (A) written to until done
3. (A) m_isWriting flag turned off... at this point either the stuff that wants to read has been polling for the flag to go off, or an indicator is forwarded saying "you can read now"
4. (B) m_isWriting flag is set to true and 1 through 3 start for it while the following is happening
5. (A) m_isReading flag set to true
6. (A) is read until it's done
7. (A) m_isReading flag is turned off, and again either the writer is polling or an indicator is forwarding allowing another write.
8. (B) starts the read cycle 5 through 7, (A) returns to 1 through 3.

If you need to have multiple things reading at once, then instead of a read flag, just use a counter, and don't allow recycling until the number of things counted as reading is back to 0.

-fel
~ The opinions stated by this individual are the opinions of this individual and not the opinions of her company, any organization she might be part of, her parrot, or anyone else. ~
vesoljc
vesoljc
tnx!

one more q:

when a write has happend and a swap needs to be done, readers have to wait. must this time period be a pooling loop, or can we put reader threads into blocked state?

[Edited by - vesoljc on July 5, 2004 5:40:21 PM]
Abnormal behaviour of abnormal brain makes me normal...www.zootfly.com
DrPizza
DrPizza
It's still not clear why you can't use a slightly modified reader/writer lock as I described before.
char a[99999],*p=a;int main(int c,char**V){char*v=c>0?1[V]:(char*)V;if(c>=0)for(;*v&&93!=*v;){62==*v&&++p||60==*v&&--p||43==*v&&++*p||45==*v&&--*p||44==*v&&(*p=getchar())||46==*v&&putchar(*p)||91==*v&&(*p&&main(0,(char**)(--v+2))||(v=(char*)main(-1,(char**)++v)-1));++v;}else for(c=1;c;c+=(91==*v)-(93==*v),++v);return(int)v;} /*** drpizza@battleaxe.net ***/
snk_kid
snk_kid
How about adopting a combined approach, using simillar concepts used in java's nio.Buffer type where you have a state invariant such as: 0 <= mark <= position <= limit <= capacity but implementated simillar to std::basic_streambuf<> where you provide iterators types for more controlled acccess.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.