Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Система, управляемая событиями и SST(super-simple tasker)
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Страницы: 1, 2, 3, 4, 5
brag
Горбатый стиль работы с очередью (или сама очередь горбатая - гибрид):
Код
queue.setOnReadyRead(readyRead);
....
void readyRead(){
   char buffer[1024];
   while(true){
       int len = queue.read(buffer,sizeof(buffer);
       if(len<=0)break;
       // process the data
       process(buffer, len);
    }
}


Нормальный асинхронный стиль:
Код
queue.setReceiver(receiver);
....
void receiver(const char* data, size_t len){
   process(buffer, len);
   queue.signal_receiverCompleted(); // не обязательно, но иногда очень полезно
}
psL
Цитата(brag @ Sep 10 2016, 08:21) *
Кста посмотрел, стека у меня выделяeтся 2мб (ничего не менял). Итого получается, что 1000 потоков сожрали всю память и линукс сдох, помог только reset.

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

Т.е. для имитации синхронности по идее нужно было обрабатывать события на сокетах при помощи select, а не каждое соединение в своем отдельном потоке. В случае асинхронности использовать epoll

Кстати, современных "синхронных" серверов уже совсем не осталось. И "тормознутый апач" уже давно может epoll : http://httpd.apache.org/docs/2.4/mod/event.html
Вот в FAQ у апачей интерсное есть: Benchmarks tend to be a measure of configuration competance, rather than of server quality

Еще, вы намекаете, что IPC при асинхронной модели не требует синхронизации. Очереди там всякие и т.п. Атомарность записи чтения обьектов IPC в томже линуксе обеспечивается ядром, с точки зрения юзерспейса ее м.б. не видно, но это не значит, что ее нет
brag
psL, синхронный стиль подразумевает наличие потоков, иначе многозадачности не будет. Но наличие потоков не подразумевает наличие синхронности. Тут я ничего не путаю.

Цитата
Т.е. для имитации синхронности по идее нужно было обрабатывать события на сокетах при помощи select, а не каждое соединение в своем отдельном
потоке.

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

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

То что там апач умеет, это не важно, я лишь показываю преимущества асинхронного подхода перед синхронным, когда нужна многозадачность. Хотя нет важно, если апач переходит на асинхронный стиль - значит это камень в огород синхронного sm.gif

Цитата
Еще, вы намекаете, что IPC при асинхронной модели не требует синхронизации. Очереди там всякие и т.п. Атомарность записи чтения обьектов IPC в томже линуксе обеспечивается ядром, с точки зрения юзерспейса ее м.б. не видно, но это не значит, что ее нет

Но и не значит, что она есть.
На каждой ОС и железе по своему. У меня, например, она есть, но частично. В большинстве случаев ее нет, lock-free обеспечивается процессором и грамотным построением алгоритма очереди, но это все зависит от конкретного камня. Но даже если есть синхронизация, то блокируются приоритеты (пара инструкций), а не тяжеленные мютексы или полная блокировка прерываний. При чем эти блокировки очень короткие, только для работы со служебными полями очереди(парочка машинных слов). Сами данные в очередь пишутся(и читаются) при полностью разблокированной системе.

Да, эти алгоритмы иногда очень сложные и требуют тщательного анализа и тестирования(выявления и устранения гонок), но это делается один раз в одном файле и потом используется повсеместно. А не как обычно - везде в пользовательском коде кишат мютексы и семафоры, отловить баги с которыми не представляется возможным на более-менее сложном проекте. А если их(мютексов) там нет - значит это уже шаги в сторону чистой асинхронщины.
psL
Цитата(brag @ Sep 10 2016, 11:05) *
синхронный стиль подразумевает наличие потоков, иначе многозадачности не будет. Но наличие потоков не подразумевает наличие синхронности. Тут я ничего не путаю.

т.е. если процесс блокируется вплоть до события на каком-то одном или нескольких дескрипторах сокетов, а потом последовательно обрабатывает эти события, то такой процесс по вашему уже работает не синхронно?

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

В том же JS тоже есть несколько реализаций потоков Web Workers API, например. Они используются для создания фоновых процессов в браузере. Дело в том, что долго работающие тяжелые скрипты могут блокировать страницу браузера (выглядит как зависание страницы). Особенно это заметно при работе с DOM браузера напрямую. Отсюда всякие проекты по отрисовке типа ReactJs. Так что не все так радужно.
brag
Цитата
т.е. если процесс блокируется вплоть до события на каком-то одном или нескольких дескрипторах сокетов, а потом последовательно обрабатывает эти события, то такой процесс по вашему уже работает не синхронно?

Хм, что будет в самом обработчике события? Например, мне нужно каждому подключившемуся клиенту слать раз в секунду 100 рандомных байт? Ну или каждому отдать файлик гигов на 5? sm.gif

Цитата
Так что не все так радужно.

Не не все так радужно в браузерном(да и в Node)-JS. Но это не значит, что асинхронный стиль плохой. В SST, например есть приоритеты событий - вытеснение одних событий другими(по сути обычные прерывания) и там никаких тормозов нет.

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

Да это ясно, только синхронный стиль нам навязывает потоки, когда надо делать многозадачность. А асинхронный - нет, там многозадачность получается сама собой.
Herz
Тему разделил. Переносить в "Операционные системы" пока не стал. Какие будут мнения?
Slash
brag, можете показать реализацию variable element-size queue?

Как у меня сделано:
очередь выдрана из mbed
CODE
#include <stdint.h>
#include <string.h>
#include "cmsis_os.h"
#include "mbed_error.h"

bool handlerMode()
{
return __get_IPSR() != 0;
}

// Очередь с ленивой инициализацией - инициализируется в момент первого использования.
// Сделано для обхода проблемы с неопределенным порядком инициализации глобальных объектов.
// Очереди создаются глобально и менеджер памяти создается глобально.
// Очередь использует выделение памяти в куче, поэтому должна быть создана после менеджера памяти.
// Поэтому и сделана ленивая инициализация.
// Бывают случаи, когда первое использование очереди происходит в прерывании, что недопустимо.
// На этот случай, нужно вызывать метод create() явно, до первого использования.
template<typename T, size_t N>
class Queue
{
public:
Queue(const char * name = NULL)
: mName(name)
{}

/** Put a message in a Queue.
@param data data.
@param millisec timeout value or 0 in case of no time-out. (default: 0)
@return status code that indicates the execution status of the function.
*/
bool put(const T & data, uint32_t millisec = 0)
{
if (!mQueueId)
{
create();
}
portBASE_TYPE taskWoken = pdFALSE; // не понятно, зачем это надо
if (handlerMode())
{
if (xQueueSendToBackFromISR(mQueueId, reinterpret_cast<const void *>(&data), &taskWoken) == pdTRUE)
{
portEND_SWITCHING_ISR(taskWoken);
return true;
}
}
else
{
TickType_t ticks = millisec / portTICK_PERIOD_MS;

if (ticks == 0)
{
ticks = 1;
}

if (xQueueSendToBack(mQueueId, reinterpret_cast<const void *>(&data), ticks) == pdTRUE)
{
return true;
}
}
return false;
}

/** Get a message or Wait for a message from a Queue.
@param millisec timeout value or 0 in case of no time-out. (default: osWaitForever).
@return event information that includes the message and the status code.
*/
bool hasMessage(uint32_t millisec = osWaitForever)
{
if (!mQueueId)
{
create();
}
bool hasMessage = false;
portBASE_TYPE taskWoken = pdFALSE;
if (handlerMode())
{
hasMessage = xQueueReceiveFromISR(mQueueId, reinterpret_cast<void *>(&mData), &taskWoken) == pdTRUE;
portEND_SWITCHING_ISR(taskWoken);
}
else
{
TickType_t ticks = 0;
if (millisec == osWaitForever)
{
ticks = portMAX_DELAY;
}
else if (millisec != 0)
{
ticks = millisec / portTICK_PERIOD_MS;
if (ticks == 0)
{
ticks = 1;
}
}

hasMessage = xQueueReceive(mQueueId, reinterpret_cast<void *>(&mData), ticks) == pdTRUE;
}
return hasMessage;
}

const T & message() const
{
return mData;
}

T & message()
{
return mData;
}

size_t spaceAvailable() const
{
return uxQueueSpacesAvailable(mQueueId);
}

// нельзя вызывать из прерывания.
void create()
{
// неплохо бы проверять, что мы находимся не в прерывании
mQueueId = xQueueCreate(N, sizeof(T));

if (!mQueueId)
{
error("Error initialising the queue object\n");
}
if (mName)
{
vQueueAddToRegistry(mQueueId, mName);
}
}

private:
const char * mName;
QueueHandle_t mQueueId;
T mData;
};


В очередь пихаю свой доморощенный Variant:
CODE
#include <algorithm>

template <size_t N>
class Variant
{
public:
Variant()
{}

template <typename T>
Variant(const T & t)
{
std::copy(reinterpret_cast<const uint8_t *>(&t), reinterpret_cast<const uint8_t *>(&t) + sizeof(mData), mData);
}

template <typename T>
T get() const
{
return *reinterpret_cast<const T *>(mData);
}

template <typename T>
T * value()
{
return reinterpret_cast<T *>(mData);
}

Variant & operator= (const Variant & other)
{
std::copy(other.mData, other.mData + sizeof(mData), mData);
return *this;
}

float toFloat() const
{
return get<float>();
}

uint32_t toUInt32() const
{
return get<uint32_t>();
}


private:
uint8_t mData[N];
};


Использование:

CODE

struct Message
{
...
};

typedef Variant<sizeof(Message)> MessageVariant; // знаю, что структура Message самая большая из помещаемых в MessageVariant
Queue<MessageVariant, 10> queue;

...
if (queue.hasMessage())
{
const MessageVariant & msgVar = queue.message();
const Message & msg = msgVar .data().get<Message>();
}

Реализация на моем уровне знания шаблонов.
AHTOXA
Тема про SST интересная. Не панацея, конечно, но попробовать можно. Жаль, что brag не привёл какого-нибудь полного примера своей реализации. Думаю, что с использованием c++11/c++14 должно получиться весьма изящно.

На всякий случай приведу ссылку на репозиторий с исходной реализацией SST и его портом под какой-то арм.
brag
Цитата
brag, можете показать реализацию variable element-size queue?

Ваша реализация не понравилась, слишком много левого зависимого кода. И куча приведений типов, тем более reinterpret_cast.
Вот моя, без зависимостей вообще. Это самый простой вариант. Текущую версию приводить не буду, чтобы не путались, там локфри, записаь происходит за несколько этапов и сложно для понимания. Разберемся с обычной - перейдем к лок-фри.
Шаблон уже видели, вот его реализация
CODE
public:
typedef uint32_t BufferT;

// Fifo item base class. Must be POD
class ItemBase{
public:
// pointer to function that knows how to call specific lambda
typedef void(*WrapperT)(ItemBase*, TArgs...);

void invoke(TArgs ... args){
wrapper_(this, args...);
}

// ------------------------------------------
static size_t mark_unused(size_t size){
return size | UnusedMask;
}

size_t size()const{ return size_ & ~UnusedMask; }
bool isUsed()const{ return (size_ & UnusedMask) == 0; }

enum{ UnusedMask = 1<<(sizeof(size_t)*8-1) };

// ------------------------------------------
protected:
size_t size_; // in sizeof(BufferT) units
WrapperT wrapper_;
};

private:
// Constructors are private.
// You cant create objects, create VdelegateFIFO_T instead
// But You can use pointers of this type
VdelegateFIFO();
VdelegateFIFO(uint16_t size): size(size){
// empty: rx == wx && pushed_objects == 0
// full: rx == wx && pushed_objects > 0
rx = wx = 0;
pushed_objects = 0;
}


// lambda, function or functor object
template<class T>
class Item : public ItemBase{
public:
Item(const T& la): lambda(la){
this->size_ = obect_size();
this->wrapper_ = [](ItemBase* that, TArgs ... args){
static_cast<Item*>(that)->lambda(args...);
};
}

// in sizeof(ItemBase) units
static constexpr size_t obect_size(){
return sizeof(Item)/sizeof(BufferT);
}

void* operator new(size_t, void *ptr){ return ptr; }

private:
T lambda;
};

// -------------------- public --------------------
public:
template<class T>
bool push(const T& obj){
int w = getFreeConsecutiveSpace(Item<T>::obect_size());
if(w<0){ // not enough space
return false;
}
new(&buffer[w]) Item<T>(obj);
wx = w + Item<T>::obect_size();
if(wx >= size)wx = 0; // wrap

pushed_objects++;
return true;
}

ItemBase* getItemToPop(){
if(isEmpty())return nullptr;
ItemBase *item = reinterpret_cast<ItemBase*>(&buffer[rx]);
// wrap if unused tail
if(!item->isUsed()){
rx = 0;
item = reinterpret_cast<ItemBase*>(&buffer[rx]);
}
return item;
}

bool pop(const ItemBase *item){
if(!item)return false;
if(isEmpty())return false;
pushed_objects--;
rx += item->size();
if(rx>=size){
rx=0; // wrap
}else if(rx>wx){
// pop unused tail of buffer too
ItemBase *item = reinterpret_cast<ItemBase*>(&buffer[rx]);
if(!item->isUsed()){
//rx += item->size();
//if(rx >= size)rx=0;
rx=0; // wrap
}
}
return true;
}

bool isEmpty()const{ return pushed_objects==0; }

// ---------------------------------------------------
private:
int getFreeConsecutiveSpace(int sz){
int w = wx;
if(w==rx && pushed_objects>0)return -1; // full
// wx after rx
if(w >= rx){
if(w+sz <= size)return w;
// mark tail of buffer unused
buffer[w] = ItemBase::mark_unused(size-w);
// wrap
w = 0;
}
// wx before rx
if(w+sz <= rx)return w;
return -1;
}

template<class T, int Size> friend class VdelegateFIFO_T;

private:
const uint16_t size; // in sizeof(BufferT) units
uint16_t rx, wx;
uint16_t pushed_objects;
// it's a bit tricky and non-standard but works
BufferT buffer[0];
};

