For this demo we'll have two threads: a producer which fills a buffer and a consumer that empties a buffer. Not only can the two threads not use the same buffer at the same time, but
a consumer can't read from an empty buffer and a producer can't fill a buffer that's already full.

We'll use a mutex (mutually exclusive) to prevent the two threads from grabbing the same piece of data and conditions to let the threads know when they can consume and
can produce.

Here are the functions that produce and consume. Producing a buffer means generating a random number and consuming a buffer mean reseting the generated number. The best way to show
how this works is go through an example.

Let's say the producer fires first and locks the mutex with SDL_LockMutex much like it would a semaphore with a
value of one:

The buffer is empty so it goes through and produces:

It then exits the function to unlock the critical section with SDL_UnlockMutex so the consumer can consume:

Ideally, we would want the consumer to consume, but imagine if the producer fired again:

And after the producer locked the critical section the consumer tries to get it but the critical section is already locked to the producer:

With just a binary semaphore, this would be a problem because the producer can't produce into a full buffer and the consumer is locked behind a mutex. However, mutexes have the
ability to be used with conditions.

What the condition allows us to do is if the buffer is already full, we can wait on a condition with
SDL_CondWait and unlock the mutex for other threads:

Now that the consumer is unlocked it can go through and consume:

And once it's done it signals the producer with SDL_CondSignal to produce again:

And then it can continue through:

With the critical section protected by a mutex and the ability of the threads to talk to each other, the worker threads will work even through we do not know in which order they will
execute.