Документация Engee

Семафоры

Семафор связан с целочисленным значением, которое никогда не должно опускаться ниже нуля. Для семафора sem можно выполнять две операции: увеличивать значение семафора на единицу с помощью post(sem) и уменьшать значение семафора на единицу с помощью wait(sem). Если значение семафора в данный момент равно нулю, вызов wait(sem) будет блокироваться до тех пор, пока значение не станет больше нуля.

Существует два вида семафоров: именованные и анонимные. Именованные семафоры идентифицируются по их имени, которое представляет собой строку вида "/somename". Анонимные семафоры поддерживаются объектами памяти (обычно общей памяти), обеспечивающими необходимое хранение. В пакете Julia InterProcessCommunication семафоры являются экземплярами Semaphore{T}, где T представляет собой String для именованных семафоров и тип объекта резервной памяти для анонимных семафоров.

Именованные семафоры

Именованные семафоры идентифицируются по их имени, которое представляет собой строку вида "/somename". Новый именованный семафор, идентифицируемый строкой name, создается следующим образом:

Semaphore(name, value; perms=0o600, volatile=true) -> sem

при этом создается новый именованный семафор с начальным значением value и возвращается экземпляр Semaphore{String}. Для указания прав доступа (значение по умолчанию предоставляет права на чтение и запись для вызывающего объекта) можно использовать ключевое слово perms. Ключевое слово volatile указывает, должен ли семафор быть отсоединен после финализации возвращаемого объекта.

Другой процесс (или поток) может открыть существующий именованный семафор, вызвав следующее:

Semaphore(name) -> sem

при этом выдается экземпляр Semaphore{String}.

Чтобы отсоединить (удалить) постоянный именованный семафор, просто сделайте следующее:

rm(Semaphore, name)

Если семафор не существует, ошибка игнорируется. Однако для других ошибок выводится ошибка SystemError.

Для максимальной гибкости экземпляр именованного семафора также может быть создан с помощью следующего:

open(Semaphore, name, flags, mode, value, volatile) -> sem

где для flags могут быть заданы биты IPC.O_CREAT и IPC.O_EXCL, mode определяет предоставленные права доступа, value является начальным значением семафора, а volatile представляет собой логическое значение, указывающее, должен ли семафор быть отсоединен после финализации возвращаемого объекта sem. Значения mode и value игнорируются, если открыт существующий именованный семафор.

Анонимные семафоры

Анонимные семафоры поддерживаются объектами памяти (обычно общей памяти), обеспечивающими необходимое хранение.

Новый анонимный семафор создается следующим образом при вызове:

Semaphore(mem, value; offset=0, volatile=true) -> sem

при этом инициализируется анонимный семафор, поддерживаемый объектом памяти mem с начальным значением value, и возвращается экземпляр Semaphore{typeof(mem)} Для указания адреса (в байтах) данных семафора относительно pointer(mem) можно использовать ключевое слово offset. Ключевое слово volatile указывает, должен ли семафор быть уничтожен после финализации возвращаемого объекта.

Количество байтов, необходимое для хранения анонимного семафора, задается с помощью sizeof(Semaphore), а анонимный семафор должен быть выровнен в памяти на величину, кратную размеру слова в байтах (то есть Sys.WORD_SIZE >> 3). Объекты памяти, используемые для хранения анонимного семафора, должны реализовывать два метода: pointer(mem) и sizeof(mem) для получения базового адреса (как экземпляра Ptr) и размера (в байтах) связанной памяти соответственно.

Другой процесс (или поток) может использовать существующий (инициализированный) анонимный семафор, вызвав следующее:

Semaphore(mem; offset=0) -> sem

где mem — объект памяти, обеспечивающий хранение семафора в относительном расположении, заданном ключевым словом offset (по умолчанию — нуль). Возвращаемое значение является экземпляром Semaphore{typeof(mem)}.

Операции с семафорами

Значение и размер семафора

Чтобы запросить значение семафора sem, выполните следующее:

sem[]

Однако следует помнить, что к моменту возвращения результата значение семафора уже может измениться. Минимальные и максимальные значения, которые может принимать семафор, задаются следующим образом:

typemin(Semaphore)
typemax(Semaphore)

Чтобы выделить память для анонимных семафоров, нужно знать количество байтов, необходимых для хранения семафора. Оно задается следующим образом:

sizeof(Semaphore)

Размещение и ожидание

Чтобы разблокировать семафор sem, вызовите следующее:

post(sem)

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

Блокировка семафора sem осуществляется с помощью следующего:

wait(sem)

при этом значение семафора sem уменьшается на единицу. Если значение семафора больше нуля, уменьшение продолжается и функция немедленно возвращается. Если в данный момент семафор имеет нулевое значение, вызов блокируется до тех пор, пока либо не появится возможность выполнить уменьшение (т. е. значение семафора станет больше нуля), либо обработчик сигнала не прервет вызов (в этом случае будет выдан экземпляр исключения InterruptException). В случае непредвиденной ошибки может возникнуть ошибка SystemError.

Чтобы попробовать заблокировать семафор sem без блокировки, сделайте следующее:

trywait(sem) -> boolean

при этом будет предпринята попытка немедленного уменьшения значения (блокировки) семафора с возвращением true в случае успеха. Если уменьшение не может быть выполнено сразу же, возвращается false. В случае прерывания или непредвиденной ошибки возникает исключение (InterruptException или SystemError соответственно).

Наконец, вызов:

timedwait(sem, secs)

уменьшает значение семафора sem (блокирует семафор). Если значение семафора больше нуля, уменьшение продолжается и функция немедленно возвращается. Если в данный момент семафор имеет нулевое значение, вызов блокируется до тех пор, пока либо не появится возможность выполнить уменьшение (т. е. значение семафора станет больше нуля), либо не истечет ограничение в заданное количество secs секунд (в этом случае будет выдан экземпляр ошибки TimeoutError), либо обработчик сигнала не прервет вызов (в этом случае будет выдан экземпляр исключения InterruptException).