// ------------------ Container ------------------
template<class T, int Size_w>
class VdelegateFIFO_T : public VdelegateFIFO<T>{
public:
VdelegateFIFO_T(): VdelegateFIFO<T>(Size_w) {}

private:
typedef typename VdelegateFIFO<T>::BufferT BufferT;
BufferT buffer[Size_w];
};

Не знаю, понятен ли этот код, поэтому обясню на пальцах, а там можно и самому сделать.
Выделяется буфер фиксированного размера - это и есть сторейж нашей очереди. Данные пишуться по кругу, ring-buffer. Но с одной оговоркой - если обьект не влазит в конец буфера, этот конец помечается, как unused, а обьект пытаемся поместить в начало буфера. Если и там места нет - значит в очереди нет места для обьекта. Все. Если очередь используется с разных уровней приоритета - нужен лок на всю операцию записи/чтения.
В лок-фри в принципе идея та же, только запись служебных полей и самих данных разделено во времени(на 3 этапа). Еще есть одна реализация, где лок есть только на служебные поля, но запись данных идет без лока, тоже 3-этапная запись. Используется на процах, где нет нужной поддержки(у кортекс это ldrex/strex)

Вот пример использования очереди, ne lock-free , обычной:
CODE
template<int Size_w> //, class TBase=void*>
class TaskQueue{
public:
TaskQueue(SST::TPriority priority):
priority(priority)
{
current_task = nullptr; // dont pop first time
}

template<class T>
bool enqueueTask(const T& la){
CriticalSection cs;
cs.enter();
bool e = fifo.isEmpty();
bool r = fifo.push(la);
cs.leave();
if(e)taskCompleted();
return r;
}

bool taskCompleted(){
bool r = SST::postMessage(priority, [this](){
processNextTask();
});
if(!r){
printf("TaskQueue::taskCompleted error: SST_queue %d full\n", priority);
}
return r;
}

private:
void processNextTask(){
CriticalSection cs;
cs.enter();
fifo.pop(current_task); // pop if exists
auto la = fifo.getItemToPop();
current_task = la;
cs.leave();
if(la)la->invoke(); // static_cast<TBase*>(this));
}

private:
typedef VdelegateFIFO_T<void(), Size_w> T_Fifo;
T_Fifo fifo;
typename T_Fifo::ItemBase* current_task;
const SST::TPriority priority;
};

Мжно использовать из любого уровня приоритта, в том числе из прерываний.
Как пользоваться этим я уже показывал ранее(пример стирания флешки, шина SPI)

Цитата
Тема про SST интересная. Не панацея, конечно, но попробовать можно. Жаль, что brag не привёл какого-нибудь полного примера своей реализации.

Не панацея, но это другой стиль и он имеет много преимуществ перед синхронным, я их всех уже разжевал в этой теме и показал на примерах. Примеров кода в качестве контр-аргументов не увидел. Увижу - попытаюсь сделать из них SST-вариант, посмотрим на какой платформе будет круче и проще sm.gif
Полный пример приводить смысла нет - все равно не разберетесь без детального изучения и практики асинхронного программирования, да и так примеров я тут кучу привел, реально работающих, во всех разобрались и поняли как работает?. Мое ядро ССТ ничем не отличается от того, что по ссылке, только сделано на плюсах и заточено под мои прихоти. Но если что-то конкретное интересует - спрашивайте, покажу.

Готового решения у меня нет, если хотите работать на SST - придется реализовывать самому, там очень мало строк кода. Если не сможете реализовать, значит не сможете работать и на готовой реализации.
Если все равно есть желание - практикуйтесь на NodeJS, там все уже готово, и есть отличная документация и куча инфы в интернете. Но там 1 приоритет, но это даже хорошо - научитесь работать в одном приоритете - потом сможете освоить и много-приоритетный стиль и движок свой напишете за пару дней.
AHTOXA
Цитата(brag @ Sep 12 2016, 02:43) *
Готового решения у меня нет, если хотите работать на SST - придется реализовывать самому, там очень мало строк кода.

Как-то не очень правильно для миссионера предлагать для "попробовать" реализовать всё самомуsm.gif
Первая доза должна быть бесплатно! sm.gif)
А если серьёзно - когда есть возможность погонять готовую реализацию, понимание приходит гораздо быстрее. К тому же лично мне гораздо проще разобраться в целом работающем проекте, чем в статье с кусками кода.
brag
Да я и не миссионер и готовой реализации у меня нет, у меня есть свой никому не нужный велосипед, применить кроме меня кто-то другой вряд ли сможет, а если сможет, то ему будет проще сделать свой. А готовая реализация - это NodeJS, с него и надо начинать.
Целый работающий проект - коммерческий и права на него не принадлежат мне, я не имею права его публиковать.
AHTOXA
Вы тут в пылу борьбы уже столько проектов сделали - и на сях, и на ноде, и клиенты, и серверы... А маленькую мигалку светодиодом на SST не можете? Жаль. Это говорит не в пользу SST:)
Впрочем, в любом случае - спасибо вам за информацию, я кое-что полезное для себя почерпнул.
ViKo
Цитата(AHTOXA @ Sep 12 2016, 10:15) *
А маленькую мигалку светодиодом на SST не можете?

И чтобы параллельно SPI-флэш память стиралась и записывалась. rolleyes.gif
jcxz
Жаль заметил этот тред поздно, но всё-же заинтересовало утверждение автора, что он использует единый стек на все задачи, при этом ещё и эти задачи могут вытеснять друг друга и будто по функциональности такая система не уступает классической RTOS, где каждая задача имеет свой стек.
Что-то не могу представить - как именно автор добился работы на едином стеке???
И при этом есть возможность использования разделяемых ресурсов с блокировками или нет?

Цитата(brag @ Sep 10 2016, 02:04) *
Я даже не представляю, какой МК понадобился бы, если решить в многопоточном стиле те задачи, которые я на них запросто решаю в асинхронном однопоточном - с одним единственным стеком. Наверное пришлось бы брать простой PC + линукс(или какой-нибудь андроид) и не париться, только рентабельность такой системы будет никакая, равно как и энергопотребление бешеное.

Имхо: реализовать работу с разделяемыми ресурсами на едином стеке - невозможно.
Например: есть задача1, низкого приоритета, есть задача2 - приоритет её выше. Выполнялась задача1, занимала стек в диапазоне 99...90 байт, тут её вытеснила задача2, заняла стек в диапазоне 89...80. Всё хорошо. Теперь задача2 например хочет записать на флешь, а флешь в это время занята стиранием например. В классической RTOS задача2 войдёт в ожидание готовности некоего семафора и управление будет передано менее приоритетной задаче1 (с переключением на её стек), которая будет выполняться до тех пор пока семафор не перейдёт в состояние "готово" и контекст не будет переключен обратно на задачу2 (и её стек).
А что делать в этом случае в системе ТСа, чтобы данные в диапазоне 89...80 не были порушены задачей1, пока задача2 ожидает готовности флешь???
Сергей Борщ
QUOTE (jcxz @ Sep 13 2016, 11:31) *
Например: есть задача1, низкого приоритета, есть задача2 - приоритет её выше. Выполнялась задача1, занимала стек в диапазоне 99...90 байт, тут её вытеснила задача2, заняла стек в диапазоне 89...80. Всё хорошо. Теперь задача2 например хочет записать на флешь, а флешь в это время занята стиранием например.
На этом задача 2 заканчивается, но перед завершением создает задачу 3, которая будет запущена как только флеш освободится. Задача 2 завершена, ее стек освобожден, задача 1 может продолжать свою работу. Когда флеш освободится - будет запущена задача 3, которая будет работать на вершине стека и докончит запись. Каждая задача строится как лямбда-функция, указатель на которую кладется в соотвествующую очередь. Это именно задачи, а потоки складываются из цепочек задач. Я понял так, пусть brag поправит, если я где-то ошибся.
AlexandrY
Цитата(Сергей Борщ @ Sep 13 2016, 11:45) *
На этом задача 2 заканчивается, но перед завершением создает задачу 3, которая будет запущена как только флеш освободится. Задача 2 завершена, ее стек освобожден, задача 1 может продолжать свою работу.


А в жизни задача 2 ничего не знает ни о задаче 3, ни об очередях, ни о чем. Это просто тупая FatFS, и драйвера ее лупят в бесконечном цикле флаг готовности.

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

FatFS создавалась не один год и brag думаю на это же время оставит нас. biggrin.gif

brag
Цитата(ViKo @ Sep 12 2016, 11:55) *
И чтобы параллельно SPI-флэш память стиралась и записывалась. rolleyes.gif

Ok, запросто. Недельки через 3 у меня появится время, сделаю простенький демонстрационный проектик.

