Обновить
129
0
Григорий Демченко@gridem

Software Engineer

Отправить сообщение
Я там ответил, что это раскрыто в следующей статье. Конкретно: в объект An можно заливать любую реализацию, будь то синглтон, созданный фабрикой или что-то еще. Это задача использующего, а не используемого, обеспечить правильный экземпляр. Этот подход является продолжением DIP — Dependency inversion principle.
На мой взгляд это — пример плохого дизайна приложения. В принципе, все действия можно запихать в конструктор, только зачем? Задача конструктора — инициализация, а не выполнение каких-либо действий вообще говоря. Встречаются исключения, типа scoped операций по типу lock/unlock, или rollback в случае исключений, но в основном конструктор — он для конструирования, как бы банально это ни звучало.
Да, действительно, такое может произойти. Я поправил реализацию. Теперь запись указателя происходит вне блокировок, поэтому результат создания объекта будет закомичен в памяти до того, как произойдет проверка указателя. Спасибо за наводку.
А можно ссылочку на соответствующий параграф в стандарте? Для более конструктивной беседы.
Если я правильно понял, под флагом имелся ввиду указатель на объект. Можно обратить внимание на эту строчку:

pt = &singleUnsafe<T>();

Здесь возвращается указатель на объект. Задача компилятора гарантировать, что результат функции singleUnsafe будет содержать валидный, т.е. проинициализированный объект. Иначе в случае однопоточного приложения будут те же грабли: если нам возвращается недоконструированный объект, то в следующий момент использования возможен креш. Затем мы этот указатель на валидный объект и присваиваем.
Чтобы ему увидеть флаг, надо сначала взять лок. Соответственно, задача лока — правильно сбросить кеши, в противном случае этот лок будет приводить к гонкам.
Собственно, это придумал не я. Можно ознакомиться со статьей: Double-Checked Locking Optimization

Example:

The Singleton object is not created yet and the instance is NULL.

Thread 1: is creating the Singleton object (the Singleton is not created in the virtual memory yet) due to optimizing actions taken by the compiler.

m_instance = new SingletonObject( )

However, the pointer to the Singleton is already created and is not NULL.

Thread 2: this thread gets focus, and will not fall through the first conditional check since the pointer is valid. As already mentioned before, the Singleton object is not created in the memory yet and the instance is returned.

Thread 2: will crash using this pointer, since it’s pointing to a memory which is not allocated yet.
Можно и это использовать. На мой взгляд, это не принципиально, подход остается ровно таким же.
Чтобы синглтоны не убили архитектуру и тестирование, советую почитать первую статью на эту тему: Использование паттерна синглтон.
Возможно, стоило как-то по-другому сформулировать. Просто я в своей профессиональной деятельности работал только с многопоточными приложениями. Стоило привести полную цитату:
В настоящий момент сложно себе представить программное обеспечение, работающее в одном потоке. Конечно, существует ряд простых задач, для которых один поток более, чем достаточен. Однако так бывает далеко не всегда и большинство задач средней или высокой сложности так или иначе используют многопоточность.

С memory ordering прикол в том, что в данном подходе не важно, когда произошла запись в память правильного адреса. Если он по каким-то причинам отложился, то мы войдем вовнутрь условия и возьмем лок, что по-любому приведет к правильному считыванию указателя. Главное — это атомарность записи адреса в нужную ячейку, что практически всегда выполняется.
Верно. Именно так и написано в статье:
В каком-то смысле здесь происходит тоже 2 проверки, только первая проверка вне блокировки использует указатель, а вторая — внутреннюю переменную, которая сгенерирована компилятором.
Я же оговорился в начале статьи, что это не классический GC. И уж тем более не пул памяти. Придумайте название, скажу спасибо.

Это называется объектный пул. Единственное отличие — это в том, что объекты пулом не создаются, а только им уничтожаются. А вообще, можно, конечно, оговариваться, но заголовок «Простая и эффективная «сборка мусора» (garbage collection) на C++» явно не соответствует содержанию.
Возможно, тут спутали 2 вещи: pick и peek. Если первая переводится как «отбирать, срывать», то вторая как «заглядывать». Также я бы сделал одно дополнение: pick и move семантика немного отличаются друг от друга: обе производят разрушающее копирование, однако move очищает исходный объект, в то время как pick переводит его в «особое» состояние, описанное чуть выше.
Небольшой комментарий по поводу контейнеров U++. Данные контейнеры реализуют так называемую pick семантику, т.е. традиционная операция копирования делает перемещение объекта в новый. При этом старый объект помечается специальным образом таким образом, чтобы его нельзя было использовать без предварительного очищения, что позволяет задетектировать ошибку неправильного использования. При этом присутствует возможность и глубокого копирования, однако по умолчанию применяется именно pick семантика. Надо также отметить, что подобный подход есть и в новом стандарте, для этого надо выполнить следующую конструкцию:

std::vector<A> va1;
va1.push_back(A(...));
...
std::vector<A> va2 = std::move(va1);


При этом va2 становится владельцем контейнера va1, а va1 становится пустым. Преимущество такого подхода в том, что такая операция переноса практически ничего не стоит. Это же относится и к pick семантике.
Вам в комментариях к статье уже намекнули на то, что у вас не GC, а всего лишь пул памяти.

Когда я вижу эту конструкцию (взята из предыдущей статьи):

new(allocator) Array(allocator);


то меня слегка передергивает: мало того, что аллокатор передается в двух местах, так и каждая функция и контейнер должны включать в себя этот аллокатор. Для больших проектов это просто неприемлимо. Здесь же представлен простой и понятный концепт: есть владелец, который может измениться, если это необходимо, что предотвращает утечки.
Использую U++ в своих проектах. Могу сказать, что на первый взгляд U++ выглядит достаточно простовато, однако, когда начинаешь копать детали, то понимаешь, что библиотека очень мощная и необычная. Чего стоит pick семантика, которая необходима для получения более быстрого кода, встроенный lock-free аллокатор с возможностью отлова утечек памяти. И все это уже встроено в библиотеку!
А можно поподробнее? Где Microsoft заявляла, что WinAPI на ARM не будет? Другое дело, что его нельзя будет использовать на ARM-устройствах, в отличие от x86-64.

Вот здесь я вижу обратную картину: тыц.
Есть мнение, что указанная диаграмма компонент не совсем правильно отражает реальную действительность. Подробнее можно посмотреть здесь: тыц. Вкратце: WinRT может базироваться на WinAPI как и все остальные обертки.
Кстати, масса от скорости не зависит. Можно ввести некую «эффективную массу» для релятивистских случаев, но масса от скорости не зависит.

Информация

В рейтинге
Не участвует
Дата рождения
Зарегистрирован
Активность