Примитивы синхронизации

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

Как программист ядра OS X, у Вас есть много выбора механизмов синхронизации в вашем распоряжении. Само ядро обеспечивает два таких механизма: блокировки и семафоры.

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

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

В дополнение к блокировкам и семафорам, определенные низкоуровневые примитивы синхронизации как тест и набор также доступны, вместе со многими другими атомарными операциями. Эти дополнительные операции описаны в libkern/gen/OSAtomicOperations.c в источниках ядра. Если Вам не нужно что-то столь же устойчивое как законченная блокировка или семафор, такие атомарные операции могут быть полезными. Так как они не общие механизмы синхронизации, однако, они выходят за рамки этой главы.

Семафоры

Семафоры и блокировки подобны, за исключением того, что с семафорами, больше чем один поток может делать данную работу сразу. Семафоры обычно используются при защите многократных неясных ресурсов. Например, Вы могли бы использовать семафор, чтобы препятствовать тому, чтобы очередь переполнила своих границ.

OS X использует традиционные семафоры подсчета, а не двоичные семафоры (которые являются по существу блокировками). Семафоры Маха повинуются Меза-семантике — т.е. когда поток пробужден семафором, становящимся доступным, это сразу не выполняется. Это представляет потенциал для исчерпания ресурсов в многопроцессорных ситуациях, когда система находится под низкой полной нагрузкой, потому что другие потоки могли продолжить опускать семафор, прежде чем просто разбуженный поток получит шанс работать. Это - что-то, что необходимо рассмотреть тщательно при записи приложений с семафорами.

