1. C++ / Говнокод #13733

    +13

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    void User::AddFriend(User& newFriend)
    {
        friends_.push_back(&newFriend);
        pDB_->AddFriend(GetName(), newFriend.GetName());
    }
    
    //...VS...
    
    void User::AddFriend(User& newFriend)
    {
        friends_.push_back(&newFriend);
        try
        {
            pDB_->AddFriend(GetName(), newFriend.GetName());
        }
        catch (...)
        {
            friends_.pop_back();
            throw;
        }
    }
    
    //...VS...
    
    class VectorInserter//Глобальный безопасный вектороВставлятель.
    {
    public:
        VectorInserter(std::vector<User*>& v, User& u)
        : container_(v), commit_(false)
        {
            container_.push_back(&u);
        }
        void Commit() throw()
        {
            commit_ = true;
        }
        ~VectorInserter()
        {
            if (!commit_) container_.pop_back();
        }
    private:
        std::vector<User*>& container_;
        bool commit_;
    };
    
    void User::AddFriend(User& newFriend)
    {
        VectorInserter ins(friends_, &newFriend);
        pDB_->AddFriend(GetName(), newFriend.GetName());
        // Everything went fine, commit the vector insertion
        ins.Commit();
    }

    Запостил: LispGovno, 03 Сентября 2013

    Комментарии (44) RSS

    • Шиза крепчала, деревья гнулись.

      В последнем примере автор статьи забыл, что он рискует уронить всю программу, если container_.pop_back(); кинет исключение в момент размотки стека из-за другого исключения.
      Ответить
      • ну OOP как OOP. на жабе этому целые фреймоворки эпохальных размеров посвящены.

        RFC1925, (6a): It is always possible to add another level of indirection.

        другими словами: основная проблема которую решает OOP, это облегчение программистам добавление оберток и прочей воды, что бы начальству было чего показать и самое главное рассказать.
        Ответить
      • > Шиза крепчала, деревья гнулись.
        Такими темпами автор и до STM дойдет.
        Ответить
        • Я статью до конца дочитал.
          В конце я помечтал, что он сделал вот так:

          class VectorInserter
          {
          public:
              VectorInserter(std::vector<User*>& v, User& u)
              : container_(v), commit_(false)
              {
                  container_.push_back(&u);
              }
              void Commit(void) throw()
              {
                  commit_ = true;
              }
              ~VectorInserter(void) throw()
              {
                  if (!commit_) 
                    try
                    {
                      container_.pop_back();
                    }
                    catch(...)
                    {
                    }
              }
          private:
              std::vector<User*>& container_;
              const bool commit_;
              
              VectorInserter(void);
              VectorInserter(const VectorInserter&);
              VectorInserter& operator=(const VectorInserter&);
          };
          
          void User::AddFriend(User& newFriend)
          {
              VectorInserter ins(friends_, &newFriend);
              pDB_->AddFriend(GetName(), newFriend.GetName());
              // Everything went fine, commit the vector insertion
              ins.Commit();
          }


          Интересно, как бы это в STM бы выглядело.
          Ответить
          • Примерно так (давно не копался в STM, код написан на глазок и от балды):
            atomically $ do
                update friends (\s -> insert s newFriend)
                addToDb db (name friendName)
            Ну в общем тупо 2 операции, объединенные в атомик блок.
            Ответить
            • А откуда db выросло? stm принципиально не работает ни с чем, кроме апдейтов памяти, т.к. они не блокируют и легко откатываются. ACID из ACI никак не сделаешь (кислоту из аси не получишь? о_О). Зато можно периодически сливать снэпшоты иммутабельных структур из STM в базу внутри IO.
              Ответить
              • А нет какого-нибудь способа выполнить обратимый сайд-эффект (например с коллбеком отката) находясь в монаде стм?
                Ответить
                • Вроде как такая возможность реализована в 3-rd party пакете stm-io-hooks.
                  Штатная реализация предоставляет лишь богомерзкий unsafeIOToSTM без возможности откатов.

                  P.S. вот, нашёл такой вопрос на SO:
                  http://stackoverflow.com/questions/8197266/using-stm-and-database-transactions-together
                  Ответить
                  • Если я правильно понимаю описание, то хуки только на коммит и ретрай, на отмену нет.

                    А вот сумеречный стм надо бы покурить, вроде как функционала хватит для работы с координатором.
                    Ответить
                    • http://svn.boost.org/svn/boost/sandbox/stm/branches/vbe/libs/stm/doc/html/index.html
                      Интересно, STM не на уровне компилятора в С++ загнулся?
                      Ответить
                    • Вообще STM не в кассу. STM это когда потоков много.
                      Ответить
                      • Ну STM и с одним потоком можно юзать ради буковок A и C в ACID (I нам с одним потоком почти бесполезна). Почему нет?
                        Ответить
              • > Зато можно периодически сливать снэпшоты иммутабельных структур из STM в базу внутри IO.

                Не самая приятная процедура... Придется дельту считать, а хаскель вроде бы не умеет сравнивать ссылки (поправьте, если ошибаюсь), поэтому преимущество от расшаренных общих данных в снимках нам реализовать не удастся. Только полное сравнение, только хардкор...

                И к тому же это совсем не D из ACID, хотя для многих применений было бы достаточно.
                Ответить
            • > update friends (\s -> insert s newFriend)
              update friends (flip insert newFriend)
              Ответить
              • фу, пошляк. развернуть и вставить... я с тобой дружить не буду
                Ответить
                • Так он же не друга разворачивает, а само действие "вставить"...
                  Ответить
                • > развернуть и вставить...
                  обновить друзей

                  Когда заводишь нового друга - его нужно обновить и обмыть. Так уж принято в обществе.
                  Ответить
          • P.S. Жопа тут в том, что база это внешняя по отношению к STM сущность, и придется городить какой-нибудь координатор транзакций между STM и базой.
            Ответить
    • Теперь создаём в одном скопе два инсертера, не коммитим первый, коммитим второй, ловим лулзы от того, что получилось.
      Ответить
      • Выскажусь в защиту автора: это уже ошибка в коде, так можно и хуй сломать просто закоммитить не то и не туда...

        Этот класс все же страхует от исключений при вставке в базу, а не от ошибок программиста.
        Ответить
        • Шаблон проектирования есть, частично страхующий от такой ерунды, Unit Of Work называется.
          Ответить
          • В случае с стд::векторами и т.п. автор заипецца реализовывать сей паттерн... (имхо)
            Ответить
            • Если вот реализовать его как-нибудь обобщено в духе boost. Вот это было бы хорошо.
              Ответить
            • Безусловно. Этот паттерн вообще гемморно руками реализовывать, во всяких жабах он реализуется неявно всякими спрингами.
              Ответить
    • void User::AddFriend(User& newFriend)
      {
          friends_.push_back(&newFriend);
          BOOST_SCOPE_EXIT(pDB_, &newFriend)
            if(!::std::uncaught_exception()) 
              pDB_->AddFriend(GetName(), newFriend.GetName());
          BOOST_SCOPE_EXIT_END
      }
      Может вот так лучше всего написать?
      Ответить
      • struct MyAtomicWorld
        {
          MyAtomicWorld(const std::vector<User*> friends, const SmallDBInMemory& DB):friends_(friends), DB_(DB) {}
          std::vector<User*> friends_;
          SmallDBInMemory DB_;
        };
        
        hazard_ptr<MyAtomicWorld> world = make_hazard<MyAtomicWorld>();
        ....
        //Long Time Ago, in a Other Thread Far Far Away:
        Transaction(world, [&](MyAtomicWorld& myWorldCopy)
        {
          myWorldCopy.friends_.push_back(&newFriend);
          myWorldCopy.DB_.AddFriend(GetName(), newFriend.GetName());
        });
        И проблему многопоточной синхронизации решает и проблему исключений решает. LockFree. Потребление памяти правда равно числу_потоков * размер_данных, так что bad_alloc'и будут частыми гостями. :D
        Ответить
        • > Потребление памяти правда равно числу_потоков * размер_данных
          Вроде как опасным указателем изначально предполагалось указывать на мелкие объектики, из которых составлена большая структура данных...
          Ответить
          • Это вот так что ли?
            struct MyAtomicWorld
            {
              MyAtomicWorld(const std::vector<User*> friends, const SmallDBInMemory& DB):friends_(friends), DB_(DB) {}
              bormand_ptr<std::vector<User*> > friends_;
              bormand_ptr<SmallDBInMemory> DB_;
            };
            Резонно. По хорошему ещё и bormand::vector<T> реализованный через std::vector<bormand_ptr<std::vector<T> > >.
            Ответить
            • Что-то по boost::hazard_ptr не гуглится ничего путнего кроме исходников с комментом на китайском. Так что я хз как его юзать :)
              Ответить
              • > boost::hazard_ptr
                Гугли hazard pointer. В бусте такого нет, так как это какой-то питух взял на это авторские права. Типа если вы используете мою мега идею - платите бабки.
                Ответить
                • Да че его гуглить, если он запатентован ;)
                  Ответить
                  • Ну вот видишь, идею ты понял, теперь можно воровать.
                    Ответить
                    • В очередной раз убеждаюсь, что патенты на софтверные идеи - идиотизм.
                      Ответить
                      • Это не дает возможности использовать идеи в отрытых проектах.

                        Какой год не могли добавить в буст этот странник_птр. В результате плюнули на всё и выкатили boost::lockfree без него из-за какого-то патентованого питуха.
                        Ответить
                • Как я понял идею (может быть и неправильно понял...): у каждого треда есть джва списка (или set'а?) - used и done.

                  Пока тред работает с неким объектом он держит указатель на него в своем списке used.

                  Если треду хочется удалить объект, он отстегивает его от структуры данных (локфри способом) и добавляет его в свой список done. Периодически тред мониторит этот список, и освобождает все объекты, которых нет в used у других тредов

                  Вот как-то так.... В общем эти указатели нужны для решения проблемы освобождения в lock-free алгоритмах, и вне их контекста, имхо, бесполезны...
                  Ответить
                  • Есть, кстати, неплохая книжка 2012 года, в которой упоминаются эти указатели - "C++ Concurrency in Action". Легко гуглится и качается нахаляву. Там ещё фишки последнего стандарта обсуждаются довольно подробно.
                    Ответить
                    • Че-то меня смутила фраза на странице 186:
                      The only place that can throw an exception is the construction of the new node, but this will clean up after itself, and the list hasn’t been modified yet, so that’s perfectly safe.

                      Там же утечка будет, если конструктор копирования сфейлится? Либо я туплю, либо автор :)
                      Ответить
                      • Разве new T() утекает, если конструктор Т кидает исключение? Вроде же нет, память освобождается, если конструктор не отработал.
                        Ответить
                        • Не утекает не из-за конструктора копирования не из-за конструктора по умолчанию.
                          Утекает только если:
                          f(new NonThrow, new Throw);
                          В зависимости от порядка конструирования аргументов (а это ID) память или утечет или не утечет.
                          Ответить
                          • Ну здесь уже понятно (ну если подобрать правильный порядок): один объект полностью доконструировался, второй сфейлился, значит первый утек.

                            Спасибо, что-то меня переклинило с этим new. Оно же ничего мне не вернет (т.к. должно перевбросить исключение), а значит само должно справиться с утечкой. Поэтому все норм, и тупанул я, а не автор.
                            Ответить
                        • Да, все ок, я ступил ;)
                          Ответить
              • > boost::hazard_ptr
                > с комментом на китайском
                Это якудза торгуют поддельными ускоренными опасными указателями, покупайте опасные указатели только из проверенных источников
                Ответить
    • А почему нельзя вот так:

      void User::AddFriend(User& newFriend)
      {
      pDB_->AddFriend(GetName(), newFriend.GetName());
      friends_.push_back(&newFriend);
      }
      Ответить
      • Потому что это слишком просто? :)

        Ну или автор боится исключения во время push_back.
        Ответить

    Добавить комментарий