Sign in to follow this  
ZealousEngine

Is it possible to buffer/delay function calls?

Recommended Posts

ZealousEngine    100
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]

Share this post


Link to post
Share on other sites
CTar    1134
(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.

Share this post


Link to post
Share on other sites
ZealousEngine    100
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).

Share this post


Link to post
Share on other sites
tok_junior    229
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.

Share this post


Link to post
Share on other sites
ZealousEngine    100
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?

Share this post


Link to post
Share on other sites
ZealousEngine    100
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?

Share this post


Link to post
Share on other sites
MaulingMonkey    1728
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.

Share this post


Link to post
Share on other sites
ZealousEngine    100
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).

Share this post


Link to post
Share on other sites
MaulingMonkey    1728
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 :-).

Share this post


Link to post
Share on other sites
ZealousEngine    100
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!

Share this post


Link to post
Share on other sites
ZealousEngine    100
Quick followup question - There arent any real 'negatives' to functors/boost::bind are there? I mean, I know I can expect to see a LITTLE extra overhead, but should I be concerned about anything inparticular?

I plan on using these functors mainly to indirectly modify my render engines 'scene state'. For example, in my main/logic thread, I will be doing a lot of 'commandList.push_back( &SceneMgrType::setPosition, &mySceneMgr, objectId, Position )', then passing that commandList to my render thread, where the scene state (object positions, ect...) will be modified (when its safe).

That should be ok, right?

Share this post


Link to post
Share on other sites
ZMaster    240
You may experience longer compile / linker times, because boost massively relies on metaprogramming / templates, especially the boost function implementation.
Depending on how extensively you are using boost and what the size of your team / project is, this possible drawback might be of great or no importance for you.

Share this post


Link to post
Share on other sites
Julian90    736
Quote:
Quick followup question - There arent any real 'negatives' to functors/boost::bind are there? I mean, I know I can expect to see a LITTLE extra overhead, but should I be concerned about anything inparticular?

I plan on using these functors mainly to indirectly modify my render engines 'scene state'. For example, in my main/logic thread, I will be doing a lot of 'commandList.push_back( &SceneMgrType::setPosition, &mySceneMgr, objectId, Position )', then passing that commandList to my render thread, where the scene state (object positions, ect...) will be modified (when its safe).

That should be ok, right?


Theres a page in the documentation for boost.function that discuses the performance, to sum it up in the worst case you'll end up with the equivelent of a single virtual function call.

One thing to watch out for with boost.bind is passing values by reference or binding values that are expensive to copy, boost.bind takes its arguments by value so if you bind to a function that takes arguments by reference then a reference to the copy made by boost.bind will be passed rather then a reference to the origonal object, to actualy pass by reference/const reference you need to wrap the argument with boost.ref/boost.cref. Because of this passing something thats expensive to copy without wrapping it might result in performance issues due to the extra copies that are made.

Share this post


Link to post
Share on other sites
MaulingMonkey    1728
Quote:
Original post by ZealousEngine
Quick followup question - There arent any real 'negatives' to functors/boost::bind are there? I mean, I know I can expect to see a LITTLE extra overhead, but should I be concerned about anything inparticular?


The three costs I can call to mind are:
1) Possible extra indirection hit (minor cost, boost::function only)
2) Possible extra virtual call hit (minor cost, boost::function only)
3) Minor compile time hit

You can guard yourself against all of these by providing yourself with the option of replacing them with your own, specialized equivilants if seriously needed, which is as simple as wrapping up the various push_back()s in helper functions if you find yourself scattering them all over the place. Doing it per frame for individual vertex data would be problematic, doing it once per frame per object in a modern RTS probably wouldn't, and you're definately in the clear if you're dealing with batched updates (e.g. setPositions).

Share this post


Link to post
Share on other sites
ZealousEngine    100
Ok cool.

Well I got 'non return' functions to work, but now im wondering how to get the return value.

For example, if 'foo()' returns a int, what am I doing wrong here...

std::vector< boost::function<void()> > cmdList;
cmdList.push_back( boost::bind( &ObjType::foo, &myObj ) );

//why doesnt this work?
int val = cmdList[0]();

Share this post


Link to post
Share on other sites
MaulingMonkey    1728
std::vector< boost::function<int()> > cmdList;
cmdList.push_back( boost::bind( &ObjType::foo, &myObj ) );

//why doesnt this work? works now.
int val = cmdList[0]();


Similarly, if you want to add parameters which you specify when you call the cmdList, you can use a combination of signature changes and placeholders:

std::vector< boost::function<int(int)> > cmdList;
cmdList.push_back( boost::bind( &ObjType::foo, &myObj , 12 , _1 ) ); //assuming foo takes two int parameters

