Jump to content
    

По месту, думаю, должен использоваться класс-счётчик, типа TickCounter<T>. У него внутри уже будет спрятана вся эта магия. А снаружи - инкремент и чтение.

Share this post


Link to post
Share on other sites

8 минут назад, AHTOXA сказал:

По месту, думаю, должен использоваться класс-счётчик, типа TickCounter<T>. У него внутри уже будет спрятана вся эта магия. А снаружи - инкремент и чтение.

Не понял. Вот берём пример из стартового поста:
 

   INLINE tick_count_t get_tick_count() { TCritSect cs; return Kernel.SysTickCount; }

Тут TCritSect разворачивается в полноценный код. Но мы-то этого не хотим, например, в случае арма, где доступ к SysTickCount и так атомарный. Как сделать так, чтобы на арме секция была пустой, а на авре полной?

Share this post


Link to post
Share on other sites

Здесь будет так:

   INLINE tick_count_t get_tick_count() { return Kernel.SysTickCounter.get(); }

А уже унутре этого get() будет вся магия.

То есть, в TKernel вместо tick_count_t SysTickCount будет TickCounter<tick_count_t> SysTickCounter;

Share this post


Link to post
Share on other sites

14 hours ago, AHTOXA said:

Если используется #include <type_traits> и auto, то это уже c++11. А если можно c++11, то незачем городить эти шаблоны вручную, там есть std::conditional.

Нету. Точнее, есть, но не работает, как хочется. Передать в конструктор экземпляр переменной, чтобы он сам догадался, какой у неё тип, можно только в C++17 (вчера узнал новое слово). А объявлять критическую секцию не для переменной, а для класса этой переменной (или decltype писать) мне не понравилось.

14 hours ago, AHTOXA said:

Что там сейчас используется от с++11? std::is_arithmetic и  std::is_enum. Думаю, можно обойтись без.

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

 

5 hours ago, dxp said:

И ещё не очень понятно, как это по месту использовать, чтобы автоматом оно понимало что с чем сравнивать.

Тут самая главная проблема, что эта "критическая" секция ограничивается только чтением и записью. В остальных случаях атомарность НЕ гарантируется.

Пожалуй лучше даже назвать в духе CritSectionReadWrite. 

 

1 hour ago, AHTOXA said:

По месту, думаю, должен использоваться класс-счётчик, типа TickCounter<T>. У него внутри уже будет спрятана вся эта магия.

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

Share this post


Link to post
Share on other sites

Я не очень понял, почему будут вложенные критические секции. Скажем, сейчас у нас есть в TEventFlag переменная

        volatile TValue      Value;


а будет

        volatile AtomicReadWrite<TValue>      Value;



Соответственно, все места, где были чтения с созданием критических секций, заменятся на Value.read(), а места, где были места с записью - на Value.set().

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

Share this post


Link to post
Share on other sites

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

 

bool OS::TEventFlag::wait(timeout_t timeout)
{
    TCritSect cs;

    if(Value)                                           // if flag already signaled
    {
        Value = efOff;                                  // clear flag
        return true;
    }
    else
    {
        cur_proc_timeout() = timeout;

        suspend(ProcessMap);

        if(is_timeouted(ProcessMap))
            return false;                               // waked up by timeout or by externals

        cur_proc_timeout() = 0;
        return true;                                    // otherwise waked up by signal() or signal_isr()
    }
}

Тут секция защищает всю функцию. Как тут эта автоматика поможет? Тут же несколько разных объектов и вызов внутренних функций, которые подразумевают, что вызываются из критической секции (а внутри они тоже выполняют немало действий, которые должны производиться тоже в критической секции). Проводить анализ, что там безопасно, а что опасно без критической секции, это ещё та работа. Сейчас оно просто и серьмяжно лочит на входе, и всё работает как часы. 

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Ну, так а профит-то получается в чём? Кроме этой get_tick_count() что-то больше и мест не особо просматривается. А там получается вместо одного макроса целая плюсовая кухня вырисовывается. И с чем боремся? :)

Share this post


Link to post
Share on other sites

Ну, лично я сразу принял вариант с макросом:)
А Сергей сказал, что надо что-то универсальнее. Вот и придумываем:))
Вот, я имел в виду что-то типа такого:
 

template <typename T, size_t atomicSize = 4>
class AtomicVar
{
public:
    void set(T value) { CS cs; val = value; }
    void setForceCs(T value) { TCritSect cs; val = value; }
    void setNoCs(T value) { val = value; }
    T get() { CS cs; return val; }    
    T getNoCs() { return val; }
    T getForceCs() { TCritSect cs; return val; }
    
    void inc(); // -- для тиков

private:
    struct FakeCriticalSection {};
    
    using CS = typename std::conditional<sizeof(T) <= atomicSize, FakeCriticalSection, TCritSect>::type;
    T val;
};

Можно использовать вместо Kernel.SysTickCount, TEventFlag::Value и в других подобных местах. По крайней мере позволит убрать все ненужные критические секции почти автоматом, и не придумывая на каждый случай макрос.

ЗЫ. Вот мини-тест: https://wandbox.org/permlink/zmJsh7mWUj1oQ2JH

Share this post


Link to post
Share on other sites

9 минут назад, AHTOXA сказал:

Можно использовать вместо Kernel.SysTickCount, TEventFlag::Value и в других подобных местах. По крайней мере позволит убрать все ненужные критические секции почти автоматом, и не придумывая на каждый случай макрос.

Код красивый, и что он эффективно работает, не сомневаюсь. Но обращаюсь опять к прежнему своему вопросу: вот код функции TEventFlag::wait, там есть ветка else, она тоже защищена критической секцией. Как этот красивый шаблон поможет защитить код в else? Этот шаблон хорошо подходит только для одиночных переменных. Что собственно и отражено в его имени. :)

Share this post


Link to post
Share on other sites

Я ведь уже ответил на этот твой вопрос:)
В TEventFlag::wait() нужно оставить общую критическую секцию и использовать функции без CS (getNoCs()/setNoCs()).

Share this post


Link to post
Share on other sites

Ну, т.е. по сути не менять в этом случае ничего? И какой тогда смысл менять TValue на AtomicVar<TValue>? Получается, что конкретно класс TEventFlag трогать вообще смысла нет. Я правильно понимаю?

 

Share this post


Link to post
Share on other sites

Ну почему же. В TEventFlag уберутся критические секции в функциях clear() и is_signaled(). Полной автоматизации не выйдет, но небольшой кусочек будет оптимальнее.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Наверняка у Сергея есть ещё кандидаты на оптимизацию. Давай подождём, что он скажет.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...