Цитата
Что-то не могу представить - как именно автор добился работы на едином стеке???
И при этом есть возможность использования разделяемых ресурсов с блокировками или нет?

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

Цитата
Имхо: реализовать работу с разделяемыми ресурсами на едином стеке - невозможно.
Например: есть задача1, низкого приоритета, есть задача2 - приоритет её выше. Выполнялась задача1, занимала стек в диапазоне 99...90 байт, тут её вытеснила задача2, заняла стек в диапазоне 89...80. Всё хорошо. Теперь задача2 например хочет записать на флешь, а флешь в это время занята стиранием например. В классической RTOS задача2 войдёт в ожидание готовности некоего семафора и управление будет передано менее приоритетной задаче1 (с переключением на её стек), которая будет выполняться до тех пор пока семафор не перейдёт в состояние "готово" и контекст не будет переключен обратно на задачу2 (и её стек).
А что делать в этом случае в системе ТСа, чтобы данные в диапазоне 89...80 не были порушены задачей1, пока задача2 ожидает готовности флешь???

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

Цитата
А в жизни задача 2 ничего не знает ни о задаче 3, ни об очередях, ни о чем. Это просто тупая FatFS, и драйвера ее лупят в бесконечном цикле флаг готовности.

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

Цитата
FatFS создавалась не один год и brag думаю на это же время оставит нас. biggrin.gif

brag, если ему будет нужно - эту FatFS портирует на SST за 1-2 суток, а если FATfs слишком зависима от RTOS, тогда brag отправит ее на мусорку и возьмет(или напишет) более совершенный код, который сможет работать как на RTOS, так и на ССТ, так и вообще без всякой ОС, тоже за несколько суток.
Сергей Борщ
QUOTE (AlexandrY @ Sep 13 2016, 12:10) *
А в жизни задача 2 ничего не знает ни о задаче 3, ни об очередях, ни о чем. Это просто тупая FatFS
Вы ничего не поняли, но снова все безапелляционно смешали в кучу. Задачу 2, задачу 3, все задачи вы пишете сами.
AlexandrY
Цитата(brag @ Sep 13 2016, 12:44) *
brag, если ему будет нужно - эту FatFS портирует на SST за 1-2 суток, а если FATfs слишком зависима от RTOS, тогда brag отправит ее на мусорку и возьмет(или напишет) более совершенный код, который сможет работать как на RTOS, так и на ССТ, так и вообще без всякой ОС, тоже за несколько суток.


Воо..! Займитесь. lol.gif
jcxz
Цитата(Сергей Борщ @ Sep 13 2016, 14:45) *
На этом задача 2 заканчивается, но перед завершением создает задачу 3, которая будет запущена как только флеш освободится. Задача 2 завершена, ее стек освобожден, задача 1 может продолжать свою работу. Когда флеш освободится - будет запущена задача 3, которая будет работать на вершине стека и докончит запись. Каждая задача строится как лямбда-функция, указатель на которую кладется в соотвествующую очередь.

Т.е. - как это задача2 будет завершена? А её куча локальных переменных, дерево вызовов процедур (запись во флешь была вызвана на некоем довольно глубоком уровне вложенности функций) - куда это всё? Где это всё хранить? Как это всё передать задаче3? Если не на стеке - то это крайне неудобно.
brag
Посмотрел FatFs - это блокирующий стиль, async IO там не предусмотрено, значит эту библиотеку я использовать не буду, а поскольку я ею никогда не пользовался, то и отправлять на свалку нечего, а так с удовольствием бы это сделал sm.gif
Благо, у меня есть своя написана давно за неделю, правда она не публичная.
В демо-проекте будем использовать что-то другое. Что - еще посмотрим. Может свою реализацию выложу в каком-то определенном виде, чтобы не было конфликтов интересов.

Значит что будет в демо-проекте - пару кнопок(плей, стоп), будем читать с флешки, декодировать и прогирывать MP3, одновременно будет мигать диод по таймеру, и одновременно будем воспроизводить анимацию на LCD. Также на SPI повесим термометр и будем раз в секунду считывать с него показания и отображать на LCD, это чтобы на практике посмотреть, как сериализировать доступ к шине.
Код максимально простой и понятный.
Проект сделаем под ПК, чтобы каждый смог запустить и опробовать в действии. Симулятор прерываний у меня давно написан и работает, SPI и флешка, gpio и дисплей - нарисуем, это не сложно. И того будет 2 модуля - симулятор и наша програмка. Кто захочет - запросто запустит саму програмку под свой МК в своей схеме, придется только самому проинициализировать железо и завернуть обработчики прерываний на наш проект.
Ну это еще будет видно как оно точно будет, пока так даю предварительную информацию.

Цитата
Т.е. - как это задача2 будет завершена? А её куча локальных переменных, дерево вызовов процедур (запись во флешь была вызвана на некоем довольно глубоком уровне вложенности функций) - куда это всё? Где это всё хранить? Как это всё передать задаче3? Если не на стеке - то это крайне неудобно.

Вы не прочитали внимательно ветку с самого начала и не имеете представления как это работает, так же не имеете опыта работы в данном стиле, поэтому Вам кажется, что это неудобно.
Велосипедисту на автомобиле тоже ехать неудобно, если он впервые сел за руль sm.gif
jcxz
Цитата(brag @ Sep 13 2016, 15:44) *
Но выполнение этого кода в любой момент может быть прервано(вытеснено) другим более приоритетным кодом(будь то прерыванием или любым другим событием)[/b]. Если нужна сериализация доступа к ресурсу(будь то флешка или что либо другое)- используется не огромный стек и тяжеленные мютексы, а быстрые легковесные очереди(FIFO), часто lock-free. Стек на весь проект один единственный.

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

Цитата(brag @ Sep 13 2016, 17:04) *
Вы не прочитали внимательно ветку с самого начала и не имеете представления как это работает, так же не имеете опыта работы в данном стиле, поэтому Вам кажется, что это неудобно.

В каком стиле? Я именно и спрашиваю про Ваш "стиль". Мне интересно.
Работу нескольких задач без блокировок и на едином стеке и с сериализацией ресурсов я реализовывал. И без всяких очередей. На кэллбэках. Но алгоритм всего построения ПО в таком стиле он очень далёк о традиционного вида нескольких задач-обработчиков сообщений.
Вот и интересуюсь - может Вы что-то новое изобрели. Всё мне читать лень - мне интересен сам принцип.
brag
Цитата
Слишком много перечитывать - не осилю.

Это очень мало, все максимально сжато, флуд можете смело пропускать. Чтобы чему-то научиться читать нужно гораздо больше.

Цитата
когда более приоритетная задача вдруг входит в состояние ожидания ресурса.

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

Цитата
В каком стиле?

Неблокирующем асинхронном.

Цитата
И без всяких очередей.

Без очередей не получится. Очередь - это "сердце" асинхронного движка(стиля). Без очереди Вы не сможете сериализовать доступ к ресурсу работая в асинхронном стиле.
Цитата
На кэллбэках. Но алгоритм всего построения ПО в таком стиле он очень далёк о традиционного вида нескольких задач-обработчиков сообщений.

Да, он очень далек, но он имеет много преимуществ перед блокирующим, А чтобы было удобно работать - нужен, как минимум С++11 с его лямбдами.
В асинхронном стиле очень удобно работать на Javascript поскольку там есть замыкания и сборка мусора. Но мы пока еще не дорасли до применения этого(или подобного) языка на МикроКонтроллерах, но очень уверенно к этому двигаемся и рано или поздно к этому придем. Я сейчас изучаю язык Rust, очень большие надежды на него. Если не он, то найдем что-то другое, или будем плюсы(17-е) допиливать, всякими шаблонами и др. стандартными для этих 17х плюсов средствами.
Благо, в однопоточном асинхронном стиле сборщик мусора гораздо проще, тк время жизни обьектов меньше, один поток, один стек.

Хотя динамические очереди это уже от части сборка мусора - сами же очереди и следят за выделением/освобождением памяти под данные задач, а не пользователь.
Вот посмотрел пару своих сложных проектов по диагонали - new/delete встречаю в коде очень и очень редко! Так что это еще один плюс SST - в подавляющем большинстве случаев сборка мусора происходит сама собой прозрачно для пользователя и очень эффективно.
В блокирующем многопоточном стиле сборщик мусора очень сложный и тяжелый и в МК он ессно не влезет.
jcxz
Цитата(brag @ Sep 13 2016, 17:18) *
Да, он очень далек, но он имеет много преимуществ перед блокирующим, А чтобы было удобно работать - нужен, как минимум С++11 с его лямбдами.
В асинхронном стиле очень удобно работать на Javascript поскольку там есть замыкания и сборка мусора. Но мы пока еще не дорасли до применения этого(или подобного) языка на МикроКонтроллерах, но очень уверенно к этому двигаемся и рано или поздно к этому придем. Я сейчас изучаю язык Rust, очень большие надежды на него. Если не он, то найдем что-то другое, или будем плюсы(17-е) допиливать, всякими шаблонами и др. стандартными для этих 17х плюсов средствами.

Если он также далёк от традиционного вида (наиболее удобного для чтения и разбирания исходников), то тогда не надо. На кэллбэках я реализую то же самое, но без всяких очередей и на обычном си.
Да кстати - хорошо, стек не переполняется у Вас, а очереди - они могут у Вас переполнится? Не стек так очереди.
И динамическая память в ембеддед - это не айс. Лучше всячески избегать её.
brag
Цитата
сли он также далёк от традиционного вида (наиболее удобного для чтения и разбирания исходников), то тогда не надо. На кэллбэках я реализую то же самое, но без всяких очередей и на обычном си.

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

Цитата
Да кстати - хорошо, стек не переполняется у Вас, а очереди - они могут у Вас переполнится? Не стек так очереди.

Необходимый размер очереди очень легко просчитывается, в отличии от стека. Стек невозможно просчитать, когда код сложный из виртуальных функций, а хуже того - рекурсии. Потом резервирование памяти под очередь в 4-кратном размере от расчетного - это каких-то лишних 50-100 байт, в то время, как для стека - это килобайт и более, на практике разница в 10 и больше раз. Это во первых.
А во вторых переполнение очереди отслеживается программно и зачастую даже прозрачно для пользователя, любое переполнение очереди - это recoverable error, в то время как переполнение стека - это крах всей системы.

Цитата
И динамическая память в ембеддед - это не айс.

Для тех, кто не умеет с ней работать sm.gif
jcxz
Цитата(brag @ Sep 13 2016, 17:52) *
Анука покажите, как Вы без очереди сериализуете доступ к шине SPI, когда на ней 2 абсолютно непредсказуемых устройства к которым может понадобится доступ в абсолютно непредсказуемое время? Желательно на примере кода.

Элементарно:
Предположим, что освобождение шины происходит в конце DMA-транзакции вызовом соответствующего ISR.
Любая задача, желающая произвести транзакцию по SPI, вызывает функцию ReqSPI() и завершается.
Тогда упрощённо это будет выглядеть например так:
CODE
static char volatile spiAct = 0; //при !=0 - идёт транзакция по SPI
static char volatile spiReq = 0; //при !=0 - идёт транзакция по SPI или есть запрос
static void (*endAct)();

//Установка состояния ожидания для прерывания num
#define IntSet(num) { NVIC.SETPEND[(num) >> 5] = 1u << ((num) & 0x1F); }
//Сброс состояния ожидания для прерывания num
#define IntClr(num) { NVIC.CLRPEND[(num) >> 5] = 1u << ((num) & 0x1F); }

void ReqSPI()
{
CPU_SR_ALLOCATE();
ENTR_CRT_SECTION(); //вход в крит. секцию (собственно - запрет прерываний)
if (!spiReq) {
spiReq = 1;
IntSet(NVIC_SPI); //программно активируем прерывание завершения транзакции SPI
}
EXIT_CRT_SECTION();
}

extern "C" void isrSPI() //ISR SPI
{
void (*p)();
if (spiAct) {
spiAct = 0;
//здесь делаем работу, необходимую при завершении транзакции по SPI
//например:
DeactivateCS();
if (endAct) endAct();
}
spiReq = 0;
if (!(p = SpiStartUser1())) //запросим пользователя1 нужна ли ему транзакция по SPI
if (!(p = SpiStartUser2())) //запросим пользователя2 нужна ли ему транзакция по SPI
...
if (!(p = SpiStartUserN())) return; //в данный момент нет пользователей желающих поработать с SPI
spiReq = 1;
IntClr(NVIC_SPI);
spiAct = 1;
ActivateCS();
endAct = p();
}
//Любой пользователь шины SPI (задача-пользователь), когда хочет произвести
//транзакцию по SPI, вызывает ReqSPI() и завершается.
//После сразу (если шина была свободна) или по её освобождении, будут в порядке
//приоритета вызваны функции с SpiStartUser1() по SpiStartUserN(), пока одна из
//них не вернёт указатель на функцию программирующую собственно железо SPI и
//запускающую транзакцию. Эта функция также возвращает указатель на функцию
//принимающую нотификацию о завершении транзакции SPI.


Цитата(brag @ Sep 13 2016, 17:52) *
код сложный из виртуальных функций, а хуже того - рекурсии.

А не надо в ембеддед использовать виртуальные функции и рекурсии и тогда всё нормально будет со стеком.
brag
jcxz, большое спасибо за код, именно так я и хотел, чтобы в таком духе развивалась тема.
Но это у Вас получилась очередь, только не динамическая(которую заполняет сам код в рантайме), а статическая, которую заполняет программист.
Вот этот код-это уже очередь, при чем приоритетная(User1 имеет высший приоритет, а UserN - низший):
Код
if (!(p = SpiStartUser1())) //запросим пользователя1 нужна ли ему транзакция по SPI
if (!(p = SpiStartUser2())) //запросим пользователя2 нужна ли ему транзакция по SPI
...
if (!(p = SpiStartUserN())) return; //в данный момент нет пользователей желающих поработать с SPI

Только пользоваться ею не удобно. У меня изначально, когда я только начинал учится асинхронщине примерно такое и выходило.
Потом эти цепочки ифов я заменил на просмотр односвязного списка User-ов, а потом и вовсе вместо списка втулил RingBuffer-FIFO User-ов, сначала ячейки были фиксированного размера, а затем стали динамические. Так и родилась концепция задач/очереди задач(то есть велосипед) в моей голове sm.gif

Цитата
А не надо в ембеддед использовать виртуальные функции и рекурсии и тогда всё нормально будет со стеком.