int val = cmdList[0](24); //calls myObj.foo(12,24); -- the 24 is placed where the _1 was in the bind() statement.


Note that the function signature: int(int) -- matches how we're calling it here (passing 1 int and returning another).

Share this post


Link to post
Share on other sites
ZealousEngine    100
Hmm so I have to hard code the return type into the command list? Dang that means I can only have one 'type' of function PER command list right?

Is there no way to have a command list that can call foo() (which returns a int), AND goo() ( which returns void )?

Share this post


Link to post
Share on other sites
Julian90    736
Quote:
Hmm so I have to hard code the return type into the command list? Dang that means I can only have one 'type' of function PER command list right?

Is there no way to have a command list that can call foo() (which returns a int), AND goo() ( which returns void )?


It depends, if you want acess to the return types you could try boost.any or boost.variant but once you store them in the list you have no way of knowing what the function will return so I would try to avoid it. Why exactly do you want to get the return value and be able to store functions with different unknown return value in one list, theres probally a better alternative.

(okay I admit you can find out the return type with boost::any or use visitors with boost::variant but I'd still say its a bad idea).

Share this post


Link to post
Share on other sites
ZealousEngine    100
Well I guess youre right, it would be messy.

Im just thinking it would be nice if, when adding a function call to the command list, I could also speicfy a pointer back to where I want the return value stored. So, later, when the function is called, it automatically writes the return value to wherever I specified. I guess thats a pretty specific request, so I wouldnt be surprised if its not built into bind.

I guess if thats not possible, I can just create a command list for 'void returns', another command list for 'int returns', ect...

*About the command lists - Is it ok to use a WIDE range of functions in a single command list? Like for my 'void return' command list, is it ok (perofrmance wise) to have functions that set the cameras position, along side functions that hide/show objects, ect... They are all the same in essence (the dont return anything) but they all take a varrying amount of arguments.

Share this post


Link to post
Share on other sites
Julian90    736
Quote:
*About the command lists - Is it ok to use a WIDE range of functions in a single command list? Like for my 'void return' command list, is it ok (perofrmance wise) to have functions that set the cameras position, along side functions that hide/show objects, ect... They are all the same in essence (the dont return anything) but they all take a varrying amount of arguments.


There shouldnt be any performance problems as far as I know, the only thing you really need to wory about is that you have enough space to store all of the arguments to the functions untill there called and removed from the command list.

Quote:
Im just thinking it would be nice if, when adding a function call to the command list, I could also speicfy a pointer back to where I want the return value stored. So, later, when the function is called, it automatically writes the return value to wherever I specified. I guess thats a pretty specific request, so I wouldnt be surprised if its not built into bind.

I guess if thats not possible, I can just create a command list for 'void returns', another command list for 'int returns', ect...


Now that is alot easier to do :).


int main()
{
// boost.lambda lets you compose functions on the fly
// just wrap all variables you want to use with boost::lambda::var
// and boost.lambda overloads all the operators to let you make the
// functions
using namespace boost::lambda;

std::vector< boost::function<void ()> > cmd_list;
my_class result;
// my_class my_function(int);
// creates a function that calls my_funtion(1) and stores the result in result
cmd_list.push_back(var(result) = boost::bind(my_function, 1));
// ...
cmd_list[0]();
// result now holds the result of my_function

// more complicated example of what boost.lambda can do
// creates and stores a functor that will print the value of result
// then call result.do_stuff then print the new value.
cmd_list.push_back(
(
std::cout << constant("Value of result before calling result.do_stuff(): ") << var(result),
boost::bind(my_class::do_stuff, &result),
std::cout << constant("Value after calling result.do_stuff()") << var(result)
));

// does all the std::cout'ing etc above
cmd_list[1]();
}







You might want to profile and potentially right a wrapper functor if your doing that lots thought because boost.lambda can result in ~10% speed hit over a hand written functor. (although I think that applies mainly to more complex stuff)

Edit:
boost.lambda
boost.lambda performance tests

[Edited by - Julian90 on May 8, 2007 7:30:30 AM]

Share this post


Link to post
Share on other sites
ZealousEngine    100
Whoa!

std::vector< boost::function<void ()> > cmd_list;
my_class result;
// my_class my_function(int);
cmd_list.push_back(&var(result) = boost::bind(my_function, 1));
// ...
cmd_list[0]();

looks like exactly what I need! So even though our cmd list was delcared using void(), we CAN get return values (IF they are automatically assigned to the address we specify)? Is that correct? If so, that works out great, because I WANT the return values to go straight to thier respective address.

