Home Forums Legacy SDKs eyeTracker.Dispose() sometimes hangs if called during calibration

Viewing 7 posts - 1 through 7 (of 7 total)
  • Author
    Posts
  • #2271
    Joseph Howse
    Participant

    I am using the EyeTracker class (from the Gaze SDK) in a WPF application. I call eyeTracker.Dispose() in the application’s OnExit method. However, the call to Dispose() sometimes hangs. This seems to happen when calibration has been started but not stopped.

    Expected behaviour: A call to Dispose() should finish safely regardless of the tracker’s calibration state. We cannot always guarantee that calibration finishes before the application terminates.

    #2284
    Robert [Tobii]
    Participant

    Ok, thank you for noticing.

    To me it sounds reasonable that the applications makes sure that there is no running calibration or other event handlers activated before trying to dispose the EyeTracker instance, but I am not sure if that is possible given your use case. Can you share more information about how to reproduce the problem?

    #2294
    Joseph Howse
    Participant

    Part of the difficulty is that there is no synchronous method for ending calibration. If the user (or the OS) wants to close the application during calibration, I would have to try to cancel the quit event, call StopCalibrationAsync with a callback, and finally close the application in the callback. To me, trying to cancel quit events to clean up asynchronously seems like bad practice; a quit event should be interpreted as a demand to clean up synchronously. Thus, a resource such as the eye tracker should be disposable quickly and synchronously during application tear-down.

    For me, the issue is arising with an EyeTracker that is created in the constructor of App (my Application subclass), accessed from a Page subclass that listens for eye tracking data events and calls eye tracking calibration methods, and disposed in App’s OnExit method.

    #2310
    Anders
    Participant

    Hi Joseph,
    I agree that this is how a Dispose method should work, and as far as I know it is also the way it works.

    There is one quirk though: if you run the event loop on a thread of your own (that is, you use EyeTracker.RunEventLoop and not EyeTracker.RunEventLoopOnInternalThread), then you are responsible for breaking the event loop and joining the event loop thread before calling EyeTracker.Dispose.

    Please let me know how you run your event loop to help with troubleshooting.

    #2312
    Joseph Howse
    Participant

    I am using EyeTracker.RunEventLoopOnInternalThread.

    #2337
    Anders
    Participant

    Hi Joseph,
    thanks for the information. I’ll try to reproduce the problem.

    #2396
    Anders
    Participant

    Hi Joseph,
    I have tried to reproduce the problem but without success.

    When you say that it sometimes hangs, how often is “sometimes”?

    I built this model application to try out what happens when the app exits while a calibration is ongoing. Can you please have a look and tell me if I do something differently from your app? Or, even better, if you can modify the code so that it sometimes hangs? I use VS2013 btw.

    Starting from a default WPF C# app, I modified the main window like so:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        
        <Grid>
            <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
                <Button Command="New" Padding="20">Start calibration</Button>
            </StackPanel>
        </Grid>
    </Window>

    And the App.xaml.cs like so:

    namespace WpfApplication1
    {
        using System.Diagnostics;
        using System.Windows;
        using System.Windows.Input;
        using Tobii.Gaze.Core;
    
        public partial class App : Application
        {
            private EyeTracker _tracker;
            //private Timer _timer;
    
            public App()
            {
                var url = new EyeTrackerCoreLibrary().GetConnectedEyeTracker();
                Debug.Assert(url != null); // if this fails: please connect tracker!
    
                _tracker = new EyeTracker(url);
                _tracker.RunEventLoopOnInternalThread(_ => { });
                _tracker.ConnectAsync(_ => { });
    
                var binding = new CommandBinding(ApplicationCommands.New, OnStartCalibration, (s, e) => e.CanExecute = true);
                CommandManager.RegisterClassCommandBinding(typeof(Window), binding);
            }
    
            private void OnStartCalibration(object sender, ExecutedRoutedEventArgs e)
            {
                _tracker.StartCalibrationAsync(errorCode =>
                    {
                        Trace.WriteLine(string.Format("Start calibration: {0}", errorCode));
                        NextCalibrationPoint();
                    });
    
                //_timer = new Timer(_ =>
                //    {
                //        Dispatcher.BeginInvoke(new Action(() =>
                //            {
                //                MainWindow.Close();
                //            }));
                //    }, null, 1000, Timeout.Infinite);
            }
    
            private void NextCalibrationPoint()
            {
                Trace.WriteLine("Calling AddCalibrationPointAsync");
                _tracker.AddCalibrationPointAsync(new Point2D(), (errorCode2 =>
                {
                    Trace.WriteLine(string.Format("Add calibration point: {0}", errorCode2));
                    NextCalibrationPoint();
                }));
            }
    
            protected override void OnExit(ExitEventArgs e)
            {
                Trace.WriteLine("Disposing tracker");
                _tracker.Dispose();
                Trace.WriteLine("Disposing tracker done");
    
                base.OnExit(e);
            }
        }
    }

    The app runs an infinite calibration sequence when you press the button. So the idea is to start it and then close the window.

    You can also get the window to self-destruct after a delay by uncommenting the commented-out lines.

Viewing 7 posts - 1 through 7 (of 7 total)
  • You must be logged in to reply to this topic.