Да любой динамически изменяемый указатель на функцию уже виртуальная функция, а хуже того - запросто может быть рекурсивная. Можно конечно и их не использовать, но тогда зачем С и даже асм сдался вместе с их компиляторами, можно запросто в машинных кодах писать и все будет в шоколаде.
Инструкций indirect jump/indirect call проца при этом тоже ессно использовать не нужно, потому что эти инструкции - это уже виртуальные функции и динамический полиморфизм sm.gif
и в следствии этого Ваш анализатор стека должен полностью уметь выполнять код программы, как это делает проц(то есть быть полным симулятором проца), мало того, он должен уметь создавать такие ситуации, когда размер стека вызовов будет максимально-возможным. Я делал подобный анализатор - очень сложная задача, так и не довел его до ума, но пользовался им постоянно и постоянно его допиливал. А после перехода на SST-модель отправил этот анализатор(вместе с несколькими месяцами своей жизни) на свалку sm.gif
Сергей Борщ
QUOTE (jcxz @ Sep 13 2016, 16:05) *
А не надо в ембеддед использовать виртуальные функции
Объясните пожалуйста, что же такого страшного есть в виртульных функциях?
jcxz
Цитата(Сергей Борщ @ Sep 13 2016, 19:51) *
Объясните пожалуйста, что же такого страшного есть в виртульных функциях?

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

Цитата(brag @ Sep 13 2016, 19:38) *
jcxz, большое спасибо за код, именно так я и хотел, чтобы в таком духе развивалась тема.
Но это у Вас получилась очередь, только не динамическая(которую заполняет сам код в рантайме), а статическая, которую заполняет программист.
Вот этот код-это уже очередь, при чем приоритетная(User1 имеет высший приоритет, а UserN - низший):

Не знаю почему Вы это называете очередью. Ну да ладно.
Это и есть разделение доступа к ресурсу, основанное на кэллбэках. Такой метод позволяет делать сериализацию доступа к ресурсам на одном стеке и иногда я им пользуюсь.
Тут не задача-пользователь запрашивает ресурс произвести операцию с ним, а ресурс (т.е. - его диспетчер) запрашивает задачи-пользователи желают-ли они произвести с ним операцию.
Но вот код задач становится гораздо хуже читаемым. Так как в классическом виде с блокировками ресурсов, выполнение идёт в порядке строк функции. А здесь будет разорвано на отдельные функции - зрительно нарушен порядок следования. В этом главный минус.
brag
Цитата(jcxz @ Sep 13 2016, 17:02) *
Т.е. - я хотел сказать, что без них используемый размер стека проще определить.

Без них стек считается очень просто, а с ними - очень сложно.

Цитата
У меня изначально, когда я только начинал учится асинхронщине примерно такое и выходило.
Потом эти цепочки ифов я заменил на просмотр односвязного списка User-ов, а потом и вовсе вместо списка втулил RingBuffer-FIFO User-ов, сначала ячейки были фиксированного размера, а затем стали динамические.

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

Цитата
Не знаю почему Вы это называете очередью. Ну да ладно.

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

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

Это из за отсутствия опыта работы в данном стиле, языковой и рантайм поддержки. Мне бы без С++11 тоже было работать в этом стиле очень трудно. В общем асинхронный стиль требует языков более высокого уровня, например javascript с его замыканиями, худо бедно эти замыкания делаются на ламбдах и шаблонах С++11.
Kabdim
Цитата(brag @ Sep 13 2016, 16:38) *
Так и родилась концепция задач/очереди задач(то есть велосипед) в моей голове sm.gif

Ваша модель, если я правильно понял ваши объяснения, называется Акторами и давно реализована в шарпе, джаве, скале. Вы её разбавили ненужым уточнением в виде SST. Обе концепции вполне способны существовать в отрыве от друг друга.
brag
Цитата(Kabdim @ Sep 13 2016, 18:23) *
Ваша модель, если я правильно понял ваши объяснения, называется Акторами и давно реализована в шарпе, джаве, скале. Вы её разбавили ненужым уточнением в виде SST. Обе концепции вполне способны существовать в отрыве от друг друга.

Да, что-то типа того. Правда я в скале и шарпе не силен, не могу точно сказать.
Сама концепция(асинхронные события с приоритетами и вытеснением) существует давно, но начала активно применятся относительно недавно.
TSerg
Цитата(brag @ Sep 13 2016, 18:32) *
Сама концепция(асинхронные события с приоритетами и вытеснением) существует давно, но начала активно применятся относительно недавно.

Ну да, конечно. Вот только разработчикам бортовых систем класса "Буран" и подобных ему в других областях - не надо это говорить даже шепотом.
brag
Не знаю что там с Бураном, даже понятие не имею что это такое sm.gif Асинк-стиль был наверное еще до блокирующего, по идеи, хз. Говорю, как вижу своими глазами. Аsync io, например в винде существует очень давно, но активно начали применять относительно недавно. Такие проекты, как NodeJS, async IO в Qt, Nginx появились уже после традиционных блокирующих решений. А в никсах так вообще изначально был только блокинг-io(хотя сигналы существуют тоже очень давно), потом были всякие костыли(и есть до сих пор), а только потом появился действительно асинхронный ввод/вывод, да и то не во всех никсах.
Или взять базы данных - раньше все было исключительно блокинг, а сейчас переходят на нон-блокинг, а новые БД(например MongoDB) - вообще чисто асинхронные изначально.
В общем, сейчас, с приходом высоко-уровневых языков и новомодных техник асинхронный подход получил новый виток развития.
sigmaN
bb-offtopic.gif
Цитата
Не знаю что там с Бураном, даже понятие не имею что это такое

Буран это такая космическая хреновина которая полностью в автоматическом режиме вышла на орбиту и приземлилась потом ) Было это в далеком 1988 году wink.gif
https://ru.wikipedia.org/wiki/%D0%91%D1%83%...B1%D0%BB%D1%8C)
AlexandrY
Цитата(brag @ Sep 13 2016, 19:46) *
Аsync io, например в винде существует очень давно, но активно начали применять относительно недавно.


Я уже лет 15 использую пакет AsyncPro написанный на чистом Delphi.
Там вообще все асинхронное. biggrin.gif

Советую кстати задуматься о том как ваш автоматно-кооперативный подход будет взаимодействовать с реально асинхронным потоком прерываний.
brag
Цитата
Я уже лет 15 использую пакет AsyncPro написанный на чистом Delphi.
Там вообще все асинхронное. biggrin.gif

Это замечательно wink.gif

Цитата
Советую кстати задуматься о том как ваш автоматно-кооперативный подход будет взаимодействовать с реально асинхронным потоком прерываний.

В cortex-M прерывания реально асинхронные, одно прерывание может прерывать другое, все легло на SST без бубна и отлично работает, в том числе под интенсивным асинхронным потоком прерываний.
Над чем тут можно задумываться и какие тут могут быть проблемы?
sigmaN
Цитата
Благо, в однопоточном асинхронном стиле сборщик мусора гораздо проще, тк время жизни обьектов меньше, один поток, один стек.

Хотя динамические очереди это уже от части сборка мусора - сами же очереди и следят за выделением/освобождением памяти под данные задач, а не пользователь.
Вот посмотрел пару своих сложных проектов по диагонали - new/delete встречаю в коде очень и очень редко! Так что это еще один плюс SST - в подавляющем большинстве случаев сборка мусора происходит сама собой прозрачно для пользователя и очень эффективно.
В блокирующем многопоточном стиле сборщик мусора очень сложный и тяжелый и в МК он ессно не влезет.
Ничего, что в C++ сборка мусора специально выполнена в строго детерминированном варианте. А именно: в конце времени жизни объекта(после выхода из области видимости) автоматически вызывается деструктор, который чистит всё что нужно(если нужно). Т.к. время жизни объектов строго детерминировано по каждой ветке выполнения программы - сборка мусора тоже 100% предсказуема. Это позволяет применять плюсы для риалтайм систем.

Представьте что JS управляет хвостовым оперением истребителя, нужно реагировать на команды пилота, но тут вдруг не очень вовремя сборщик мусора проснулся и начал метлой махать )))

