PCSX2 Documentation/Threading Basics: Difference between revisions
PCSX2 Documentation/Threading Basics (view source)
Revision as of 19:34, 9 January 2015
, 9 January 2015no edit summary
No edit summary |
No edit summary |
||
Line 3: | Line 3: | ||
In this page are a handful of tips and guidelines for programming with threads. The PCSX2 team provides and updates this resource in large part because there is a lot of mis-information out there when it comes to parallel programming. | In this page are a handful of tips and guidelines for programming with threads. The PCSX2 team provides and updates this resource in large part because there is a lot of mis-information out there when it comes to parallel programming. | ||
InterlockExchange / AtomicExchange | ==InterlockExchange / AtomicExchange== | ||
The primary tool of PCSX2 threading is the AtomicExchange method and it's many cousins (see Utilities/Threading.h for all of them). These functions are a friendly C++ layer for underlying _InterlockedExchange intrinsics. Some folks prefer the term Interlocked, but we use Atomic because it's shorter, and begins with 'A' like the A-Team, which is cool. | The primary tool of PCSX2 threading is the AtomicExchange method and it's many cousins (see Utilities/Threading.h for all of them). These functions are a friendly C++ layer for underlying _InterlockedExchange intrinsics. Some folks prefer the term Interlocked, but we use Atomic because it's shorter, and begins with 'A' like the A-Team, which is cool. | ||
In non-debug builds all varieties of Atomic methods translate into one to three inline'd Lock-prefixed instructions. Lock-prefixing is quite efficient on modern CPUs, so there is little concern over using interlocking liberally (better safe than sorry); though they should still only be used as needed in performance-critical code. This guide will help you know exactly when thy are, in fact, needed. | In non-debug builds all varieties of Atomic methods translate into one to three inline'd Lock-prefixed instructions. Lock-prefixing is quite efficient on modern CPUs, so there is little concern over using interlocking liberally (better safe than sorry); though they should still only be used as needed in performance-critical code. This guide will help you know exactly when thy are, in fact, needed. | ||
Atomic Operation Criteria | ==Atomic Operation Criteria== | ||
Definition: An atomic operation is one which executes completely, and will not yield a 'partial' result even if a concurrent operation on the same memory address is being performed in parallel. In other words, atomic operations are thread-safe. :) | Definition: An atomic operation is one which executes completely, and will not yield a 'partial' result even if a concurrent operation on the same memory address is being performed in parallel. In other words, atomic operations are thread-safe. :) | ||
Line 17: | Line 17: | ||
Translation: If only one thread modifies a variable, no atomic operations are needed. Any number of other threads can safely read that value simutaneously. If more than one thread modifies a variable, all threads must use atomic operations when modifying it! | Translation: If only one thread modifies a variable, no atomic operations are needed. Any number of other threads can safely read that value simutaneously. If more than one thread modifies a variable, all threads must use atomic operations when modifying it! | ||
Mutex and Semaphore | ==Mutex and Semaphore== | ||
These are the two most basic tools of the multi-threaded programmer. Understaing their basic functionality and common uses will be important to many programming tasks in PCSX2. | These are the two most basic tools of the multi-threaded programmer. Understaing their basic functionality and common uses will be important to many programming tasks in PCSX2. | ||
Line 28: | Line 28: | ||
In PCSX2, it is usually best to use the ScopedLock class instead of acquiring and releasing mutexes directly. This is because PCSX2 uses C++ exceptions just about everywhere, and using a ScopedLock ensures that the mutex will be released if an excepion occurs. Otherwise, a mutex could be left dangling, and some form of deadlock would eventually occur. | In PCSX2, it is usually best to use the ScopedLock class instead of acquiring and releasing mutexes directly. This is because PCSX2 uses C++ exceptions just about everywhere, and using a ScopedLock ensures that the mutex will be released if an excepion occurs. Otherwise, a mutex could be left dangling, and some form of deadlock would eventually occur. | ||
Don't Use Timed Mutexes or Semaphores! | ==Don't Use Timed Mutexes or Semaphores!== | ||
Generally speaking, using timeout parameters on mutexes and semaphores is a bad idea. Some people try to use timed mutexes in order to help avoid deadlock scenarios -- this is not recommended. The better alternative is to use Proxy Queue Threads, which allow two complex threads to communicate with eath other safely via a third thread that acts as a proxy. | Generally speaking, using timeout parameters on mutexes and semaphores is a bad idea. Some people try to use timed mutexes in order to help avoid deadlock scenarios -- this is not recommended. The better alternative is to use Proxy Queue Threads, which allow two complex threads to communicate with eath other safely via a third thread that acts as a proxy. | ||
Note that PCSX2 itself does use timed mutexes and semaphores on the Main/UI thread. This feature is indended as a last resort for when bad multithreaded coding practices have resulted in long delays or deadlocks, and should not be relied upon. I may remove them in the future, or replace them with concrete assertion failures. | Note that PCSX2 itself does use timed mutexes and semaphores on the Main/UI thread. This feature is indended as a last resort for when bad multithreaded coding practices have resulted in long delays or deadlocks, and should not be relied upon. I may remove them in the future, or replace them with concrete assertion failures. | ||
Aggressive vs. Passive Threading | ==Aggressive vs. Passive Threading== | ||
Aggressive threading uses spins (tight loops) to monitor changes made by other threads. | Aggressive threading uses spins (tight loops) to monitor changes made by other threads. | ||