пятница, 16 января 2015 г.

[prog.c++] И еще про короутины. User-Mode Scheduling в 64-битовых Window 7 и выше.

Продолжение темы (часть 1, часть 2, часть 3, часть 4).

К своему стыду не знал, что в Windows 7 появилась весьма интересная штука: User-Mode Scheduling. Между тем, для реализации акторов в виде последовательных функций, а не в виде конечных автоматов, это очень интересная штука.

Как я понял после поверхностного ознакомления, механизм UMS (User-Mode Scheduling) работает по следующему принципу:

Пользователь определяет отдельную нить в качестве UMS-диспетчера. Для выполнения диспетчерских функций пользователь должен написать функцию-диспетчер, указатель на которую передается Windows при переводе нити из разряда обычных нитей в разряд UMS-диспетчеров. Windows будет сама вызывать эту функцию-диспетчер для уведомления о том, что произошло какое-то важное для UMS-диспетчера событие.

Пользователь определяет отдельные UMS-нити, которые будут использоваться в качестве рабочих. Эти нити связываются с UMS-диспетчером. Диспетчеризацией таких нитей занимается не ядро Windows, а UMS-диспетчер.

Привлекательность этого подхода в сравнении с подходом Boost.Fibers (см. презентацию из предыдущей заметки) состоит в том, что рабочие UMS-нити могут использовать обычные Windows-примитивы и обычные системные вызовы Windows. Если UMS-нить блокируется каким-то системным вызовом (например, засыпает не ожидании события/мутекса или на синхронной операции ввода-вывода), то Windows уведомляет об этом UMS-диспетчер. Мол, такая-то из твоих рабочих нитей заблокирована, можно дать управление кому-то другому. Что позволяет UMS-диспетчеру толкнуть на исполнение любую из других подчиненных ему UMS-нитей. Когда ранее заблокированная UMS-нить разблокируется (например, операция ввода-вывода завершилась), то Windows пришлет в UMS-диспетчер уведомление о том, что нить может продолжать свою работу и UMS-диспетчер может либо толкнуть ее на выполнение, либо сохранить в своем списке готовых для запуска нитей.

Получается, что рабочие UMS-нити можно писать точно так же, как и обычные нити: без оглядки на то, что вместо обычного мутекса нужно использовать какой-то псевдомутекс, что синхронные IO-операции дергать нежелательно и т.д. Тогда как при использовании Boost.Fiber нужно об этом всем думать (т.е. задействовать boost::fiber::mutex вместо std::mutex, boost::fiber::promise вместо std::promise и т.д.).

А это означает, что UMS позволяет оформлять обычные последовательные функции, с обычными системными вызовами Windows, в виде акторов, диспетчеризация которых осуществляется посредством своего собственного диспетчера.

Что выглядит очень круто. Но, похоже, непереносимо на другие платформы. Даже на 32-х битовый Windows :)

Комментариев нет: