uIP 1.0
Группы | Файлы
Библиотека протопотоков (Protothreads)

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

Группы

 Локальные продолжения (Local continuations, далее LC)
 

LC являются базой для реализации протопотоков.


Файлы

файл  pt.h
 

Реализация protothread-ов.


Initialization

#define PT_INIT(pt)
 Инициализация protothread.

Декларация и определение

#define PT_THREAD(name_args)
 Декларация protothread.
#define PT_BEGIN(pt)
 Декларирует начало protothread внутри функции C, реализующей protothread.
#define PT_END(pt)
 Декларируется конец для protothread.

Блокирующее ожидание.

#define PT_WAIT_UNTIL(pt, condition)
 Блокирует выполнения и ждет, пока условие не станет true.
#define PT_WAIT_WHILE(pt, cond)
 Блокировка и ожидание, пока условие true.

Иераржия protothread-ов

#define PT_WAIT_THREAD(pt, thread)
 Блокирует и ждет, пока не завершится дочерний protothread.
#define PT_SPAWN(pt, child, thread)
 Порождает дочерний protothread и ждет выхода из него.

Выход и рестарт

#define PT_RESTART(pt)
 Делает рестарт для protothread.
#define PT_EXIT(pt)
 Выход из protothread.

Вызов protothread

#define PT_SCHEDULE(f)
 Шедулинг для protothread.

Уступка контекста (Yielding) из protothread

#define PT_YIELD(pt)
 Уступка контекста управления (Yield) из текущего protothread.
#define PT_YIELD_UNTIL(pt, cond)
 Делает уступку процессорного времени из protothread, пока не не будет выполнено условие.

Подробное описание

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

Протопотоки предоставляют линейное выполнение кода для событийно-управляемых систем, реализованных на языке C. Протопотоки могут использоваться как совместно с RTOS, так и без нее.

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

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

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

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

Главные особенности:

Примеры приложений, где можно использовать:

API протопотоков состоит из 4 базовых операций. Это инициализация PT_INIT(), выполнение PT_BEGIN(), блокирование на условии PT_WAIT_UNTIL() и выход PT_END(). Кроме того для удобства есть еще 2 функции блокировка по обратному условию PT_WAIT_WHILE() и блокировка на протопотоке PT_WAIT_THREAD().

См. также:
Документация Protothread API

Protothread-библиотека опубликована под лицензией в стиле BSD, которая позволяет как некоммерческое, так и коммерческое использование. Единственное требование - указать о том, кто автор.

Авторы

Protothread-библиотека была написана Adam Dunkels <adam@sics.se> с поддержкой Oliver Schmidt <ol.sc@web.de>.

Protothread-ы

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

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

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

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

Протопотоки похожи на асимметричные сопрограммы (co-routines). Главное отличие от сопрограмм в том, что сопрограммы используют стек для каждой сопрограммы, а протопотоки не используют для себя отдельный стек. Наиболее похожий механизм, как у протопотоков, есть у генераторов Python. У них тоже безстековая конструкция, только другое предназначение. Протопотоки предоставляют блокировки контекста внутри функции C, а генераторы Python предоставляют несколько точек выхода из функции генератора.

Локальные переменные

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

Шедулинг (планировщик задач) протопотоков

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

Реализация

Протопотоки реализованы с использованием локальных продолжений. Локальное продолжение представляет текущее состояние выполнения в отдельном месте программы, но не предоставляет какую-либо историю вызовов или локальные переменные. Локальное продолжение может быть установлено в отдельной функции для захвата состояния функции. После того, как локальное продолжение установлено, оно может быть продолжено в виде восстановления состояния функции в той точке, где локальное продолжение было установлено. Прим. переводчика: звучит конечно как бред, но кое-что станет понятно, если посмотрите код макросов протопотоков, и как они используются - например в сетевом приложении hello-world, которое построено на протопотоке.

Локальное продолжение может быть реализовано несколькими способами:

  1. с помощью привязанного к архитектуре кода на ассемблере,
  2. с помощью стандартных конструкций C, или
  3. с помощью расширений компилятора.

Первый способ работает путем сохранения и восстановления состояния процессора, за исключением указателей стека, и требует от 16 до 32 байт на протопоток. Точное количество памяти зависит от используемой архитектуры процессора.

Реализация на стандартном C требует только 2 байта на протопоток под сохранение состояния, и задействует оператор C switch() неочевидным способом. Эта реализация вводит, однако, небольшое ограничение для кода, который использует протопотоки - сам код не может использовать операторы switch().

У определенных компиляторов есть расширения C, которые можно использовать для реализации протопотоков. GCC поддерживает указатели-метки, которые могут использоваться для этой цели. С такой реализацией протопотоки потребуют 4 байта RAM на один протопоток.


Макросы

#define PT_BEGIN (   pt)

Декларирует начало protothread внутри функции C, реализующей protothread.

Этот макрос используется для декларирования стартовой точки для protothread. Она должна быть размещена в начале функции, в которой запускается protothread. Все операторы C до вызова PT_BEGIN() будут выполнены каждый раз, когда назначается шедулинг для protothread.

Аргументы:
ptУказатель на protothread control structure.
Примеры:
dhcpc.c.

См. определение в файле pt.h строка 114

#define PT_END (   pt)

Декларируется конец для protothread.

Этот макрос используется для декларирования места, где заканчивается protothread. Он должен всегда использоваться совместно с соответствующим парным вызовом макроса PT_BEGIN().

Аргументы:
ptУказатель на protothread control structure.
Примеры:
dhcpc.c.

См. определение в файле pt.h строка 127

#define PT_EXIT (   pt)

Выход из protothread.

Этот макрос производит выход из protothread. Если protothread был порожден другим protothread, то родительский protothread станет разблокированным и сможет продолжить работу.

Аргументы:
ptУказатель на protothread control structure.

См. определение в файле pt.h строка 247

#define PT_INIT (   pt)

Инициализация protothread.

Инициализирует protothread. Инициализация должна быть выполнена до начала выполнения protothread.

Аргументы:
ptУказатель на protothread control structure (структура управления).
См. также:
PT_SPAWN()
Примеры:
dhcpc.c.

См. определение в файле pt.h строка 81

#define PT_RESTART (   pt)

Делает рестарт для protothread.

Этот макрос заблокирует и перезапустит работающий protothread с того места, где стоит вызов PT_BEGIN().

Аргументы:
ptУказатель на protothread control structure.
Примеры:
dhcpc.c.

См. определение в файле pt.h строка 230

#define PT_SCHEDULE (   f)

Шедулинг для protothread.

Эта функция осуществляет шедулинг для protothread. Возвращаемое значение функции не рано 0, если protothread работает, или 0, если protothread сделал выход.

Аргументы:
fВызов C-функции, которая реализует шедулинг для protothread.

См. определение в файле pt.h строка 271

#define PT_SPAWN (   pt,
  child,
  thread 
)

Порождает дочерний protothread и ждет выхода из него.

Этот макрос порождает дочерний protothread и ждет, пока он не выполнит выход. Макрос может быть использован только в пределах protothread. macro can only be used within a protothread.

Аргументы:
ptУказатель на protothread control structure.
childУказатель на protothread control structure дочернего protothread.
threadДочерний protothread с аргументами.

См. определение в файле pt.h строка 207

#define PT_THREAD (   name_args)

Декларация protothread.

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

Аргументы:
name_argsИмя и аргументы функции C, реализующей protothread.
Примеры:
dhcpc.c и smtp.c.

См. определение в файле pt.h строка 100

#define PT_WAIT_THREAD (   pt,
  thread 
)

Блокирует и ждет, пока не завершится дочерний protothread.

Этот макрос запускает шедулинг дочернего protothread. Текущий protothread будет заблокирован, пока дочерний protothread не завершится.

Заметки:
Дочерний protothread должен быть инициализирован вручную функцией PT_INIT() до того, как будет использована функция PT_SPAWN.
Аргументы:
ptУказатель на protothread control structure.
threadДочерний protothread с аргументами.
См. также:
PT_SPAWN()

См. определение в файле pt.h строка 192

#define PT_WAIT_UNTIL (   pt,
  condition 
)

Блокирует выполнения и ждет, пока условие не станет true.

Этот макрос блокирует выполнение protothread, пока указанное условие не станет равным true.

Аргументы:
ptУказатель на protothread control structure.
conditionУсловие.
Примеры:
dhcpc.c.

См. определение в файле pt.h строка 148

#define PT_WAIT_WHILE (   pt,
  cond 
)

Блокировка и ожидание, пока условие true.

Эта функция блокирует выполнение и ждет, пока условие в состоянии true. См. также PT_WAIT_UNTIL().

Аргументы:
ptУказатель на protothread control structure.
condУсловие.

См. определение в файле pt.h строка 167

#define PT_YIELD (   pt)

Уступка контекста управления (Yield) из текущего protothread.

Эта функция уступает процессорное время этого protothread, что позволяет другим protothread выполняться в системе.

Аргументы:
ptУказатель на protothread control structure.
Примеры:
dhcpc.c.

См. определение в файле pt.h строка 290

#define PT_YIELD_UNTIL (   pt,
  cond 
)

Делает уступку процессорного времени из protothread, пока не не будет выполнено условие.

Аргументы:
ptУказатель на protothread control structure.
condУсловие.

Эта функция будет приводить к уступке процессорного времени для этого protothread, пока указанное условие вычисляется как true.

См. определение в файле pt.h строка 312