Избегайте ошибок выделения с помощью функций destroy () и asyn c - PullRequest
2 голосов
/ 24 апреля 2020

Ниже приведен простой сценарий для некоторого расширения GNOME:

  1. Включить расширение. Расширение является классом, который расширяет Clutter.Actor.
  2. . Он создает актер с именем myActor и добавляет его: this.add_child(myActor).
  3. . Затем он вызывает асинхронную трудоемкую функцию * 1011. * который в конце концов что-то делает с myActor.

Вот где я сталкиваюсь с проблемой:

  1. Мы отключаем (запускаем this.destroy()) расширение сразу после включения.

  2. При отключении this.destroy() запускает GObject's this.run_dispose() для сбора мусора. Однако, если this._tcFunction() не завершил работу, он позже попытается получить доступ к myActor, который, возможно, уже был освобожден this.run_dispose().

В одну сторону к go Примерно так: определить логическую переменную в this.destroy()

destroy() {
    this._destroying = true
    // ...
    this.run_dispose;
}

, а затем добавить проверку в this._tcFunction(), например,

async _tcFunction() {
    await this._timeConsumingStuff();

    if (this._destroying === true) { return; }

    myActor.show();

}

Мой вопрос: есть ли лучший способ иметь дело с этими ситуациями? Может быть с Gio.Cancellable()? AFAIK, нет простого способа остановить функцию asyn c в javascript ...

1 Ответ

3 голосов
/ 24 апреля 2020

Во-первых, следует помнить о двух вещах:

  1. Избегайте вызова низкоуровневых функций управления памятью, таких как GObject.run_dispose(), поскольку в библиотеках C есть случаи, когда эти объекты кэшируются для повторного использования и не на самом деле утилизируются, когда вы думаете, что это так. Сигнал утилизации также отсутствует, и другим объектам может потребоваться уведомление.

  2. Избегайте переопределения функций, запускающих удаление, таких как Clutter.Actor.destroy(), и вместо этого используйте сигнал уничтожения. Сигнал обратного вызова GObject всегда получает исходящий объект в качестве первого аргумента, и можно безопасно использовать , что в обратном вызове уничтожения.

Есть несколько способов, которые я мог бы подумать решения этого, но это зависит от ситуации. Если функция asyn c является библиотечной функцией GNOME asyn c, она, вероятно, имеет отменяемый аргумент:

let cancellable = new Gio.Cancellable();

let actor = new Clutter.Actor();
actor.connect('destroy', () => cancellable.cancel());

Gio.File.new_for_path('foo.txt').load_contents_async(cancellable, (file, res) => {
    try {
        let result = file.load_contents_finish(res);

        // This shouldn't be necessary if the operation succeeds (I think)
        if (!cancellable.is_cancelled())
            log(actor.width);
    } catch (e) {
        // We know it's not safe
        if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
            log('it was cancelled');

        // Probably safe, but let's check
        else if (!cancellable.is_cancelled())
            log(actor.width);
    }
});

// The above function will begin but not finish before the
// cancellable is triggered
actor.destroy();

Конечно, вы всегда можете использовать отменяемый с Promise или просто обычная функция / шаблон обратного вызова:

new Promise((resolve, reject) => {
   // Some operation
   resolve();
}).then(result => {
    // Check the cancellable
    if (!cancellable.is_cancelled())
        log(actor.width);
});

Другой вариант - null из вашей ссылки, так как вы можете безопасно проверить это:

let actor = new Clutter.Actor();
actor.connect('destroy', () => {
    actor = null;
});

if (actor !== null)
    log(actor.width);
...