Семафоры могут использоваться любое место, где могут произойти взаимные исключения. Это устраняет их использование в обработчиках прерываний или в контексте планировщика и делает, это строго препятствовало в системе VM. Общедоступный API для семафоров разделен между сгенерированным MIG task.h файл (расположенный в Вашей сборке выводит каталог, включенный с #include <mach/task.h>) и osfmk/mach/semaphore.h (included with #include <mach/semaphore.h>).

Общедоступный семафорный API включает следующие функции:

kern_return_t semaphore_create(task_t task, semaphore_t *semaphore,
    int policy, int value)
kern_return_t semaphore_signal(semaphore_t semaphore)
kern_return_t semaphore_signal_all(semaphore_t semaphore)
kern_return_t semaphore_wait(semaphore_t semaphore)
kern_return_t semaphore_destroy(task_t task, semaphore_t semaphore)
kern_return_t semaphore_signal_thread(semaphore_t semaphore,
    thread_act_t thread_act)

которые описаны в <mach/semaphore.h> или xnu/osfmk/mach/semaphore.h (за исключением создают и уничтожают, которые описаны в <mach/task.h>.

Использование этих функций является относительно прямым за исключением semaphore_create, semaphore_destroy, и semaphore_signal_thread вызовы.

value и semaphore параметры для semaphore_create точно, что Вы ожидали бы — указатель на семафорную структуру быть переполненными и начальное значение для семафора, соответственно.

task параметр относится к основной задаче Маха, которой будет «принадлежать» блокировка. Этой задачей должна быть та, которая в конечном счете ответственна за последующее уничтожение семафора. task параметр, используемый при вызове semaphore_destroy должен соответствовать тот, используемый, когда он создавался.

Для коммуникации в ядре, task параметр должен быть результатом вызова к current_task. Для синхронизации с пользовательским процессом необходимо определить базовую задачу Маха для того процесса путем вызова current_task на стороне ядра и mach_task_self на стороне приложения.

task_t current_task(void);  // returns the kernel task port
task_t mach_task_self(void);// returns the task port of the current  thread

policy параметр передается как политика для очереди ожидания, содержавшей в семафоре. Возможные значения определяются в osfmk/mach/sync_policy.h. Текущие возможные значения:

Политика FIFO, как имя предполагает, метод «первым пришел - первым вышел». Фиксированные приоритетные причины политики ожидают очередь, переупорядочивающая на основе фиксированных приоритетных политик потока. Предварительно проводить политика вызывает semaphore_signal функционируйте для не постепенного увеличения счетчика, если никакие потоки не ожидают на очереди. Эта политика необходима для создания условных переменных (где поток, как ожидают, всегда будет ожидать, пока не сообщено). Посмотрите раздел Wait Queues и Wait Primitives для получения дополнительной информации.

semaphore_signal_thread вызовите берет определенный поток от очереди ожидания и помещает его назад в одну из ожидать-очередей планировщика, таким образом делая тот поток доступным, чтобы быть запланированным для выполнения. Если thread_act NULL, первый поток в очереди так же сделан выполнимым.

За исключением semaphore_create и semaphore_destroy, эти функции могут также быть вызваны от пространства пользователя через RPC. Посмотрите Вызов RPC От Пользовательских приложений для получения дополнительной информации.

Условные переменные

Часть BSD OS X обеспечивает msleep, wakeup, и wakeup_one, которые эквивалентны условным переменным с добавлением дополнительного тайм-аута. Можно найти эти функции в sys/proc.h в заголовках платформы Ядра.

msleep(void *channel, lck_mtx_t *mtx, int priority, const char *wmesg,  struct  timespec *timeout);
msleep0(vvoid *channel, lck_mtx_t *mtx, int priority, const char  *wmesg, uint64_t  deadline);
wakeup(void *channel);
wakeup_one(void *channel);

msleep вызов подобен условной переменной. Это помещает поток для сна до wakeup или wakeup_one обращен тот канал. В отличие от условной переменной, однако, можно установить тайм-аут, измеренный в тактах системных часов. Это означает, что это - и вызов синхронизации и задержка. Прототипы следуют:

msleep(void *channel, lck_mtx_t *mtx, int priority, const char *wmesg,  struct  timespec *timeout);
msleep0(vvoid *channel, lck_mtx_t *mtx, int priority, const char  *wmesg, uint64_t  deadline);
wakeup(void *channel);
wakeup_one(void *channel);

Три вызова сна подобны кроме механизма, используемого для тайм-аутов. Функция msleep0 не рекомендуется для общего использования.

В этих функциях, channel уникальный идентификатор, представляющий единственное условие, на которое Вы ожидаете. Обычно, когда msleep используется, Вы ожидаете для разнообразия для появления в структуре данных. В таких случаях распространено использовать адрес той структуры данных как значение для channel, поскольку это гарантирует, что никакой код в другом месте в системе не будет использовать то же значение.

priority параметр имеет три эффекта. Во-первых, когда wakeup вызывается, потоки вставляются в очередь планирования в этом приоритете. Во-вторых, если бит (priority & PCATCH) установлен, msleep0 не позволяет сигналам прервать сон. В-третьих, если бит (priority & PDROP) нуль, msleep0 отбрасывает взаимное исключение на сне и повторно получает его после пробуждения. Если (priority & PDROP) один, msleep0 отбрасывает взаимное исключение, если оно должно спать, но не повторно получает его.

subsystem параметром является строка краткого текста, представляющая подсистему, ожидающую на этом канале. Это используется исключительно для отладки целей.

timeout параметр используется для установки максимального времени ожидания. Поток может проснуться раньше, однако, если wakeup или wakeup_one вызывается на надлежащем канале. Если сигнал получен, в зависимости от значения, это может также проснуться раньше priority. В случае msleep0, это дано как Мах abstime срок. В случае msleep, это дано в относительное время (секунды и наносекунды).

Вне части BSD ядра условные переменные могут быть реализованы с помощью семафоров.

Блокировки

OS X (и Мах в целом) имеет три основных типа блокировок: спин-блокировки, взаимные исключения и блокировки чтения-записи. Каждый из них имеет различное использование и различные проблемы. Существует также много других типов блокировок, не реализованных в OS X, таком как сон вращения блокирует, некоторые из которых могут быть полезными для реализации в целях сравнения производительности.

Спин-блокировки

Спин-блокировка является самым простым типом блокировки. В системе с инструкцией теста-и-набора или эквивалентом, выглядит примерно так код:

while (test_and_set(bit) != 0);

Другими словами, пока блокировка не доступна, она просто «вращается» в жестком цикле, продолжающем проверять блокировку, пока квант времени потока не истекает, и следующий поток начинает выполняться. Так как весь квант времени для первого потока должен завершиться, прежде чем следующий поток может выполнить и (возможно) выпустить блокировку, спин-блокировка очень расточительна из процессорного времени и должна использоваться только в местах, где взаимное исключение не может использоваться, такой как в аппаратном обработчике исключений или низкоуровневом обработчике прерываний.

Обратите внимание на то, что поток может не блокировать при содержании спин-блокировки, потому что это могло вызвать мертвую блокировку. Далее, в то время как спин-блокировка сохранена, вытеснение отключено на данном процессоре.

Существует три основных типа спин-блокировок, доступных в OS X: lck_spin_t (который заменяет simple_lock_t), usimple_lock_t, и hw_lock_t. Вы строго призваны не использовать hw_lock_t; это только упоминается ради полноты. Из них, только lck_spin_t доступно от расширений ядра.

u в usimple стенды для унипроцессора, потому что они - единственные спин-блокировки, обеспечивающие фактическую блокировку в однопроцессорных системах. Традиционные простые блокировки, в отличие от этого, отключают вытеснение, но не вращаются в однопроцессорных системах. Обратите внимание на то, что в большинстве контекстов, не полезно вращаться в однопроцессорной системе, и таким образом Вам обычно только нужны простые блокировки. Использование блокировок usimple допустимо для синхронизации между контекстом потока и контекстом прерывания или между унипроцессором и интеллектуальным устройством. Однако в большинстве случаев взаимное исключение является лучшим выбором.

Функции спин-блокировки, доступные для расширений ядра, состоят из следующего:

 extern lck_spin_t     *lck_spin_alloc_init(
          lck_grp_t     *grp,
          lck_attr_t     *attr);
 
 extern void lck_spin_init(
          lck_spin_t     *lck,
          lck_grp_t     *grp,
          lck_attr_t     *attr);
 
 extern void lck_spin_lock(
          lck_spin_t     *lck);
 
 extern void lck_spin_unlock(
          lck_spin_t     *lck);
 
 extern void lck_spin_destroy(
          lck_spin_t     *lck,
          lck_grp_t     *grp);
 
 extern void lck_spin_free(
          lck_spin_t     *lck,
          lck_grp_t     *grp);
 
 extern wait_result_t lck_spin_sleep(
          lck_spin_t     *lck,
          lck_sleep_action_t     lck_sleep_action,
          event_t     event,
          wait_interrupt_t     interruptible);
 
 extern wait_result_t lck_spin_sleep_deadline(
          lck_spin_t     *lck,
          lck_sleep_action_t     lck_sleep_action,
          event_t     event,
          wait_interrupt_t     interruptible,
          uint64_t     deadline);

Прототипы для этих блокировок могут быть найдены в <kern/locks.h>.

Параметры этим функциям описаны подробно в Использовании Функций Блокировки.

Взаимные исключения

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

Взаимные исключения более эффективны, чем спин-блокировки в большинстве целей. Однако они менее эффективны в многопроцессорных средах, где ожидаемое время удержания блокировки относительно коротко. Если среднее время относительно коротко, но иногда долго, блокировки вращения/сна могут быть лучшим выбором. Несмотря на то, что OS X не поддерживает вращение/сон, привязывает ядро, они могут быть легко реализованы поверх существующих примитивов блокировки. Если Ваша производительность кода улучшается в результате использования таких блокировок, однако, необходимо, вероятно, искать способы реструктурировать код, такой как использование больше чем одной блокировки или перемещение в блокировки чтения-записи, в зависимости от природы рассматриваемого кода. Посмотрите Блокировки Вращения/Сна для получения дополнительной информации.

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

По подобной причине не разумно блокировать в планировщике. Кроме того, блокирование в системе VM может легко вести, чтобы зайти в тупик, если блокировка, которой Вы ожидаете, сохранена задачей, разбитой на страницы.

Однако в отличие от простых блокировок, допустимо блокировать при содержании взаимного исключения. Это произошло бы, например, если бы Вы взяли одну блокировку, затем попытались взять другого, но вторая блокировка была сохранена другим потоком. Однако это обычно не рекомендуется, если Вы тщательно не тщательно исследуете все использование того взаимного исключения для возможного проспекта, ожидает, поскольку это может привести к мертвой блокировке. Можно избежать, чтобы это всегда взятием привязало определенный порядок.

В целом блокирование при содержании взаимного исключения, определенного для кода, прекрасно, пока Вы записали свой код правильно, но блокирование при содержании более глобального взаимного исключения, вероятно, не, так как Вы не можете быть в состоянии гарантировать, что код других разработчиков соблюдает те же правила упорядочивания.

Взаимное исключение Маха имеет тип mutex_t. Функции, воздействующие на взаимные исключения, включают:

lck_mtx_t           *lck_mtx_alloc_init(lck_grp_t       *grp,
                                        lck_attr_t      *attr);
extern void         lck_mtx_init(       lck_mtx_t       *lck,
                                        lck_grp_t       *grp,
                                        lck_attr_t      *attr);
 
extern void         lck_mtx_lock(   lck_mtx_t           *lck);
 
extern void         lck_mtx_unlock( lck_mtx_t           *lck);
 
extern void         lck_mtx_destroy(lck_mtx_t           *lck,
                                    lck_grp_t           *grp);
 
extern void         lck_mtx_free(   lck_mtx_t           *lck,
                                    lck_grp_t           *grp);
 
extern wait_result_tlck_mtx_sleep(  lck_mtx_t           *lck,
                                    lck_sleep_action_t  lck_sleep_action,
                                    event_t             event,
                                    wait_interrupt_t    interruptible);
 
extern wait_result_tlck_mtx_sleep_deadline(
                                    lck_mtx_t           *lck,
                                    lck_sleep_action_t  lck_sleep_action,
                                    event_t             event,
                                    wait_interrupt_t    interruptible,
                                    uint64_t            deadline);
 
extern void         lck_mtx_assert( lck_mtx_t           *lck,
                                    unsigned int        type);

как описано в <kern/locks.h>.

Параметры этим функциям описаны подробно в Использовании Функций Блокировки.

Блокировки чтения-записи

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

Блокировка чтения-записи позволяет это совместное использование путем осуществления следующих ограничений:

  • В любое время многократные читатели могут содержать блокировку.

  • Только один писатель может содержать блокировку в любой момент времени.

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

  • Читатели, поступающие, в то время как писатель ожидает для получения блокировки, блокируют, пока писатель не получил и выпустил блокировку.

Первое ограничение позволяет совместное использование чтения. Второе ограничение предотвращает совместное использование записи. Третье предотвращает совместное использование чтения-записи, и четвертое предотвращает исчерпание ресурсов писателя непрекращающимся потоком входящих читателей.

Блокировки чтения-записи Маха также предоставляют возможность для читателя для становления писателем и наоборот. В блокировке терминологии обновление - когда читатель становится писателем, и снижение - когда писатель становится читателем. Для предотвращения мертвой блокировки некоторые дополнительные ограничения должны быть добавлены для обновлений и снижений:

  • Обновления одобрены по писателям.

  • Вторые и последующие параллельные обновления перестанут работать, заставляя что блокировка чтения потока быть выпущенными.

Первое ограничение необходимо, потому что читатель, запрашивающий обновление, содержит блокировку чтения, и писатель не был бы в состоянии получить блокировку записи, пока читатель не выпускает ее блокировку чтения. В этом случае читатель и писатель ожидали бы друг друга навсегда. Второе ограничение необходимо для предотвращения мертвой блокировки, которая произошла бы, если два читателя ожидают другого для выпуска ее блокировки чтения так, чтобы могло произойти обновление.

Функции, воздействующие на блокировки чтения-записи:

extern lck_rw_t *lck_rw_alloc_init(
            lck_grp_t               *grp,
            lck_attr_t              *attr);
 
extern void lck_rw_init(
            lck_rw_t                *lck,
            lck_grp_t               *grp,
            lck_attr_t              *attr);
 
 
 
extern void lck_rw_lock(
            lck_rw_t                *lck,
            lck_rw_type_t   lck_rw_type);
 
extern void lck_rw_unlock(
            lck_rw_t                *lck,
            lck_rw_type_t   lck_rw_type);
 
extern void lck_rw_lock_shared(
            lck_rw_t                *lck);
 
extern void lck_rw_unlock_shared(
            lck_rw_t                *lck);
 
extern void lck_rw_lock_exclusive(
            lck_rw_t                *lck);
 
extern void lck_rw_unlock_exclusive(
            lck_rw_t                *lck);
 
extern void lck_rw_destroy(
            lck_rw_t                *lck,
            lck_grp_t               *grp);
 
extern void lck_rw_free(
            lck_rw_t                *lck,
            lck_grp_t               *grp);
 
extern wait_result_t lck_rw_sleep(
            lck_rw_t                        *lck,
            lck_sleep_action_t      lck_sleep_action,
            event_t                         event,
            wait_interrupt_t        interruptible);
 
extern wait_result_t lck_rw_sleep_deadline(
            lck_rw_t                        *lck,
            lck_sleep_action_t      lck_sleep_action,
            event_t                         event,
            wait_interrupt_t        interruptible,
            uint64_t                        deadline);
 

Это - более сложный интерфейс, чем тот из других механизмов блокировки, и фактически является интерфейсом, на котором создаются другие блокировки.

Функции lck_rw_lock и lck_rw_unlock заблокируйте и разблокируйте блокировку или как совместно использованную (чтение) или как монопольный (запись), в зависимости от значения lck_rw_type., который может содержать также LCK_RW_TYPE_SHARED или LCK_RW_TYPE_EXCLUSIVE. Вы должны всегда быть осторожными при использовании этих функций, как разблокирование блокировки, сохраненной в совместно используемом режиме с помощью монопольного вызова, или наоборот приведете к неопределенным результатам.

Параметры этим функциям описаны подробно в Использовании Функций Блокировки.

Блокировки вращения/Сна

Блокировки вращения/сна не реализованы в ядре OS X. Однако они могут быть легко реализованы поверх существующих блокировок при желании.

Если коротко, ожидает в многопроцессорных системах, количество времени, проведенное в контекстном переключении, может быть больше, чем количество времени, проведенное, вращаясь. Когда время потратило вращение, в то время как ожидание блокировки становится больше, чем контекстное переключение наверху, однако, взаимные исключения становятся более эффективными. Поэтому, если существует значительная степень изменения во время ожидания на, высоко спорил блокировка, блокировки вращения/сна могут быть более эффективными, чем традиционные спин-блокировки или взаимные исключения.

Идеально, программа должна быть записана таким способом, которым потраченное содержание времени блокировки всегда о том же, и выбор блокировки ясен. Однако в некоторых случаях это не практично для, высоко спорил блокировка. В тех случаях можно рассмотреть использование блокировок вращения/сна.

Основной принцип блокировок вращения/сна прост. Поток берет блокировку, если это доступно. Если блокировка не доступна, поток может ввести цикл вращения. После определенного периода времени (обычно часть кванта времени или небольшого количества квантов времени), достигнут тайм-аут подпрограммы вращения, и это возвращает отказ. В той точке блокировка помещает поток ожидания в очередь и помещает его для сна.

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

Если коротко, ожидайте периоды на многопроцессорных компьютерах, блокировка вращения/сна более эффективна, чем взаимное исключение и примерно так же эффективна как стандартная спин-блокировка. Для дольше ожидают периоды, блокировка вращения/сна значительно более эффективна, чем спин-блокировка и только немного менее эффективна, чем взаимное исключение. Существует период около перехода между вращением и сном, в котором блокировка вращения/сна может вести себя значительно хуже, чем любой из основных типов блокировки, как бы то ни было. Таким образом блокировки вращения/сна не должны использоваться, если с блокировкой в большой степени не спорят и имеет широко переменные времена хранения. Когда возможно, необходимо переписать код для предотвращения таких проектов.

Используя функции блокировки

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

Первая вещь, которую необходимо сделать при выделении блокировок, состоит в том, чтобы выделить группу блокировки и набор атрибута блокировки. Группы блокировки используются, чтобы назвать блокировки для отладки целей и сгруппировать блокировки функцией для общей понятности. Наборы атрибута блокировки позволяют Вам устанавливать флаги, изменяющие поведение блокировки.

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

Перечисление 17-1  , Выделяющее атрибуты блокировки и группы (снятый подробно с kern_time.c)

lck_grp_attr_t *tz_slock_grp_attr;
lck_grp_t *tz_slock_grp;
lck_attr_t *tz_slock_attr;
lck_spin_t *tz_slock;
 
/* allocate lock group attribute and group */
tz_slock_grp_attr = lck_grp_attr_alloc_init();
lck_grp_attr_setstat(tz_slock_grp_attr);
 
tz_slock_grp =  lck_grp_alloc_init("tzlock", tz_slock_grp_attr);
 
/* Allocate lock attribute */
tz_slock_attr = lck_attr_alloc_init();
//lck_attr_setdebug(tz_slock_attr); // set the debug flag
//lck_attr_setdefault(tz_slock_attr); // clear the debug flag
 
/* Allocate the spin lock */
tz_slock = lck_spin_alloc_init(tz_slock_grp, tz_slock_attr);

Первый параметр инициализатору блокировки, типа lck_grp_t, группа блокировки. Это используется для отладки целей, включая профилирование конкуренции за блокировку. Подробные данные трассировки блокировки выходят за рамки этого документа, однако, каждая блокировка должна принадлежать группе (даже если та группа содержит только одну блокировку).

Второй параметр инициализатору блокировки, типа lck_attr_t, содержит атрибуты для блокировки. В настоящее время единственный доступный атрибут является отладкой блокировки. Этот атрибут может быть установлен с помощью lck_attr_setdebug и очищенный с lck_attr_setdefault.

Для избавления от блокировки Вы просто вызываете соответствующие свободные функции. Например:

lck_spin_free(tz_slock, tz_slock_grp);
lck_attr_free(tz_slock_attr);
lck_grp_free(tz_slock_grp);
lck_grp_attr_free(tz_slock_grp_attr);

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

extern wait_result_t lck_spin_sleep(
                lck_rspin_t         *lck,
                lck_sleep_action_t  lck_sleep_action,
                event_t             event,
                wait_interrupt_t    interruptible);
 
extern wait_result_t lck_spin_sleep_deadline(
                lck_spin_t          *lck,
                lck_sleep_action_t  lck_sleep_action,
                event_t             event,
                wait_interrupt_t    interruptible,
                uint64_t            deadline);

Параметр lck_sleep_action средства управления, предъявят ли в отношении блокировки претензии после сна до этого функционального возврата. Допустимые опции:

LCK_SLEEP_DEFAULT

Выпустите блокировку при ожидании события, затем предъявите претензии в отношении него. Блокировки чтения-записи сохранены в том же режиме, как они были первоначально сохранены.

LCK_SLEEP_UNLOCK

Выпустите блокировку и возвратитесь с удовлетворенной блокировкой.

LCK_SLEEP_SHARED

Предъявите претензии в отношении блокировки в совместно используемом режиме (чтение-запись блокирует только).

LCK_SLEEP_EXCLUSIVE

Предъявите претензии в отношении блокировки в монопольном режиме (чтение-запись блокирует только).

event параметр может быть любым произвольным целым числом, но это должно быть уникально через систему. Для обеспечения уникальности общая практика программирования должна использовать адрес глобальной переменной (часто та, содержащая блокировку) как значение события. Для получения дополнительной информации об этих событиях посмотрите Событие и Тимера Уэйтса.

Параметр interruptible указывает, должен ли планировщик позволить ожиданию быть прерванным асинхронными сигналами. Если это будет ложью, то любые ложные следы приведут к процессу, засыпающему сразу назад (за исключением сигнала истечения срока таймера, который все еще проснется lck_spin_sleep_deadline).