Im confused about why youre using the lambda namespace though.. everything about the above snippet looks pretty standard. I hear lambda is pretty slow, where exactly are you using it in the above snippet?

*Ok heres another question (assuming I understand the above code snippet). Lets say on thread A, I push a command request to creats a new 'game object' (3d model). The command list is sent to thread B, where it creates the game object, and 'sends' the pointer back to thread A (because we specified the return value to go to some variable that exists back on thread A).

Now, rather than passing a message back from thread B, telling thread A it is now safe to use the pointer, couldnt thread A just 'watch' the pointer, and as soon as it turns != 0, we know thread B has 'returned' the pointer?

Or do you think I still need to pass a traditional 'message' telling thread A when its safe to use the pointer?

Share this post


Link to post
Share on other sites
Julian90    736
Quote:
looks like exactly what I need! So even though our cmd list was delcared using void(), we CAN get return values (IF they are automatically assigned to the address we specify)? Is that correct? If so, that works out great, because I WANT the return values to go straight to thier respective address.


What happens is in var(result) = stuff var(result) evaluates to a object with an overloaded operator= which is called and the operator= returns a funtor equivelent to
void assign_result() { result = stuff() }
So whats happening is that we create a new function which assigns stuff() to resuls

Quote:
Im confused about why youre using the lambda namespace though.. everything about the above snippet looks pretty standard. I hear lambda is pretty slow, where exactly are you using it in the above snippet?


See the link at the end of the previous post on the performance of boost.lambda for simple things like this it tends to be as fast as the equivelent hand written thing, its only for more complex things that it loses out. (and thats only because the compiler gives up optomizing it due to how long it takes to compile, in theory it should be just as fast for all expressions)

In the snippet the var(result) = ... is from boost.lambda (i.e. it uses boost::lambda::var and boost::lambda::var::operator=)

Quote:
Now, rather than passing a message back from thread B, telling thread A it is now safe to use the pointer, couldnt thread A just 'watch' the pointer, and as soon as it turns != 0, we know thread B has 'returned' the pointer?


Yes that can be done but you need to modify it a bit (there was an erorr in my example)


int main()
{
using namespace boost::lambda;

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

my_class my_obj;
my_class* result = NULL;

cmd_list.push_back(
// create a function that
(
// my_obj = my_obj.my_function(5)
var(my_obj) = boost::bind(my_class::my_function, &my_obj, 5),
// result = &my_obj
var(result) = &my_obj
));

// ... send cmd_list to thread B ...

// wait for thread B to call the function
while (result == NULL)
{ }

// now we can keep going
}



Alternativly we can dynamically allocate the result


int main()
{
using namespace boost::lambda;

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

my_class* result = NULL;

cmd_list.push_back(
// create a function that
(
// result = new my_class(my_obj.my_function(5));
var(result) = boost::bind(new_ptr<my_class>(), boost::bind(my_class::my_function, &my_obj, 5))
));

// ... send cmd_list to thread B ...

// wait for thread B to call the function
while (result == NULL)
{ }

// now we can keep going
}

Share this post


Link to post
Share on other sites
ZealousEngine    100
Wow thanks again for helping me out here, this is great stuff.

So it IS safe to 'wait and watch' for a pointer to be set from across threads, and as soon as you see its '!=0', you can safely use it? Good to know.

And is it ok to put these 'lambdas' that return parms, in the SAME 'command list' with the other 'non lambda' functions? I mean I wont see any performance gain if I split them up will I?

Share this post


Link to post
Share on other sites
Julian90    736
Quote:
Wow thanks again for helping me out here, this is great stuff.

So it IS safe to 'wait and watch' for a pointer to be set from across threads, and as soon as you see its '!=0', you can safely use it? Good to know.

And is it ok to put these 'lambdas' that return parms, in the SAME 'command list' with the other 'non lambda' functions? I mean I wont see any performance gain if I split them up will I?


Yes it is safe in this case you have to be careful though because the location of the sequence points changes depending on if the operators being called are userdefined overloads or not (you need to be sure there is a sequence point before the operator= assigning it to the pointer)

Putting them in one list is fine, they are exactly the same as if you rewrote every function to take a extra paramater that was the "return adress" and store the result there instead of returning it normaly.

Share this post


Link to post
Share on other sites
ZealousEngine    100
Zuh? I dont quite understand what you mean by "sequence points". Can you give a example of when its NOT safe to do the little 'while ( result == 0 ) {}'?

I was just always afraid that there could be a situation where, RIGHT as thread B is assigning a value to the pointer, thread A is checking to see if it has a value. If that happens at the same split second, isnt it possible for thread A to get a 'partial'/garbage read of the pointer? Or is such a pointer/variable assignment consider one of those 'atomic' operations?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this