Skip to main content
GameDev.net gamedev.net
🔒 Locked

Making non thread safe code asynchronous

Started by Mephs May 5, 2009 at 2:18 PM 2 replies 1.6k views
Original Post
Mephs
Mephs
Hey all, I'm trying to stop my game code from chugging at the moment by making as much of the time consuming code asynchronous and thread safe, as possible. I have managed to get the vast majority of this accomplished, but I'm struggling a little with some code which I am unable to make thread safe because it is using a DirectX device and is very low level code within the engine (non multithreading aware D3D Device for performance reasons). The code in question was not written by me and is very low level which makes it even harder to refactor. I'm wondering if it would be safe to move the non threadsafe code into its own thread and halt execution of the main thread whilst the job in question is processing. The idea would be to then code an object which, when instantiated, will periodically stop the current thread from processing and allow the main thread to continue. When the main thread has sufficient processing time remaining in a cycle, the main thread will again be locked and the thread the job belongs to will be allowed to continue. Repeat this ad infinatum until the job has completed. All that would then be required is that the code instantiate these objects to give the main thread a breather every now and again and the job is effectively spread over several cycles whilst hopefully still remaining thread safe. Even though the code is itself not threadsafe, the main thread is always locked whilst the code is executing and nothing would use the code in any other thread. I always get my head caught up in thread safety stuff, so firstly I'm wondering if my scheme sounds like it would be threadsafe despite the fact the code is not running in the main thread and is not inherently thread safe. Secondly, I wonder if anyone might have a better suggestion as to how to cope with spreading code that takes a while to execute over several frames without having to drastically refactor it? The code in question is making lots of calls which are not very expensive on their own, but add up over a period of time. Roughly 6,000 calls to about 5 different functions is taking about 0.05 seconds which is the cause of my chug as far as I can tell. Any advice would be very much appreciated :) I can provide extra info if needed. Many thanks, Steve O
Antheus
Antheus
To make something thread-safe, you need to make sure shared state remains consistent.

So the first step is to determine what this shared state is.

Next, you determine whether you can have multiple threads operating on it, and how to make sure they don't stomp on each other.

Quote:
I'm wondering if it would be safe to move the non threadsafe code into its own thread and halt execution of the main thread whilst the job in question is processing.

If main thread needs to be halted, then there is no reason to have multiple threads. One cannot progress if other isn't halted. It just adds complication of synchronization for no benefit.

Quote:
the main thread is always locked whilst the code is executing and nothing would use the code in any other thread.

So why not just call it directly in main thread as needed or as feasible?

Unless you can do multiple things in parallel without locking, there is no benefit to be had from multiple threads. Concurrency is all about finding out which parts can run at the same time without waiting for one another.

Quote:
I wonder if anyone might have a better suggestion as to how to cope with spreading code that takes a while to execute over several frames without having to drastically refactor it?

By processing it a piece at a time. This is only possible if entire application can be split into small, coarse-grained but single-threaded pieces of functionality. These are then called via asynchronous dispatcher, which executes individual parts concurrently, but those parts are guaranteed to not share any state.
Mephs
Mephs
The point of having it in it's own thread rather than the main thread is so that execution of the task can be paused and resumed at will, so the job can be completed in bite-sized tasks without a major refactor instead of as one big lump as it currently stands.

If I tried to do this in the main thread, I would have no way of pausing the task whilst others complete without losing my place in the code being executed. In a separate thread, execution of the task would be paused rather than broken out of. Are you trying to say that this is not possible? I'm thinking it might be possible with boost signals/slots.

I should also mention that the code in question is also not called very often. It seems to cause the chugs at a rate of around 5 chugs per 5-10 minute period.

Thanks,

Steve O
Antheus
Antheus
Quote:
Original post by Mephs
The point of having it in it's own thread rather than the main thread is so that execution of the task can be paused and resumed at will, so the job can be completed in bite-sized tasks without a major refactor instead of as one big lump as it currently stands.


Again, shared state. If the mystery routine uses state which is shared with rest of application, then you cannot pause and resume later. If it's not shared, then it can always keep on running since it doesn't affect the rest of application.

Quote:
If I tried to do this in the main thread, I would have no way of pausing the task whilst others complete without losing my place in the code being executed. In a separate thread, execution of the task would be paused rather than broken out of. Are you trying to say that this is not possible? I'm thinking it might be possible with boost signals/slots.


This has nothing to do with API, but with algorithm itself. What does the mystery routine do, and how does it interact with rest of application.

Unless your mystery routine already provides a way to execute it piecemeal, then you cannot deterministically interrupt it. If it does provide such a way, call it from main thread.

This falls under ACID problem. Your mystery routine does this:
// ASELECT * FROM X;// do something with *UPDATE *;// B

Such code can only be interrupted at points A or B, but not in between. So unless you have a way of accomplishing that using existing code, then no, it cannot be done since you lose ACID guarantee and consequently thread-safety. In databases, transactions serve this purpose, where they guarantee sequences execute as a whole. Languages generally do not have such constructs. They can be achieved using locks, but they require enclosing entire shared state in them. In your case, suspending a thread that is holding a lock would dead-lock entire application. Thread holding the lock would be suspended, rest of application would be waiting for lock to release.


Threads can be suspended and resumed, but that does not do what you're trying to do. Suspending a thread does not freeze any shared state it might have. And if state isn't shared, there is no need to suspend the thread - just keep running it, it's not affecting anything.

Quote:
I'm thinking it might be possible with boost signals/slots.


I use boost asio's io_service::post() for this purpose, which is the approach I mentioned above. Split entire application into small chunks, then let io_service multiplex them, perhaps concurrently.

It's not necessarily trivial, but is one of preferred ways of building responsive applications.

Topic Locked

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

Sign in to reply to this topic.