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

Author Topic: Inheriting from sf::Thread : destruction  (Read 6286 times)

0 Members and 1 Guest are viewing this topic.

Kalith

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Inheriting from sf::Thread : destruction
« on: June 27, 2010, 03:09:59 pm »
Hello !

I've been playing with sf::Thread and found out something that disturbed me. Using slightly modified code from the tutorials :
Code: [Select]
#include <SFML/System.hpp>
#include <iostream>

class MyThread : public sf::Thread
{
private :

    virtual void Run()
    {
        // Print something...
        for (int i = 0; i < 10; ++i)
            std::cout << "I'm the thread number 2" << std::endl;
    }
};

int main()
{
    // Create an instance of our custom thread class
    {
        MyThread Thread;

        // Start it !
        Thread.Launch();
    }

    // Print something...
    for (int i = 0; i < 10; ++i)
        std::cout << "I'm the main thread" << std::endl;

    return EXIT_SUCCESS;
}

... then the content of MyThread::Run is never executed.

Looking at the source of sf::Thread, it seems that Wait() is called in sf::Thread's destructor, so one could expect that the thread waits until Run() returns before the end of the destructor.
But by that time, we are in sf::Thread's destructor, and no derived function can be called : the thread now runs sf::Thread::Run(), which does nothing and terminates the thread.
As a check : turn sf::Thread::Run() into a pure virtual function and you'll get an error message saying a pure virtual function has been called.

I'm not sure this is the expected behavior, because you don't have this problem with free functions.

More intriguing, if you write :
Code: [Select]
#include <SFML/System.hpp>
#include <iostream>

class MyThread : public sf::Thread
{
private :

    virtual void Run()
    {
        // Print something...
        for (int i = 0; i < 10; ++i)
            std::cout << "I'm the thread number 2" << std::endl;
    }
};

int main()
{
    int n = 5;

    // Create an instance of our custom thread class
    {
        MyThread Thread;

        // Start it !
        Thread.Launch();

        for (int i = 0; i < n; ++i)
            std::cout << "I'm the main thread" << std::endl;
    }

    // Print something...
    for (int i = 0; i < 10 - n; ++i)
        std::cout << "I'm the main thread" << std::endl;

    return EXIT_SUCCESS;
}

... with n = anything greater than 0, then sf::Thread actually executes the whole MyThread::Run() function :shock:

I'm using SFML 2.0 (15/06/2010) on Windows XP with MinGW.
Any clue ?
Kal.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Inheriting from sf::Thread : destruction
« Reply #1 on: June 27, 2010, 03:57:57 pm »
This is probably because the destructor in the main thread is executed before the thread starts. So when it tries to Run(), the object is already destroyed in the main thread.
Laurent Gomila - SFML developer

Kalith

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Inheriting from sf::Thread : destruction
« Reply #2 on: June 27, 2010, 05:46:19 pm »
Makes sense.
But even if the thread has a chance to be created before, isn't the call to Wait() in sf::Thread destructor supposed to call sf::Thread::Run() instead of MyThread::Run() ? That's what I'd expect (see "Things to remember" at the bottom).
Kal.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Inheriting from sf::Thread : destruction
« Reply #3 on: June 27, 2010, 06:43:39 pm »
Wait() doesn't call Run(), it is called by Launch().
Laurent Gomila - SFML developer

Kalith

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Inheriting from sf::Thread : destruction
« Reply #4 on: June 27, 2010, 07:12:41 pm »
Sorry, I wasn't very clear.
When the thread is created, it calls MyThread::Run() on the "Thread" object we created in the main().
When you enter sf::Thread destructor, MyThread's destructor has been called already : "Thread" doesn't exist anymore.

The problem is : I don't understand how the thread can call MyThread::Run(), because the object it used to call it with no longer exists.

Here we're fine because MyThread::Run() doesn't use any member variable, but try this :
Code: [Select]
#include <SFML/System.hpp>
#include <iostream>

class MyThread : public sf::Thread
{
public :

    MyThread() : destroyed(false)
    {

    }

    ~MyThread()
    {
        std::cout << "~MyThread" << std::endl;
        destroyed = true;
    }

private :

    virtual void Run()
    {
        // Print something...
        for (int i = 0; i < 10; ++i)
        {
            if (!destroyed)
                std::cout << "I'm the thread number 2" << std::endl;
            else
                std::cout << "I'm the *dead* thread number 2" << std::endl;
        }
    }

    bool destroyed;
};

int main()
{
    int n = 5;

    // Create an instance of our custom thread class
    {
        MyThread Thread;

        // Start it !
        Thread.Launch();

        for (int i = 0; i < n; ++i)
            std::cout << "I'm the main thread" << std::endl;
    }

    // Print something...
    for (int i = 0; i < 10 - n; ++i)
        std::cout << "I'm the main thread" << std::endl;

    return EXIT_SUCCESS;
}


Result on my computer :
Code: [Select]
I'm the main thread
I'm the thread number 2
I'm the main thread
I'm the thread number 2
I'm the main thread
I'm the thread number 2
I'm the main thread
I'm the thread number 2
I'm the main thread
I'm the thread number 2
I'm the main thread
~MyThread
I'm the thread number 2
I'm the *dead* thread number 2
I'm the *dead* thread number 2
I'm the *dead* thread number 2
I'm the *dead* thread number 2
I'm the main thread
I'm the main thread
I'm the main thread
I'm the main thread
I'm the main thread


... now imagine a real code with more complex member values, and you're almost sure to get a crash (undefined behavior anyway).

The solution would be to call Wait() from MyThread's destructor instead of sf::Thread, but there is no way to ensure the user will actually do it.
Kal.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Inheriting from sf::Thread : destruction
« Reply #5 on: June 27, 2010, 07:56:08 pm »
That's right. It's the user's responsibility to end the thread at the right time. If it needs to be done before the destructor is executed, then Wait() must be called explicitely.
Laurent Gomila - SFML developer

Kalith

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Inheriting from sf::Thread : destruction
« Reply #6 on: June 27, 2010, 08:51:46 pm »
I see.
Maybe a little note in the docs wouldn't hurt ?
Kal.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Inheriting from sf::Thread : destruction
« Reply #7 on: June 27, 2010, 10:52:17 pm »
Probably :)
Laurent Gomila - SFML developer

Kalith

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Inheriting from sf::Thread : destruction
« Reply #8 on: June 27, 2010, 11:00:12 pm »
That's fine with me then :)
Thank you for your time, and keep up the good work !
Kal.