Plugin not destructing

Philipp's Avatar

Philipp

03 Feb, 2016 04:33 PM

Hi Chris,

I have recently started to write on a Plugin for a custom Teensy-IO using MWorks 0.6.

This plugin establishes a connection to the Teensy within the initialization routine and then schedules 5 executions of a sync event in order to sync Teensy and MWorks time.

I do this (within Teensy::initialize()):

    scheduler = Scheduler::instance();
    shared_ptr<Teensy> this_one = component_shared_from_this<Teensy>();
    schedule_node = scheduler->scheduleMS(std::string(FILELINE ": ") + getTag(),
                                          1, //defer first start 1ms
                                          1000, //repeat every second
                                          5,//M_REPEAT_INDEFINITELY,
                                          boost::bind(send_sync_request_, this_one),
                                          M_DEFAULT_PRIORITY,
                                          M_DEFAULT_WARN_SLOP_US,
                                          M_DEFAULT_FAIL_SLOP_US,
                                          M_MISSED_EXECUTION_DROP);

As a result, I get 5 executions of the send_sync_request function. But doing this also prevents the plugin from ever reaching the destructor. I need to reset the serial port there (which works perfectly when no scheduling takes place). What am I doing wrong? Should I clear the shared_ptr manually and how would this look like exactly?

Thank you for your help,
Philipp

PS: I used the Eyelink Plugin as a template. I didn't have time to check, but it might be it will also never destruct once started... this should be checked out.

  1. Support Staff 1 Posted by Christopher Sta... on 03 Feb, 2016 06:17 PM

    Christopher Stawarz's Avatar

    Hi Philipp,

    Because you use a shared_ptr in the call to boost::bind, schedule_node owns a reference to the Teensy instance. If schedule_node is a member of the Teensy class, then this creates a reference cycle, which prevents the instance from being destructed.

    In order to break the cycle, you need to destroy schedule_node outside of the Teensy destructor. For an I/O device, a likely place to do this is in the stopDeviceIO method. Add the following:

    if (schedule_node) {
        schedule_node->cancel();  // Ensure the task is stopped
        schedule_node.reset();    // Release the ScheduleTask instance
    }
    

    PS: I used the Eyelink Plugin as a template. I didn't have time to check, but it might be it will also never destruct once started... this should be checked out.

    OK, I'll take a look.

    Cheers,
    Chris

  2. 2 Posted by Schwedhelm, Phi... on 04 Feb, 2016 10:39 AM

    Schwedhelm, Philipp's Avatar

    Hi Chris,

    thank you for your fast reply.

    I can of course destroy the schedule_node within stopDeviceIO(), but the idea was that the clock syncing starts at plugin load time and ends when the plugin is unloaded. I can live with this not being possible, but what will happen when the server attempts to unload the plugin while it is running? Will stopDeviceIO() be called automatically then or will the reference cycle then still prevent the plugin from being unloaded?

    For the case of the Eyelink plugin, I think I checked within the destructor whether the device was still running and if it was, I would stop and destroy the schedule nodes like you suggested. This was working just fine, but now, if I understand you right, the destructor will not even be called if a reference cycle exists, even if this cycle would break within the destructor. Is there perhaps another function that I can use? Something like “plugin_unload()”?

    Best,
    Philipp

  3. Support Staff 3 Posted by Christopher Sta... on 04 Feb, 2016 03:22 PM

    Christopher Stawarz's Avatar

    Hi Philipp,

    I can of course destroy the schedule_node within stopDeviceIO(), but the idea was that the clock syncing starts at plugin load time and ends when the plugin is unloaded.

    OK, then you probably want to use a weak_ptr. In initialize, create schedule_node like this:

    auto weak_this = boost::weak_ptr<Teensy>(component_shared_from_this<Teensy>());
    auto functor = [weak_this]() {
        if (auto shared_this = weak_this.lock()) {
           send_sync_request_(shared_this);
        }
    }
    schedule_node = scheduler->scheduleMS(std::string(FILELINE ": ") + getTag(),
                                          1, //defer first start 1ms
                                          1000, //repeat every second
                                          M_REPEAT_INDEFINITELY,
                                          functor,
                                          M_DEFAULT_PRIORITY,
                                          M_DEFAULT_WARN_SLOP_US,
                                          M_DEFAULT_FAIL_SLOP_US,
                                          M_MISSED_EXECUTION_DROP);
    

    This way, schedule_node does not own a reference to the Teensy instance, so it won't prevent its destruction. You can then put the schedule_node shutdown code that I shared previously in the Teensy destructor.

    For the case of the Eyelink plugin, I think I checked within the destructor whether the device was still running and if it was, I would stop and destroy the schedule nodes like you suggested. This was working just fine, but now, if I understand you right, the destructor will not even be called if a reference cycle exists, even if this cycle would break within the destructor.

    Yes, you understand correctly. However, the EyeLink plugin works, because it does what I recommended in my previous message: It destroys the schedule node in stopDeviceIO. The similar code in the destructor could be removed, because if the schedule node still exists, the destructor will never run.

    Chris

  4. 4 Posted by Philipp on 04 Feb, 2016 05:26 PM

    Philipp's Avatar

    Thank you Chris, this works great !!

    Now, is there any disadvantage to using the weak_ptr instead of shared_ptr? Can something go wrong when there are two scheduler nodes working in parallel, both with weak pointers?

    Thanks again for the great help!
    Philipp

  5. Support Staff 5 Posted by Christopher Sta... on 05 Feb, 2016 12:57 PM

    Christopher Stawarz's Avatar

    Hi Philipp,

    Now, is there any disadvantage to using the weak_ptr instead of shared_ptr?

    The only disadvantage is the added complexity of needing to ensure that the pointed-to object still exists before (and while) you use it. My example code shows the right way to do this: First attempt to create a shared_ptr (i.e. strong reference) with the lock method, and if successful, hold on to the shared_ptr for as long as you need to use the object:

    if (auto strong_ref = weak_ref.lock()) {
        // Do work with strong_ref
        // ...
        // strong_ref goes out of scope here, and strong reference is released
    }
    

    Can something go wrong when there are two scheduler nodes working in parallel, both with weak pointers?

    Nothing can go wrong that can't also go wrong using shared pointers. In both cases, if multiple threads are accessing the same object, then you need to use some synchronization mechanism (e.g. mutex locks) to prevent one thread from modifying shared data while another thread reads or writes that data.

    Cheers,
    Chris

  6. Christopher Stawarz closed this discussion on 17 Feb, 2016 03:25 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac

Recent Discussions

17 May, 2022 02:12 PM
16 May, 2022 03:12 PM
04 May, 2022 06:02 PM
03 May, 2022 01:30 PM
02 May, 2022 10:47 PM