ravi.joshi53's picture

OpenTK is not releasing a thread when invoked from Windows Form

Hi,
I am getting points from a device continuously. Since the device is little slower, so as per the suggestions from the guys here, I kept it in a separate thread.
I want the device to be disconnected once the game is closed. Also I am invoking the GameWindow form Windows form. Below is the snippet of code-

Game.cs

class Game : GameWindow
{
    int currentX;
    int currentY;
    Point point;
    Device device;
    object pointLock = new object();
 
    public Game() : base(800, 600, GraphicsMode.Default, "Test")
    {
        VSync = VSyncMode.On;
        this.WindowBorder = WindowBorder.Fixed;
        this.device = new Device();
        Thread deviceThread = new Thread(UpdatePoint);
        deviceThread.IsBackground = true;
        deviceThread.Start();
    }
 
    void UpdatePoint()
    {
        while (true)
        {
            Point temp = device.GetPoint();
            lock (pointLock) { point = temp; }
        }
    }
 
    protected override void OnUpdateFrame(FrameEventArgs e)
    {
        base.OnUpdateFrame(e);
        lock (pointLock)
        {
            currentX = point.x;
            currentY = point.y;
        }
    }

Form.cs

Thread gameThread;
readonly object launcherLock = new object();
 
private void button1_Click(object sender, EventArgs e)
{
    lock (launcherLock)
    {
        if (gameThread == null)
        {
            gameThread = new Thread(() =>
           {
               using (var game = new Game())
                   game.Run(60);
               gameThread = null;
           });
 
            gameThread.SetApartmentState(ApartmentState.STA);
            gameThread.Start();
        }
        else
            MessageBox.Show("The game is already running.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

Device.cs

class Device
{
    public Device()
    {
        connectDevice();
    }
 
    ~Device()
    {
        disconnectDevice();
    }
 
    public Point GetPoint()
    {
        Point point = new Point();
        // some more stuff here
        return point;
    }

I observed that the device is connected even though the game is closed. The device is disconnected only when the from is being closed.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

Your code is responsible for cleaning up the thread once its work is complete. Hook GameWindow.Unload event and signal your background thread to exit.

This is not in any way specific to OpenTK - the owner of a resource (threads, files, sockets, ...) is always responsible for cleaning up.

ravi.joshi53's picture

Hi Fiddler, Is this what you are saying?

protected override void OnUnload(EventArgs e)
{
    base.OnUnload(e);
    deviceThread.Abort();
}

For testing purpose, I have changed the code of connectDevice(); and disconnectDevice();. connectDevice(); now creates a file whereas disconnectDevice(); deletes this created file. I can see that still sometimes it is not deleting the file.

the Fiddler's picture

As written in this blog post, "Thread.Abort is like stopping a car by shooting the driver in the head. The car will stop, but there’s no telling what damage it’ll do in the process."

Do this instead:

    int exit_request;
    void UpdatePoint()
    {
        while (exit_request == 0)
        {
            Point temp = device.GetPoint();
            lock (pointLock) { point = temp; }
        }
    }
 
    protected override OnUnload(object sender, EventArgs e)
    {
        Interlocked.Increment(ref exit_request);
        deviceThread.Join();
        device.Dispose(); // see below
    }

Also don't use a finalizer when you need deterministic resource cleanup (e.g. for files). Use the disposable pattern instead.

ravi.joshi53's picture

I am reading the blog post pointed by you. However I am not familiar with disposable pattern. By reading the above link, I am guessing that I need to implement Disposable class in my Device class. But how? Below is a snippet of Device class

public class Point
{
    public int x;
    public int y;
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}
 
public class Device
{
    int height;
    int width;
 
    ~Device()
    {
        disconnectDevice();
    }
 
    public Device(int width, int height)
    {
        this.width = width;
        this.height = height;
        if (!connectDevice())
            Console.WriteLine("Unable to connect to device");
    }
 
    public void disconnectDevice()
    {
     // some stuff here
    }
 
    private bool connectDevice(int comPort, int baudRate)
    {
     // some stuff here
    }
 
    public Point getPoint()
    {
        Point point = new Point();
        // some more stuff here
        return point;
    }
 
    private string getDeviceInfo()
    {
     // some stuff here
    }
 
    private bool IsDeviceConnected()
    {
     // some stuff here
    }
}
ravi.joshi53's picture

Hi Fiddler,

I have used the disposable pattern in my Device class. Now I need to test it. I just got a quick question. Since Device class returns Point class object, so do I need to dispose Point class too? (The Point class have only two integers x and y)

Below is my disposable pattern implementation for Device class-

class Device : IDisposable
{
    bool disposed;
    public Device()
    {
        connectDevice();
    }
 
    ~Device()
    {
        Dispose(false);
    }
 
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
 
    protected virtual void Dispose(bool disposing)
    {
        if (disposed)
            return;
 
        if (disposing)
            disconnectDevice();
 
        disposed = true;
    }

Does it look good to you?

the Fiddler's picture

This looks good, yes.

You only need to explicitly Dispose() objects that contain unmanaged resources: OS threads, files, sockets; OpenGL textures, VBOs, etc. Point does not contain any unmanaged resources, so you don't need to dispose it.

In general, if a class implements IDisposable then you should dispose it manually. If not, you can let the GC take care of it.

ravi.joshi53's picture
the Fiddler wrote:

This looks good, yes.

You only need to explicitly Dispose() objects that contain unmanaged resources: OS threads, files, sockets; OpenGL textures, VBOs, etc. Point does not contain any unmanaged resources, so you don't need to dispose it.

In general, if a class implements IDisposable then you should dispose it manually. If not, you can let the GC take care of it.

Thanks a lot for the valuable knowledge.... I got it now.