P.S. Чувствую сейчас наш гуру скажет, что его не волнует как устроена сборка мусора в JS и что она периодически выполняется JS движком, причем в отдельном потоке(сюрприз-сюрприз) он тоже не слышал biggrin.gif
http://v8project.blogspot.com/2015/08/gett...n-for-free.html
Цитата
After marking, the free memory is made available again for the application by sweeping the whole old generation memory. This task is performed concurrently by dedicated sweeper threads. Finally, memory compaction is performed to reduce memory fragmentation in the old generation. This task may be very time-consuming and is only performed if memory fragmentation is an issue.
brag
Цитата
в C++ сборка мусора специально выполнена в строго детерминированном варианте.

В c++ сборки мусора нет, пользователь сам вручную управляет памятью операторами new и delete. Есть, правда, всякие библиотеки типа boehm-gc, но это не имеет отношения к теме.

Цитата
Представьте что JS управляет хвостовым оперением истребителя, нужно реагировать на команды пилота, но тут вдруг не очень вовремя сборщик мусора проснулся и начал метлой махать )))

Ну JS скорее всего туда не годится(хотя не факт). Потом сборщику можно дать определенный приоритет, чтобы он не мешал работать time-critical задачам. Тем более, сборщик мусора для однопоточных асинхронных приложений очень простой получается.
Динамические очереди и стек - это уже автоматическое управление памятью(сборка мусора - отработавших задач, обработчиков событий и функций), а поскольку в SST код состоит почти из одних задач, обработчиков событий и функций, то и эта сборка мусора покрывает почти весь проект, при чем работает очень быстро(порядка 4-10 тактов на cortex-m). По этому(и не только) для меня SST удобнее потоков и на нем разработка приложений происходит быстрее и багов меньше.
Осталось сборку реализовать для остальной части кода, где есть данные "сбоку" задач, например того же GUI, именно в нем я чаще всего встречаю ручное управление памятью(и то довольно редко), от которого при должной языковой поддержке можно полностью избавится. Возможно с Rust этого удастся достичь, или будем на плюсах делать велосипед. Полностью автоматическая сборка мусора - это большой плюс к скорости разработки - это нам и нужно.

Цитата
P.S. Чувствую сейчас наш гуру скажет, что его не волнует как устроена сборка мусора в JS и что она периодически выполняется JS движком, причем в отдельном потоке(сюрприз-сюрприз) он тоже не слышал biggrin.gif

Лично на моей системе приложение Node занимает 4-10 потоков. Но что там внутри и почему меня действительно не волнует - я пользователь Node, а не ее разработчик, и мой пользовательский JS-код выполняется в одном потоке и не требует синхронизации.
Или взять Qt, на винде простое приложение - 4 потока, на линуксе - 3 (у кого-то другого цифры могут быть другими, зависит от конкретной платформы и версии Qt), но меня это тоже не волнует - мой пользовательский код работает в одном потоке и не требует синхронизации(если сам своих потоков не наклепаю).
AlexandrY
Цитата(brag @ Sep 13 2016, 22:09) *
В cortex-M прерывания реально асинхронные, одно прерывание может прерывать другое, все легло на SST без бубна и отлично работает, в том числе под интенсивным асинхронным потоком прерываний.
Над чем тут можно задумываться и какие тут могут быть проблемы?


Сейчас у меня на столе встраиваемый модуль с 12-ю базовыми задачами и 6-ю динамическими.
Это обычный простейший контроллер кинематической системы. В роботах таких десятки могут быть.
Это задачи:
-системный менеджер,
-менеджер локальных входных сигналов c фильтрами, антидребезгом, тревогами и проч.
-менеджер PWM модуляторов для исполнительных механизмов и квадратурных энкодеров,
-две задачи на вход и выход CAN протоколов,
-задача USB стека,
-задача VT100 терминала и драйвера UART,
-задача коммуникационного канала с BLE устройствами,
-задача HMI управляющая около сотни элементов индикации-диагностики локально и по сети CAN,
-супервизор контролирующий жизнедеятельность,
-IDLE задача с измерением загруженности процессора,
-задача лога и записи на SD карту.
-задача риалтайм наблюдателя FreeMaster
-динамические задачи двигателя, возникают во время движения для отработки разных стадий траектории.

Вот чё тут делать с вашим методом?
Я ему кстати название придумал - АКОП (автоматно-кооперативный) biggrin.gif
Хорошая метафора однако.
В своем акопе пытаетесь укрыться от наступления RTOS. Бесполезное занятие.
brag
Цитата
Вот чё тут делать с вашим методом?

И в чем могут быть проблемы?
Подобные задачи отлично ложатся на SST и код проще выходит и ресурсов надо меньше, чем на RTOS.
Я уже приводил в этой теме сравнение асинхронного кода с традиционным блокирующим, как по мне - асинк гораздо проще и лучше., по крайней мере конструктивной критики асинк-кода в пользу блокинг я практически не увидел. Кто-то критикует его за "непоследовательность", но это дело привычки. И то, что лучше - 5-10 строк простого непоследовательного кода или 100 строк последовательного, да еще и запутанного? - каждому свое в виду его знаний и опыта в конкретном стиле. Я работал по несколько лет и в том и в другом, есть с чем сравнивать и в итоге сделал свой выбор.
Не увидел никакой конкретики почему по Вашему мнению сюда не подходит SST, одни голословные утверждения.
AlexandrY
Цитата(brag @ Sep 14 2016, 10:15) *
Я уже приводил в этой теме сравнение асинхронного кода с традиционным блокирующим

Не увидел никакой конкретики почему по Вашему мнению сюда не подходит SST, одни голословные утверждения.


Во первых, никакого сравнения для embedded вы не приводили.
C того как сделать 1000 серверных сокетов на Node.js начинаются все учебники.
Это просто копипаста с вашей стороны.

Я еще могу с натяжкой поверить что вы возьметесь переписать FatFS под стиль своего АКОП-а.
FatFS стоит новичку переписать чтобы понять как работает файловая система.

Но в моих задачах не менее 4-х сторонних программных модулей сравнимых по сложности с FatFS.
Перепишете?
Задайте здесь вопрос кто верит что вы сможете переписать TCP/IP, FAT, USB с классами, драйвера уровня HAL, риалтаймную обработку событий и т.д. под AKOП и за какое время.
Флейм будет еще веселей.

Во вторых подсчеты количества строк в таких крупных проектах это ни о чем.
Где-то 50 % строк сплошные коменты.
Коменты и делают код читаемым и понятным. Одно только искусство писать коменты сделает программирование легче в 10 раз. Ваш АКОП нервно курит.
Опять же я не верю что вы строчите код с такой скоростью чтобы экономия строк имела значение. В среднем в день небось максимум сто строк можете написать.
И опять же экономить надо символы, а не строки 01.gif
brag
Стиль программирования что для embedded, что для пк одинаков. Даже функции называются так же, при чем мои проекты запросто компилируются под ПК и на нем отлаживаются. Проект отдельно, а платформозависимый код отдельно, вместо него автоматом средствами С++ подключаются свои заглушки или симуляторы, это гораздо удобнее.

Цитата
Задайте здесь вопрос кто верит что вы сможете переписать TSP/IP, FAT, USB с классами, драйвера уровня HAL, риалтаймную обработку событий и т.д. под AKOП и за какое время.
Флейм будет еще веселей.

Все уже написано и переписано давно. Первым была USB(device), лет 12 назад, если не больше написал и пользуюсь до сих пор, иногда вношу какие-то недостающие фичи. Остальные аналогично. Многое использую готовое, тот же mp3, jpeg.
Многое беру готовое и применяю свои патчи(например ogg), чтобы нормально можно было из плюсов и SST работать.

