Is it possible to buffer/delay function calls?

Started by
47 comments, last by ZealousEngine 16 years, 11 months ago
Lets say I have two threads, A and B. If I want thread A to manipulate a object that exists on thread B, I have a couple options.. 1.) I could lock the object from thread A. Of course this is not the best solution, because it will stop thread B in its tracks. 2.) Implement a lockless message que/buffer inbetween the two threads. This way thread A can send requests to thread B, and when thread B receives those requests, it will manipulate the object accordingly. The main problem with this solution is - if the object we are trying to access on thread B is complex (with many methods), we will need to write a equally complex que/buffer in order to get at all the functionality. This could result in a LOT of extra work (potentially a message que that is as complex as the object itself!). Which brings me to my question. Is THIS possible... 3.) From thread A, record a list of all the function calls that you WANT to perform on the object, then send this 'list of commands' (via a message que) to thread B. Once the list arrives on thread B, thread B executes the list of commands. This would solve the main problem with solution number two, in that you no longer have to write a complex message que to simulate all the functionality of the object youre trying to access. You can now directly access a object that exists on thread B, FROM thread A. You just do it in a delayed/buffered fashion. Now of course any method that RETURNS data would have to be sent back via another message que, but thats not a big deal because I plan on writing to this particular object MUCH more than I plan on reading from it. So, to recap. Lets say thread B has a object called myObject, with a method foo(). How can I (from thread A) write something to the effect of... commandList.push_back( myObject.foo() ); Then PASS this list of commands to thread B, where it will be executed. Thanks in advance! *also note, we are assuming 'myObject' is closed source. So I cant just implement the 'buffered' functionality into the object itself. And we are working with C++. [Edited by - ZealousEngine on May 8, 2007 1:49:34 AM]
Advertisement
(assuming C++ because of the push_back call)

Couldn't you just make an array of pointers to member functions and send that? That would solve the issue with the complex interface in 2 too.
You mean send pointers to the member functions BACK to thread A? But then it would not be safe to USE those pointers from thread A (we cant assume thread B isnt reading from part of the object).
Don't try and put the actual method-pointers/functors on a queue, make an enum instead, and stick a switch into thread B.

commandList.push_back(CMD_FUNCTIONA);

and in thread B
switch(*commandListIter)
{
case CMD_FUNCTIONA: break;
case CMD_FUNCTIONB: break;
default: ScreamLoudly(); break;
}

or something like that.
You will of course have to synchronize reading/writing to the command-list, but you'll never get away from that.
Trying to do this with functors is just a lot of unnecessary hassle.
Hmm but making a switch like that could be very complex (if its going to match up 1 to 1 with the functionality of the object).

What im talking about is a way to write a custom block of code, pass it to another thread, then 'execute' it said block of code on that thread.

BTW what is a functor?
Ok so im reading up on functors, it looks like they would work right? I mean I could say (from thread A)...

commandList.add( &myObj, Object::foo(), parms_If_Any );

Pass the commandList to thread B, where (if im understanding functors correctly), it can call foo() on object myObj and pass in the parms I specified (if any).

Eh?
Quote:Original post by tok_junior
Don't try and put the actual method-pointers/functors on a queue, make an enum instead, and stick a switch into thread B.

commandList.push_back(CMD_FUNCTIONA);

and in thread B
switch(*commandListIter)
{
case CMD_FUNCTIONA: break;
case CMD_FUNCTIONB: break;
default: ScreamLoudly(); break;
}

or something like that.
You will of course have to synchronize reading/writing to the command-list, but you'll never get away from that.
Trying to do this with functors is just a lot of unnecessary hassle.


Hmm Writing a big switch statement is just a lot of unnecessary hassle (not to mention brittle). Just use a container of boost::function<void()>s, and use boost::bind to create the functors you want to execute later, if you even need to do that.

For example:
commandList.push_back( boost::bind( &MyObjectType::foo, &myObject ) );

commandList is of type:

std::vector< boost::function<void()> >

Or similar.

The Boost C++ Libraries

Also, a library like (boost) Asio can help handle syncronization issues. It's not core [boost], and still under development, but it works, and accomplishes what it's meant to.
Good so it sounds like this is pretty simple/common problem? I have ZERO experience with functors, but I LOVE boost. So boost::bind is in essence a boost version of a functor? Can I pass parms with bind? Ill have to do some research on it...

And I have synchronization under control (that part is super easy imo).
Quote:Original post by ZealousEngine
Good so it sounds like this is pretty simple/common problem?


The more generalized problem -- storing function calls/actions to be performed later -- is fairly common. This has all sorts of possible applications -- from undo systems (by storing the acts needed to undo a given transformation), to GUI systems (by providing the OO equivilant of callbacks -- that is, the ability to store a function to be called when, say, a button is clicked).

Quote:I have ZERO experience with functors, but I LOVE boost. So boost::bind is in essence a boost version of a functor?


Almost. It is a function which returns a functor -- a functor "factory" if you will. It makes it easy as 1-2-3 to make functors without the tedium of writing all the boilerplate yourself (which is still pretty minimal).

boost::function on the other hand is a functor which can store other functors of varying types very easily (which is handy when you want to store different functors to represent different calls/actions, as is usually the case).

Quote:Can I pass parms with bind?


Yes.

Quote:And I have synchronization under control (that part is super easy imo).


Glad to hear :-).
Awesome! Thanks for all the good news. Im gonna do some research on boost bind, and maybe start converting my project today!

Up to this point, whenever I wanted to access some part of a object that existed in another thread, I had to write a CUSTOM message que (what a pain!). Now I can 'access' these same objects from ANYWHERE by passing a list of access requests to the objects 'home thread'. Once on the home thread, the access request list will be executed only when it is safe to do so.

HOORAY FOR FUNCTORS!

This topic is closed to new replies.

Advertisement