Is it possible to buffer/delay function calls?

Started by
47 comments, last by ZealousEngine 16 years, 11 months ago
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?
Advertisement
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}
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?
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.
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?
A pointer assignment or comparison will *probably* be atomic on *most* platforms; however, C++ makes no such guarantee, as it doesn't concern itself with multithreading issues at all. If you really want to be safe, use a "real" synchronization primitive.
Quote: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?


It is probally atomic, however, if there isnt a sequence point at the assignment (more on that below) then what can happen is the pointer is assignmed to before the memory it points to is initialized.

Sequence Points

The problem:
int* bad = NULL;// assuming operator new hasnt been overloaded// there is no sequence point at the = and so bad might be set// to the address of the int that was created before the value of the// int is set to 5.bad = new int(5);int* create_int(int i) { return new int(i); }int* good = NULL;// there is a sequence point at the call to create_int which means// that the returned pointer is garunteed to already have the 5 assigned to it// and so it will be valid when the pointer is assigned to good.good = create_int(5);
Hmm.. still a tad confused. I swear ive done...

int* bad = NULL;
bad = new int(5);

..before. I dont remember having problems.

Is this why you broke the earlier command into two parts?

// 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



Quote:Hmm.. still a tad confused. I swear ive done...

int* bad = NULL;
bad = new int(5);


Yes the problem is when you do
int* bad = NULL;// Thread Awhile (bad == NULL){ }int i = *bad;// Thread Bbad = new int(5);


Quote:Is this why you broke the earlier command into two parts?


No, i broke it in two parts because im assigning to two different variables, I join it back into one part in the second version.
Ok I think I see, the danger is when youre working across threads, because thread A might THINK the pointer is ready to go a split second before its actually ready.

Im haven a little trouble with your earlier code. Forget pointers, lets say I wanted to just return a integer value, why doesnt the following work?...

ObjectType myObj; //ObjectType has one method foo which returns a int
int result;

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

cmdList.push_back((

boost::lambda::var(result) = boost::bind(new_ptr<ObjectType>(), boost::bind( ObjectType::foo, &myobj ) )

)};

*btw where did 'new_ptr' come from?

[Edited by - ZealousEngine on May 8, 2007 10:37:04 AM]

This topic is closed to new replies.

Advertisement