Что мешает DOMTimerCoordinator::NextID войти в бесконечный цикл?

Я изучил кодовую базу Blink, чтобы ответить на этот вопрос о максимально возможном количестве таймеров в JavaScript.

Новые таймеры создаются с помощью DOMTimerCoordinator::InstallNewTimeout(). Он вызывает NextID()< /a> для получения доступного целочисленного ключа. Затем он вставляет новый таймер и соответствующий ключ в timers_.

int timeout_id = NextID();
timers_.insert(timeout_id, DOMTimer::Create(context, action, timeout,
                                            single_shot, timeout_id));

NextID() получает следующий идентификатор в циклической последовательности от 1 до 231-1:

int DOMTimerCoordinator::NextID() {
  while (true) {
    ++circular_sequential_id_;

    if (circular_sequential_id_ <= 0)
      circular_sequential_id_ = 1;

    if (!timers_.Contains(circular_sequential_id_))
      return circular_sequential_id_;
  }
}

Что произойдет, если используются все идентификаторы?
Что мешает NextID() войти в бесконечный цикл?

Весь процесс более подробно объясняется в моем ответе на этот вопрос.


person David    schedule 03.11.2018    source источник
comment
Хотите объяснить отрицательный голос? Я хотел бы улучшить вопрос, если это возможно. Мои знания C++ ограничены, и мне очень интересно понять, как Blink решает эту проблему.   -  person David    schedule 03.11.2018
comment
Из любопытства я скопировал максимально возможное количество таймеров в JavaScript в Google и нашел Перегрузка таймера JavaScript. Это создало у меня впечатление, что он действительно не предназначен для того, чтобы иметь столько таймеров, чтобы идентификаторы могли быть исчерпаны. Кстати. для одиночной обработки и исчерпанных идентификаторов NextID() не будет блокироваться - он будет блокироваться, поскольку он будет бесконечно вращаться, и никакой другой код для запуска таймера не может быть выполнен (не в этом потоке). Когда я писал блокировку, я имел в виду многопоточность, но я не эксперт JS.   -  person Scheff's Cat    schedule 03.11.2018
comment
Чтобы назначить 2^31 - 1 тайм-аут, вероятно, потребуется больше времени, чем максимально возможное значение тайм-аута.   -  person Kaiido    schedule 07.11.2018
comment
@Kaiido - Очень хороший момент. Но они могут быть все таймеры, установленные setInterval(), потому что список является общим для обоих. В этом случае они будут существовать до тех пор, пока пользователь не очистит их.   -  person David    schedule 07.11.2018


Ответы (1)


Мне нужно немного, чтобы понять это, но я верю, что понял.

Это шаги, которые превратили его в смысл для меня.

  1. circular_sequential_id_ используется как уникальный идентификатор. Он не выставлен, но из другой информации я подозреваю, что это int с 32 битами (например, std::int32_t).

  2. Я подозреваю, что circular_sequential_id_ является переменной-членом class (или struct) DOMTimerCoordinator. Следовательно, после каждого вызова NextID() он запоминает последнее возвращенное значение. При вводе NextID() сначала увеличивается circular_sequential_id_:

        ++circular_sequential_id_;
  3. Приращение ++circular_sequential_id_; может рано или поздно вызвать переполнение (ууух. Если я правильно помню, это считается неопределенным поведением, но на самом деле мир он в основном просто обтекает.) и становится отрицательным. Чтобы справиться с этим, следующая строка хороша для:

        if (circular_sequential_id_ <= 0)
          circular_sequential_id_ = 1;
  4. Последний оператор в цикле проверяет, используется ли сгенерированный идентификатор в каком-либо таймере:

        if (!timers_.Contains(circular_sequential_id_))
          return circular_sequential_id_;

    Если он не используется, возвращается идентификатор. В противном случае, сыграй еще раз, Сэм.

Это подводит меня к наиболее разумному ответу:

Да, это может стать бесконечным циклом...

...if 231 - 1 таймер был занят и, следовательно, все идентификаторы были израсходованы.

  1. Я предполагаю, что с 231-1 таймерами у вас есть гораздо более существенные другие проблемы. (В одиночку, представляя себе хранилище, которое может понадобиться этим таймерам, и время, чтобы обработать их все...)

  2. Даже если таймеры 231 - 1 не являются фатальной проблемой, функция может продолжать цикл до тех пор, пока один из таймеров не освободит свой идентификатор и снова не сможет быть занят. Таким образом, NextID() будет блокироваться, если ресурс (свободный идентификатор для таймера) временно недоступен.

Подумав дважды, вариант 2. скорее теоретический. Я не могу поверить, что кто-то будет управлять ограниченными ресурсами таким образом.

Я предполагаю, что этот код работает в предположении, что никогда не будет 231 - 1 таймеров одновременно, и, следовательно, он найдет свободный идентификатор за несколько итераций.

person Scheff's Cat    schedule 03.11.2018
comment
@David Извините, я только что понял, что вы сами описали это в своем другом ответе. Поэтому, пожалуйста, пропустите верхнюю часть. Нижняя часть может быть интересной для вас. (И, OT, но: я начал с C64, но до этого получил несколько уроков от родственного ZX Spectrum...) ;-) - person Scheff's Cat; 03.11.2018
comment
+1. Я согласен со всем. Я не принимаю его сразу в надежде, что эксперт по кодовой базе Blink заглянет и даст авторитетный ответ. (OT - мой лучший друг в то время получил C64; мы оба так гордились нашими машинами!) - person David; 03.11.2018
comment
И ты правильно помнишь. Переполнение целого числа со знаком — это UB. На самом деле Blink использует такие циклические последовательности в нескольких местах кодовой базы, и некоторые были переписаны, чтобы избежать UB. . - person David; 03.11.2018
comment
Я могу ошибаться, но в документации указано, что идентификатор никогда не будет повторяться, и вы несколько раз косвенно упоминаете, что идентификатор может быть освобожден (временно недоступен, одновременные таймеры и т. д.). Могли бы вы объяснить? - person php_nub_qq; 04.11.2018
comment
@php_nub_qq Я не знаю, почему doc. заявляет об этом. (Я даже не знаю этого документа.) Вы уверены, что он действительно говорит никогда не повторять или вместо этого никогда не дублировать или не уникален. Никогда не будет двух таймеров с одинаковым идентификатором одновременно. Судя по открытому коду, я бы сказал, что их можно повторить. С другой стороны, 2^31 - 1 — чертовски большое число. ;-) Не могли бы вы предоставить ссылку на этот документ? - person Scheff's Cat; 04.11.2018
comment
@php_nub_qq Я просмотрел документ. предоставлено Дэвидом: таймеры. Я не нашел такого утверждения никогда не повторяющегося, но оно утверждает, что: есть список активных таймеров. Каждая запись в этих списках идентифицируется номером, который должен быть уникальным в пределах списка в течение всего срока действия. Обратите внимание, что есть также clearTimeout() и clearInterval(). Если таймер удаляется из списка активных таймеров, указанный выше NextID() распознает его идентификатор как свободный при проверке, потому что он просто просматривает этот список. - person Scheff's Cat; 04.11.2018
comment
@php_nub_qq Хммм, согласен: звучит как-то двусмысленно. Кому вы доверяете больше? Док. текст или исходный код? ;-) Хотя, опять же, 2^31 - 1 - чертовски большое число, и у каждого объекта есть свой пул идентификаторов. Вероятность повторения идентификатора довольно мала. Я предполагаю, что браузер вылетит раньше. (Эта вероятность намного выше...) ;-) - person Scheff's Cat; 04.11.2018
comment
@php_nub_qq - У Шеффа очень хорошая интуиция. Официальная спецификация W3C гласит: Каждая запись в этих списках идентифицируется числом, которое должно быть уникальным в своем списке на протяжении всего времени жизни объекта. Блинк-код соответствует спецификации. - person David; 04.11.2018
comment
Опс, пишу с телефона, не видела последних комментариев. Список для обоих типов таймеров общий, вы можете, например, удалить тайм-аут с помощью clearInterval(). - person David; 04.11.2018
comment
Я связался с разработчиками Blink по поводу NextID() но пока у меня нет ответа. Если они ответят, я отредактирую ваш ответ здесь (надеюсь, вы согласны). Большое спасибо за твою помощь! - person David; 07.11.2018