[Threads] Conditions and avoiding lengthy mutex locks (background loading for a game)
Hello,
I'm trying to use the Boost Threads library to help me implement a
background loading system for a game I'm writing.
I want to enqueue load requests while the main game thread is running, and
have a separate thread load the data and pass back the appropriate objects.
Currently when I enqueue a new request, I notify a 'something to do'
condition, which my loading thread waits on. The trouble is, when I notify
it, the loader could be loading in the background, and has a lock on the
mutex connected with the condition.
Would anyone be able to offer me any advice on how to solve this?
Many thanks,
Peter
P.S. Please find attached my current implementation.
begin 666 loader.cpp
M(VEN8VQU9&4@(FQO861E
You usually don't hold the mutex guarding the state indicating variables for a long time. You usually hold it just enough to update the variables to indicate the new state you have transitioned to. That is you hold it while you pick up the request from the queue and transfer it to local variables in the thread (or somehow mark it in the queue) as being processed, then you release the lock and go off and handle request. After you are done processing you grab the lock again and update state or lock for new requests and then repeat. If there are no requests then you block on condition while holding the mutex and the blocking wait releases and reacquires the lock for you when the condition is signaled. You will be holding the mutex lock for very little time unless popping/marking queue elements is quite expensive. Also, it often a little more efficient to signal the condition variable while not holding the mutex. It can avoid needless context ping ponging. On Apr 27, 2006, at 3:49 PM, Peter Mackay wrote:
Hello,
I'm trying to use the Boost Threads library to help me implement a background loading system for a game I'm writing.
I want to enqueue load requests while the main game thread is running, and have a separate thread load the data and pass back the appropriate objects.
Currently when I enqueue a new request, I notify a 'something to do' condition, which my loading thread waits on. The trouble is, when I notify it, the loader could be loading in the background, and has a lock on the mutex connected with the condition. Would anyone be able to offer me any advice on how to solve this?
Many thanks, Peter
P.S. Please find attached my current implementation.
begin 666 loader.cpp M(VEN8VQU9&4@(FQO861E
PT*"2\O(%-I9VYA;"!T:&4@=&AR M96%D('1O(&5X:70N#0H)>PT*"0EB;V]S=#HZ;75T97@Z.G-C;W!E9%]L;V-K M(&QO8VLH;75T97@I.PT*"0ET:')E861? PT*"0DO+R!786ET('5N=&EL('=E(&=E="!S;VUE=&AI;F<@ M=&\@9&\N#0H)"7-O;65T:&EN9U]T;U]D;RYW86ET*&QO8VLI.PT*#0H)"2\O M(%-H;W5L9"!W92!E>&ET/PT*"0EI9B H=&AR96%D7W-H;W5L9%]E>&ET*0T* M"0E[#0H)"0DO+R!%>&ET('1H92!T:')E860N#0H)"0ER971U begin 666 loader.hpp M(VEF;F1E9B!,3T%$15)?2%!0#0HC9&5F:6YE($Q/041%4E](4% -"@T*(VEN M8VQU9&4@/'%U975E/@T*(VEN8VQU9&4@/&)O;W-T+W-H87)E9%]P='(N:'!P M/@T*(VEN8VQU9&4@/&)O;W-T+W1H
"YH<' ^#0HC:6YC;'5D92 \8F]O M &ET M.PT*"7-T9#HZ<75E=64\:6YT/@D)<&5N9&EN9U]L;V%D 2X-"@EB;V]S=#HZ _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
Hello, Thomas, Thank you very much for your detailed reply.
You usually don't hold the mutex guarding the state indicating variables for a long time. You usually hold it just enough to update the variables to indicate the new state you have transitioned to.
Ah, so even though I pass the mutex into the condition when waiting, I don't have to leave the mutex locked while handling the request.
That is you hold it while you pick up the request from the queue and transfer it to local variables in the thread (or somehow mark it in the queue) as being processed, then you release the lock and go off and handle request.
That sounds exactly like what I want to do. Thanks.
After you are done processing you grab the lock again and update state or lock for new requests and then repeat. If there are no requests then you block on condition while holding the mutex and the blocking wait releases and reacquires the lock for you when the condition is signaled. You will be holding the mutex lock for very little time unless popping/marking queue elements is quite expensive.
I'm planning on just copying the requests to a local variable in the thread function, so this should be okay.
Also, it often a little more efficient to signal the condition variable while not holding the mutex. It can avoid needless context ping ponging.
Ah, I didn't realise I could do that. Thanks again, you've really helped me out. Peter
Very rough psuedo code might look like this code: while ( true ) { Request myReq; { scopedLock lock( myMutex ); while ( myQueue.empty() ) myCond.wait( myMutex ); myReq = myQueue.pop(); } if ( myReq.isQuitReq() ) break; myReq.process(); } Note this doesn't handle things like posting results/errors back to the queue clients and lots and lots of other things but it's just to get a rough idea. Notice that the mutex is only locked long enough to check the queue and pop elements. The myCond.wait call will release the mutex and then reacquire it for you when the cond is signalled. On Apr 28, 2006, at 2:22 AM, Peter Mackay wrote:
Hello,
Thomas, Thank you very much for your detailed reply.
You usually don't hold the mutex guarding the state indicating variables for a long time. You usually hold it just enough to update the variables to indicate the new state you have transitioned to.
Ah, so even though I pass the mutex into the condition when waiting, I don't have to leave the mutex locked while handling the request.
That is you hold it while you pick up the request from the queue and transfer it to local variables in the thread (or somehow mark it in the queue) as being processed, then you release the lock and go off and handle request.
That sounds exactly like what I want to do. Thanks.
After you are done processing you grab the lock again and update state or lock for new requests and then repeat. If there are no requests then you block on condition while holding the mutex and the blocking wait releases and reacquires the lock for you when the condition is signaled. You will be holding the mutex lock for very little time unless popping/marking queue elements is quite expensive.
I'm planning on just copying the requests to a local variable in the thread function, so this should be okay.
Also, it often a little more efficient to signal the condition variable while not holding the mutex. It can avoid needless context ping ponging.
Ah, I didn't realise I could do that.
Thanks again, you've really helped me out.
Peter
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
Thanks again, that pseudo-code is very much how my code is now, and it seems
to work like a charm.
Peter
"Thomas Costa"
Very rough psuedo code might look like this code:
while ( true ) { Request myReq;
{ scopedLock lock( myMutex );
while ( myQueue.empty() ) myCond.wait( myMutex );
myReq = myQueue.pop(); }
if ( myReq.isQuitReq() ) break;
myReq.process(); }
Note this doesn't handle things like posting results/errors back to the queue clients and lots and lots of other things but it's just to get a rough idea. Notice that the mutex is only locked long enough to check the queue and pop elements. The myCond.wait call will release the mutex and then reacquire it for you when the cond is signalled.
On Apr 28, 2006, at 2:22 AM, Peter Mackay wrote:
Hello,
Thomas, Thank you very much for your detailed reply.
You usually don't hold the mutex guarding the state indicating variables for a long time. You usually hold it just enough to update the variables to indicate the new state you have transitioned to.
Ah, so even though I pass the mutex into the condition when waiting, I don't have to leave the mutex locked while handling the request.
That is you hold it while you pick up the request from the queue and transfer it to local variables in the thread (or somehow mark it in the queue) as being processed, then you release the lock and go off and handle request.
That sounds exactly like what I want to do. Thanks.
After you are done processing you grab the lock again and update state or lock for new requests and then repeat. If there are no requests then you block on condition while holding the mutex and the blocking wait releases and reacquires the lock for you when the condition is signaled. You will be holding the mutex lock for very little time unless popping/marking queue elements is quite expensive.
I'm planning on just copying the requests to a local variable in the thread function, so this should be okay.
Also, it often a little more efficient to signal the condition variable while not holding the mutex. It can avoid needless context ping ponging.
Ah, I didn't realise I could do that.
Thanks again, you've really helped me out.
Peter
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
This is a reply to an April 2006 thread with two questions: 1) Why does the myCond.wait( myMutex ); call happen inside a while loop rather than in an if statement? I would have expected that since one is waiting for the queue to get something put in it that the condition would only get set if something was placed in the queue. if one returned from the wait and found the queue empty I'd tend to want to log that as an internal error. 2) Suppose one wants to wait on a queue with a timer so that one does not wait forever. For example, suppose one wants to wait for a maximum of 1 second for something to get placed on the queue. If one is using a timed_mutex then does the same timer get used on the condition wait as on the mutex lock wait? The first question is more a curiosity. The second question is my real problem: How to wait on a queue which might or might not be empty when I go to read it and where, if it is empty, I want to eventually timeout on the wait. Does a condition "inherit" the timer for a timed_mutex? Thomas Costa wrote:
Very rough psuedo code might look like this code:
while ( true ) { Request myReq;
{ scopedLock lock( myMutex );
while ( myQueue.empty() ) myCond.wait( myMutex );
myReq = myQueue.pop(); }
if ( myReq.isQuitReq() ) break;
myReq.process(); }
Note this doesn't handle things like posting results/errors back to the queue clients and lots and lots of other things but it's just to get a rough idea. Notice that the mutex is only locked long enough to check the queue and pop elements. The myCond.wait call will release the mutex and then reacquire it for you when the cond is signalled.
On Apr 28, 2006, at 2:22 AM, Peter Mackay wrote:
Hello,
Thomas, Thank you very much for your detailed reply.
You usually don't hold the mutex guarding the state indicating variables for a long time. You usually hold it just enough to update the variables to indicate the new state you have transitioned to. Ah, so even though I pass the mutex into the condition when waiting, I don't have to leave the mutex locked while handling the request.
That is you hold it while you pick up the request from the queue and transfer it to local variables in the thread (or somehow mark it in the queue) as being processed, then you release the lock and go off and handle request. That sounds exactly like what I want to do. Thanks.
After you are done processing you grab the lock again and update state or lock for new requests and then repeat. If there are no requests then you block on condition while holding the mutex and the blocking wait releases and reacquires the lock for you when the condition is signaled. You will be holding the mutex lock for very little time unless popping/marking queue elements is quite expensive. I'm planning on just copying the requests to a local variable in the thread function, so this should be okay.
Also, it often a little more efficient to signal the condition variable while not holding the mutex. It can avoid needless context ping ponging. Ah, I didn't realise I could do that.
Thanks again, you've really helped me out.
Peter
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
This is due to spurious wakeups (waking up from the condition when it hasn't actually been signalled). To be safe I think you have to always check the condition once you wake up from the condition => hence the while loops -----Original Message----- From: boost-users-bounces@lists.boost.org on behalf of Randall Parker Sent: Sun 3/11/2007 10:44 AM To: boost-users@lists.boost.org Subject: Re: [Boost-users] [Threads] Conditions and avoiding lengthy mutexlocks (background loading for a game) This is a reply to an April 2006 thread with two questions: 1) Why does the myCond.wait( myMutex ); call happen inside a while loop rather than in an if statement? I would have expected that since one is waiting for the queue to get something put in it that the condition would only get set if something was placed in the queue. if one returned from the wait and found the queue empty I'd tend to want to log that as an internal error.
participants (4)
-
Peter Mackay
-
Randall Parker
-
Sohail Somani
-
Thomas Costa