Руководство программиста в Photon (1037671), страница 45
Текст из файла (страница 45)
Потоки блокированы до тех пор, пока:
-
Они не получили доступ к библиотеке
-
Они не получили толчок от сигнала
-
Другой поток не выдал сигнал или трансляцию сигналов (broadcasts) переменной состояния.
-
Другой поток не вызвал PtExit(), exit() или _exit().
Функция PtCondTimedWait() похожа на PtCondWait(), но время блокирования ограничено тайм-аутом.
Не-Photon'овские и Photon'овские потоки
Библиотека отслеживает, какие из Ваших потоков являются Photon'овскими (читающими сообщения), а какие – не-Photon'овскими (нечитающими). Таким образом, библиотека всегда знает, сколько Ваших потоков способно получать и обрабатывать события. Эта информация в настоящее время используется только функцией PtModaBlock() (см. раздел "Модальные операции и потоки" ниже).
По умолчанию, поток, вызвавший функцию PtInit(), является читателем событий, а все остальные – нет. Но если нечитающий поток вызывает функцию PtProcessEvent() или PtMainLoop(), он автоматически становится читающим события.
Photon не запускает новых потоков для Вас, если Вы завершили Photon'овские потоки.Вы также можете превратить нечитающий поток в читающий и обратно, передавая флаг в функцию PtEnter() или PtLeave():
Pt_EVENT_PROCESS_ALLOW | Превратить вызывающий поток в читающий события |
PtEVENT_PROCESS_PREVENT | Превратить вызывающий поток в нечитающий. |
Если Вам не требуется изменять состояние потока (например, для не-Photon'овского потока, который никогда не обрабатывает никаких событий), не устанавливайте ни тот, ни другой из этих битов во флагах.
Если Вы вызываете функцию Pt_Leave() в ответной реакции, потому что собираетесь выполнить что-то достаточное длительное по времени, передайте функции PtLeave() признак Pt_EVENT_PROCESS_PREVENT. Это укажет библиотеке, что данный поток не собирается обрабатывать событие довольно значительный промежуток времени. Убедитесь, что передали Pt_EVENT_PROCESS_ALLOW функции PtEnter(), перед тем как выполнить возврат из ответной реакции.
Модальные операции и потоки
Модальная операция – это та, где Вам надо ожидать появления какого-то конкретного события перед тем, как Вы можете начать исполнение – например, когда Вы хотите, чтобы пользователь принял решение и нажал кнопку "Да" или "Нет". Поскольку обычно до того, как появится ожидаемое, обычно придут другие события, Вам надо гарантировать, что они обработаны.
В однопоточном приложении прикрепите ответную реакцию к кнопкам "Да" и "Нет". В этой ответной реакции вызовите PtModalUnblock(). Когда Вы отобразите диалог, вызовите функцию PtModalBlock(). Эта функция запустит петлю обработки событий, похожую на PtMainLoop(), за исключением того, что функция PtModalBlock() возвращает управление, когда что-нибудь (например, ответная реакция, прикреплённая к кнопкам "Да" и "Нет"), вызовет PtModalUnblock().
В многопоточном приложении функция PtModalBlock() может:
-
делать то же, что и однопоточном приложении
или
-
блокироваться на переменной состояния и позволить другим потокам Photon'а обрабатывать события.
По умолчанию функция PtModalBlock() использует переменную состояния, если у Вас имеются какие-либо другие Photon'овские потоки. Она удаляет поток из пула обрабатывающих события потоков, но предотвращает ситуацию, когда запущенная вторая модальная операция в потоке, который запустил петлю в PtModalBlock(), делает невозможным для первой PtModalBlock() вернуть управление до тех пор, пока вторая модальная операция не будет завершена.
В большинстве приложений этого не должно произойти; обычно Вы либо не хотите позволить другие модальные операции до тех пор, пока выполняющаяся в текущий момент не завершится, либо же Вы действительно хотите получить стековый характер действий, когда вторая модальная операция не допускает завершания первой. Например, если первая модальная операция – это файловый селектор, и вторая – это вопрос "Вы уверены, что хотите переписать этот файл?", Вы не хотите позволить пользователю закрыть файловый селектор до ответа на вопрос.
Если Вы знаете, что Ваше приложение не имеет двух несвязанных модальных операций, которые могут выполняться одновременно, но могут завершаться в любом порядке, Вы можете передать признак Pt_EVENT_PROCESS_ALLOW в функцию PtModalBlock(). Это указывает функции PtModalBlock() запустить петлю событий, даже если Вы имеете другие доступные Photon'овские потоки, и может уменьшить общее число Photon'овских потоков, которые нужны Вашему приложению.
Завершение многопоточной программы
Завершение многопоточного приложения может оказаться мудрёным; вызов функции exit() сделает так, что Ваши потоки просто исчезнут, так что Вам следует убедиться, что Вы не завершите работу, пока другой поток делает что-то такое, что нельзя прервать, например, сохраняет файл.
Не вызывайте pthread_exit() в потоке, который запер библиотеки Photon'а. Если вы сделаете это, в Вашем приложении будет утечка памяти.
Помните, что все ответные реакции выполняются потоком, который запер библиотеки.
В приложении Photon'а библиотека может вызвать функцию PtExit(), когда закрывается последнее окно Вашего приложения. Если вы не хотите, чтобы это случилось, пока поток выполняет что-нибудь важное, сбросьте признак Ph_WM_CLOSE в ресурсе Pt_ARG_WINDOW_MANAGER_FLAGS Вашего базового окна и обработайте сообщение о закрытии самостоятельно. Вам также необходимо найти все вызовы exit() или PtExit() в Вашем программном коде и принять меры, чтобы Вы не завершили работу до тех пор, пока это не станет безопасным. Если виджет в Вашем базовом окне имеет ответную реакцию типа Done или Cancel, Вы также должны это обработать.
Библиотека Photon'а предлагает несколько механизмов, чтобы сделать обработку этого типа ситуации проще и безопаснее:
-
Это простой счётчик, который вынуждает блокироваться функцию PtExit() до тех пор, пока он не станет равен нулю.
Функции, предоставляющие этот счётчик, PtPreventExit() и PtAllowExit(), являются не только потоко-безопасными, но также безопасными в смысле реального времени: они гарантируют выполнение ограниченного объёма машинного кода и никогда не генерируют инверсию приоритета.
Этот механизм считается относительно низкоуровневым и предназначен прежде всего для потоков, которые ничего не делают с функциями Photon'а (возможно, временно – т.е. пока находятся внутри секции PtLeave()/PtEnter() ).
Основанием является то, что определённые вызовы функций Photon'а, которые обычно являются блокирующими, просто завершают вызывающий поток, если висит PtExit() (в противном случае функция PtExit() будет потенциально надолго блокировать). Это также случается, когда поток блокируется перед тем, как другой поток вызывает PtExit(); блокированный поток завершается без возвращения из блокированного вызова. Список вызовов Photon'овских функций, которые явялются "летальными" после того, как другой поток вызвал PtExit(), включает попытки обработки событий, выполнение чего-либо модального, блокирования на переменной состояния с использованием функций PtCondWait() или PtCondTimedWait(), или вызовов PtEnter() или PtLeave().
-
Иногда может оказаться трудным гарантировать, чтобы Ваш поток не вызывал ничего из этого после вызова PtPreventExit() – и если это делается и убивается без возможности вызова PtAllowExit(), Ваш процесс будет заперт и Вы должны его убить.
Чтобы избежать подобных ситуаций, имеется флаг Pt_DELAY_EXIT, который Вы можете передать функции PtEnter() или PtLeave(). Выполнение этого не только помешает функциям PtEnter() или PtLeave() завершить Ваш поток, когда другой поток вызовет PtExit(), но также вызовет неявно PtPreventExit(). Если Ваш поток по какой-либо причине помирает, библиотека знает, что для Вас надо вызвать PtAllowExit(). Флаг Pt_DELAY_EXIT делает Вашу ответную реакцию "сохранить файл" столь простой:
my_callback( ... ) {
PtLeave( Pt_DELAY_EXIT );
save_file(); /* Здесь Вы в безопасности… */
PtEnter( 0 ); /* Но это может убить Вас – и это хорошо! */
}
Кроме того, Вы должны обеспечить, чтобы save_file() не пыталась выполнить какие-либо "летальные" вызовы. В частности, Вы не можете поднять всплывающий диалог с сообщением об ошибке, если что-то пошло неправильно. Если Вы хотите поднять всплывающий диалог, который потенциально займёт экран на минуты или часы, Вы должны сделать это перед вызовом PtExit(), например, использованием приёма с Pt_ARG_WINDOW_MANAGER_FLAGS, обсуждённого выше.
Чтобы завершить поток, который выполняет PtMainLoop(), без прекращения работы приложения в целом, вызывайте PtQuitMainLoop().
Не вызывайте функцию PtMainLoop() в потоке, который не запер библиотеки Photon'а.
Если Вы вызываете PtQuitMainLoop() из главного потока Вашего приложения, приложение прекращает свою работу. Чтобы определить, находитесь ли Вы в главном потоке или нет:
-
Вызовите функцию pthread_self() из функции инициализации Вашего приложения и сохраните идентификатор потока в глобальной переменной.
-
Перед вызовом PtQuitMainLoop() вызовите pthread_self() вновь и сравните возвращённый идентификатор потока с глобальной переменной. Если идентификаторы потоков различны, вызов PtQuitMainLoop() будет прекращать выполнение потока, а не приложения.
Потоки и рабочие процедуры
Заметьте следующее относительно потоков и рабочих процедур:
-
Если Вы прикрепляете рабочую процедуру и у Вас имеется более одного читающего [события – Прим. пер.] потока, имеется очень узкое окно, в котором может быть немедленно вызвана рабочая процедура, вместо того чтобы запускать её после того, как иссякнут события.
-
Смешение потоков и рабочих процедур может привести к маленьким проблемам; если один из других потоков добавляет рабочую процедуру в то время, когда другой поток уже находится в ожидании события, рабочая процедура может быть не вызванной до тех пор, пока Вы не получите событие.
Глава 18. Необработанное рисование и мультипликация
В этой главе описывается:
-
Виджет PtRaw
-
Цвет
-
Атрибуты рисования
-
Дуга, эллипсы, многоугольники и прямоугольники
-
Линии, пиксели и массивы пикселей
-
Текст
-
Побитовые образы (bitmaps)
-
Образы (images)
-
Мультипликация
-
Режим рисования напрямую
-
Внеэкранная видеопамять
-
Поддержка альфа-сопряжения [alpha blending – взвешенное наложение смешиваемых цветов. Прим. пер.]
-
поддержка хроматического ключа [Chroma key – средство объявления некоторого цвета видеоизображения "прозрачным". Прим. пер.]
-
Операции расширенного растра
-
Видеорежимы
-
Градиенты
-
Видеоверлей
Виджет PtRaw
Подпрограммы Pg библиотеки Photon'а являются функциями рисования самого низкого уровня. Они используются библиотекой виджета для прорисовки виджета. Вы можете использовать в приложении Photon'а функции Pg, но Вашему приложению придётся:
-
обрабатывать все взаимодействия с пользователем;
-
определять, когда прорисовка повреждена (например, когда она открывается, будучи ранее закрытой, или когда пользователь перемещает окно);
-
восстанавливать прорисовку каждый раз, когда она повреждена.
Вам следует всегда, когда это возможно, использовать виджеты, поскольку они делают всё вышеперечисленное автоматически.
Если Ваше приложение должно выполнять свою собственную прорисовку, Вам следует использовать виджет PtRaw. Он делает следующее:
-
Сообщает приложению, что он получил повреждения
-
Сбрасывает буфер рисования почти всегда, когда это необходимо (Вам придётся самим сбрасывать буфер, например, перед операцией блитирования, т.е. пересылки массива информации большого объёма. Блитирование выполняет смещение прямоугольной области Вашего рисунка на определённое расстояние; Вы можете захотеть, чтобы Ваш рисунок обновился перед тем, как это произойдёт).
Чтобы создать виджет PtRaw в PhAB, щёлкните на его иконке в палитре виджетов:
Разместите его там, где Вы хотите выполнять прорисовку. Вы можете предусмотреть для виджета PtRaw разнообразые функции; они вызываются в порядке, данном ниже, когда виджет реализуется, и затем вызываются по необходимости:
Pt_ARG_RAW_INIT_F | Функция инициализации, которая вызывается перед тем, как вычисляется пространство, занимаемое виджетом. |
Pt_ARG_RAW_EXTENT_F | Будучи предусмотренной, вычисляет пространство виджета, когда тот перемещается или изменяется вразмерах. |
Pt_ARG_RAW_CALC_OPAQUE_F | Вычисляет список затенённых "черепиц" виджета. |
Pt_ARG_RAW_CONNECT_F | Вызывается как последний этап реализации виджета, непосредственно перед тем, как создаются какие-либо требующиеся области. |
Pt_ARG_RAW_DRAW_F | Выполняет прорисовку |
Большую часть времени Вам будет нужно задавать только функцию рисования (см. ниже). Вы можете использовать редактор функций Photon'а (описанный в главе "Редактирование ресурсов и ответных реакций в PhAB'е") для редактирования этих ресурсов – но прежде Вы должны присвовать рисуемому виджету уникальное имя экземпляра. Вы можете также установить эти ресурсы из своего программного кода приложения; более подробно см. раздел "Ресурсы функций" в главе "Управление ресурсами в программном коде приложения". Информацию по ресурсам PtRaw'а см. в "Справочнике виджетов Photon'а".
Функция необработанного рисования
Когда Вы создаёте виджет PtRaw в PhAB'е и приметесь редактировать его функцию Pt_ARG_RAW_DRAW_F, Вы увидите предлагаемый по умолчанию код такого вида:
void my_raw_draw_fn( PtWidget_t *widget, PhTile_t *damage ) {