Мы говорим о реальных преимуществах и недостатках SST для нового кода, а не использования несовместимого синхронного legacy кода. Если стоит задача работать с RTOS-зависимым legacy - SST Вам не подойдет, даже C++ не всегда подойдет, часто приходится на С лопатить, чтобы сохранить определенный стиль, хуже того, если проект коллективный и все программисты - сишники.
Если Вы пишете что-то новое свое, используете асинхронные библиотеки, или команда разработчиков у Вас способна учится и не зависит от legacy - SST очень здорово упростит процесс разработки и сэкономит системные ресурсы.
Я тоже на С пишу, особенно писал раньше довольно часто, тк этого требовал заказчик - проггеры, которые будут обслуживать проект не имели знаний и опыта плюсов.
A недавно перевел один проект из синхронного С++ на асинхронный, заняло не так много времени, но код стал короче, проще и появилось куча свободных ресурсов. До перевода они уже закончились, добавлять новые возможности было некуда, сейчас туда можно еще добавлять и добавлять.

По читаемости кода спорить не буду, кому-то комменты дают, а кому-то просто короткий и простой код. С таким успехом и обьектые языки можно заменить простым ассемблером с комментами sm.gif
Строчу от 200 до 1000 строк в день, хотя бывает и десятка не наберется. Я еще люблю удалять код, из 1000 строк делаю 200.
sigmaN
Цитата
Лично на моей системе приложение Node занимает 4-10 потоков. Но что там внутри и почему меня действительно не волнует - я пользователь Node, а не ее разработчик, и мой пользовательский JS-код выполняется в одном потоке и не требует синхронизации.
Или взять Qt, на винде простое приложение - 4 потока, на линуксе - 3 (у кого-то другого цифры могут быть другими, зависит от конкретной платформы и версии Qt), но меня это тоже не волнует - мой пользовательский код работает в одном потоке и не требует синхронизации(если сам своих потоков не наклепаю).
Пытался я вам как-то уже объяснить, что если в вашем коде нет вэйтов и блокировок то это не означает что их нет нигде.... И еще кто-то тут тоже пытался.... Но это всё бесполезно ))
Понять, что один стек на всю систему и один стек для вашего приложения, подписанного на события - это тоже разные вещи вы категорически не желаете.

AlexandrY прав. В итоге кроме примера в три строки как на Node.js запустить сервер мы пока ничего не видели. Мягко говоря, это не очень соответствует теме форума.

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

Опять же в реальном мире и Qt и JS ваш любимый вовсю используют потоки....
Про некоторую вашу фанатичность я уже говорил. Видимо повторяться не стоит )
brag
Состояние и там и там надо запоминать, при чем вручную. В одном случаи - создавая переменную в стеке, в другом - переменную в классе. Кому что удобнее - я не знаю, мне лично одинаково.

Мало того, в лямбдах о состоянии заботится компилятор, а не пользователь. Те задачи, которые ложатся на лямбды - вообще не требуют ручного выделения места под состояние, а таких задач много. И то лямбды в плюсах слабые. Если появится инструмент по-мощнее - будет еще удобнее.
sizeof ессно растет, но он заранее известен, в то время как просчитать стек нереально, если там виртуалы(indirect call/indirect jump) и/или рекурсия.

Интересно было бы увидеть Ваш код, где бы Вы показали простоту/преимущества блокирующего подхода, а я бы привел его аналог в неблокирующем стиле, вот тогда и можно сравнивать. А без этого это голословные утверждения.
Повторюсь - SST уместен для нового async-кода, для синхронного legacy он не катит.
sigmaN
Цитата
Интересно было бы увидеть Ваш код, где бы Вы показали простоту/преимущества блокирующего подхода

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

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

Пример простоты/преимущества блокирующего подхода засчитан?

Цитата
Повторюсь - SST уместен для нового async-кода, для синхронного legacy он не катит.
Это всё замечательно. Только вот в процессе нашей учёной беседы мы тут обнаруживаем немало ситуаций, когда SST ваш этот жизнь усложняет, вместо того чтобы её упрощать. И даже внезапно выяснилось, что вместо сэкономленного стека sizeof() объекта таки растет. Факт этот был вами парирован тем, что дескаать sizeof то этот статичен, а размер стека непредсказуем.... Так себе аргумент, скажу я вам.... Очереди, на которые теперь ложится весь груз межзадачного взаимодействия и даже передачи данных - теперь становятся резиновыми.
НО вы тут предлагаете на ПК просимулировать всё это дело, опеределить размер очередей который понадобился, потом взять запасик и в продакшн. Было? Быыыло.

Не слыхали ли вы, как бородатые дядьки заполняют в стартапе оперативку неким значением(редковстречающимся сочетанием цифр), после чего точно так-же как вы запускают контроллер, он работает по всем тестам которые удалось придумать, а после этого делают дамп оперативки и смотрят до куда вырастал стек. Узнать это просто - ведь мы заполнили память заранее известным сочетанием циферок.
Ах да, потом дядька бородатый берет это значение, увеличивает его для надежности и в продакшн.
Вы уже чувствуете как вы повторили опыт бородатых дядек с лямбдами-функторами на модном С++11 )))?
brag
Цитата
Пример простоты/преимущества блокирующего подхода засчитан?

Нет. ) Нужен реальный пример кода на любом языке
sigmaN
Мне лень вам приводить здесь абсолютно типичный кусок кода из букваря многопоточного программирования )
Кстати подправил своё предыдущее сообщение, дополнил про размер стека.
AlexRayne
насколько мне не изменяет склероз:
вытесняюшая многозадачность пошла в моду под давлением глючного ПО: кооператив прекрасен если все задачи нормально отлажены, не тянут одеяло друг на друга, не падают внезапно, и не крашат стек.
в реальности первый же залетный дятел рушит кооперативную систему наглухо. поэтому становится актуальна задача в общем случае снятия умершей задачи безболезненно для выполнения остальных. во встроенных системах - там где нет своевольного юзера, все идет раз и навсегда по отлаженым рельсам. в пользовательской системе узеру необходимо иметь окружение отзывчивое, поэтому зависшие процессы надо снимать, и проги изолировать друг от друга чтоб они не гадили кому попало.
ныне мода изоляции приложений идет еще дальше - передовые оси декларирую требование о возможности снять/вытеснить приложение из памяти когда им нужно. с другой стороны виртуальные машины уже кустятся мощно.
Так что баталии коопа против вытеснения не решают задачи паралельной или асинхронной обработки. оба подхода работают успешно. разница начинает ощущаться при работе с ненадежным окружением или глючными алгоритмами.
brag
Без кода это разговор ни о чем. Будет реальный пример - я приведу контр-пример на SST.
Стек я подобным образом(и не только, куча вариантов разных было) и вычислял, только тесты сложные ради этого надо делать и не всегда их результат верный - компилятор при первом чихе(исправите где-то одну буковку) может поменять всю картину. А с учетом блокировок - так жутко сложные выходят, блокировка хоть год может висеть.
С очередями такого нет. Да и очереди места меньше требуют, чем стек. В стеке надо все регистры хранить(и не только), а в очереди - пару указателей.
Часто для очередей хватает и места под 1 обьект, но я для запаса делаю под 8, тк расходы на это копеечные.

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

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

Кооператив все таки слабоват - нужны прерывания, чтобы вытеснять менее-приоритетный код более приоритетным, тут на помощь приходит SST.
A пример реального кода, как глючная задача валит всю ОС я уже приводил. Так что это миф, что многопоточный стиль дает защиту, а асинк - нет. На чисто асинхронном стиле тоже можно сделать должную защиту, просто никто существующие оси(линух,винда), а тем более весь блокинг-софт,который под них работает, переписывать не будет, зато на МК мы можем себе это позволить.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.