Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Mutex inconsistency  (Read 3237 times)

0 Members and 1 Guest are viewing this topic.

Jaenis

  • Newbie
  • *
  • Posts: 48
    • View Profile
Mutex inconsistency
« on: July 27, 2009, 05:08:04 am »
There seems to be inconsistency in mutex implementation between Windows and Linux in SFML 1.5.

I have following code:
Code: [Select]
#include <SFML/System.hpp>
#include <iostream>

sf::Mutex WorkMutex;

void Worker(void *)
{
int Count=0;
while (Count<5)
{
// Wait for job
WorkMutex.Lock();

// Doing the job now
std::cout << "Work: " << Count << std::endl;
Count++;
}

std::cout << "Thread exit" << std::endl;
}

int main()
{
// Lock mutex to prevent worker doing anything
WorkMutex.Lock();

// Create and launch worker thread
sf::Thread Thread(Worker);
Thread.Launch();

// Start five jobs
for (int i=0; i<5; i++)
{
std::cout << "Start: " << i << std::endl;
WorkMutex.Unlock();

// Sleep a bit
sf::Sleep(0.5);
}

return EXIT_SUCCESS;
}


In Windows this prints:
Code: [Select]
Start: 0
Work: 0
Work: 1
Work: 2
Work: 3
Work: 4
Thread exit
Start: 1
Start: 2
Start: 3
Start: 4

When I unlocked the WorkMutex from main, Worker thread entered to critical section (as it is implemented in SFML/Win32). And if thread is on critical section, locking it doesn't do anything. Therefore Worker thread processed all jobs immediately.

But, here comes the interesting part.
I replaced mutex with boost::mutex which is using POSIX pthread_mutex_t or similar (as is SFML/Linux code) and here's the output:
Code: [Select]
Start: 0
Work: 0
Start: 1
Work: 1
Start: 2
Work: 2
Start: 3
Work: 3
Start: 4
Work: 4
Thread exit


So, I dare to claim that my code example works differently between Windows and Linux, even if both are using same SFML source.
Could someone try this code on linux and tell what it outputs?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32451
    • View Profile
    • SFML's website
    • Email
Mutex inconsistency
« Reply #1 on: July 27, 2009, 07:58:22 am »
Your code highly depends on the OS scheduler, which can be very different between Linux and Windows. So you can't expect the same results.

And there's nothing wrong with both outputs. So what's the problem actually?
Laurent Gomila - SFML developer

Jaenis

  • Newbie
  • *
  • Posts: 48
    • View Profile
Mutex inconsistency
« Reply #2 on: July 27, 2009, 08:55:57 am »
I found this when I was porting my data loader thread from SDL to SFML.

Here's more complete example:
Code: [Select]
#include <SFML/System.hpp>
#include <iostream>
#include <queue>

sf::Mutex WorkMutex;

enum JobEnum
{
LoadJob,
ExitJob
};

sf::Mutex JobMutex;
std::queue<enum JobEnum> LoadQue;

/*--------------------------------------------------------------------------*/

void Worker(void *)
{
bool Done=false;
while (!Done)
{
// Wait for job
WorkMutex.Lock();

// Get a job from queue
enum JobEnum Job;
{
sf::Lock Lock(JobMutex);
std::cout << "Get job from queue" << std::endl;
Job=LoadQue.front();
LoadQue.pop();
}

// Check the type of job
switch (Job)
{
case LoadJob:
std::cout << "Load job" << std::endl;
break;

case ExitJob:
std::cout << "Exit job" << std::endl;
Done=true;
break;

default:
std::cout << "FATAL: Unknown job" << std::endl;
Done=true;
break;
}
}

std::cout << "Thread exit" << std::endl;
}

/*--------------------------------------------------------------------------*/
// Main
//
int main()
{
// Lock mutex to prevent worker doing anything
WorkMutex.Lock();

// Create and launch worker thread
sf::Thread Thread(Worker);
Thread.Launch();

// Add few load jobs
for (int i=0; i<3; i++)
{
// Add job to queue
{
sf::Lock Lock(JobMutex);
std::cout << "Add job to queue" << std::endl;
LoadQue.push(LoadJob);
}

// Tell thread that there is something to do
WorkMutex.Unlock();

// Simulate data processing
sf::Sleep(0.5);
}

// Tell worker thread to exit
std::cout << "Add exit job" << std::endl;
LoadQue.push(ExitJob);
WorkMutex.Unlock();

return EXIT_SUCCESS;
}

(fyi: this code is not complete, this version here has logical errors)

This prints following:
Code: [Select]
Add job to queue
Get job from queue
Load job
Get job from queue       <---- Bug here, there was only one job in que
FATAL: Unknown job
Thread exit
Add job to queue
Add job to queue
Add exit job


Since SFML is using critical section my worker thread does not work and it gets two jobs from queue, even if only one was created.


My point here is that when someone writes similar code on Linux, it will not work when it is compiled on Windows.
This is not a problem for me, since now I know that SFML doesn't do mutexes as I expect, but it might be problem for someone else at some point.

Also, since my code above should be using Semaphore instead of WorkMutex, this might be much smaller issue than it sounds.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32451
    • View Profile
    • SFML's website
    • Email
Mutex inconsistency
« Reply #3 on: July 27, 2009, 09:08:53 am »
Well, I think that the only problem is in your logic, not in SFML ;)

In your code, nothing prevents the Worker thread from getting a second job before the main thread adds it to the queue. There's no bug here. I think you were previously just relying on an undefined and completely random behaviour.
Laurent Gomila - SFML developer

l0calh05t

  • Full Member
  • ***
  • Posts: 200
    • View Profile
Mutex inconsistency
« Reply #4 on: July 27, 2009, 07:13:47 pm »
Quote from: "Laurent"
Your code highly depends on the OS scheduler, which can be very different between Linux and Windows. So you can't expect the same results.

And there's nothing wrong with both outputs. So what's the problem actually?


Actually, in this first example, the behavior doesn't depend on the OS scheduler. The SFML mutex is behaving like a recursive mutex, and the boost mutex like a non-recursive mutex, but(!) there is a big problem in this code as the mutex is locked/unlocked accross threads, which is undefined as usually only the owner of a lock can unlock the mutex. What you actually want here is a counting semaphore with main incrementing and the worker decrementing.