Войти в систему

Home
    - Создать дневник
    - Написать в дневник
       - Подробный режим

LJ.Rossia.org
    - Новости сайта
    - Общие настройки
    - Sitemap
    - Оплата
    - ljr-fif

Редактировать...
    - Настройки
    - Список друзей
    - Дневник
    - Картинки
    - Пароль
    - Вид дневника

Сообщества

Настроить S2

Помощь
    - Забыли пароль?
    - FAQ
    - Тех. поддержка



Пишет yigal_s ([info]yigal_s)
@ 2006-01-25 13:30:00


Previous Entry  Add to memories!  Tell a Friend!  Next Entry
программистское
попал на забавный клин в синглетоне: функция getInstance() может быть вызвана из-под конструктора, вызваного опять же из функции getInstance() для того же самого класса. Не обязательно напрямую, разумеется, а через цепочку вызовов. В моём случае всё было еще сложнее: getInstance() обратилась к кому-то там через COM, и пока вызов отрабатывался, в тот же тред пришел другой COM-вызов (вот не понимаю, зачем микрософт такое разрешил), приведший к новому вызову getInstance().

Иными словами
1. функция getInstance для класса T вызвается в самый первый раз
2. вызывается конструктор класса Т
3. конструктор вызывает еще что-то-там
4. что-то-там вызывает getInstance для класса Т


Вроде бы, ни в одной книжной имплементации синглетона я не видел защиты от подобного сценария, или упоминания о нём.


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


[info]solomon2@lj
2006-01-25 08:40 (ссылка)
Против (бесконечно-рекурсивного) лома нет приема :-)

(Ответить)


[info]dimrub@lj
2006-01-25 08:44 (ссылка)
Можно сериализовать COM запросы в поток, используя STA.

(Ответить) (Ветвь дискуссии)


[info]yigal_s@lj
2006-01-25 10:13 (ссылка)
так нет же, если бы речь шла об MTA, то всё бы сработало, быть может.

Проблема как раз и была в STA: когда STA делает call наружу, она ожидает его результата, но при этом может принять не только callback (что естественно), но и внешний call (с иной causality).

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

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dimrub@lj
2006-01-25 10:21 (ссылка)
Ну, в таком случае, наверное не стоит делать так, чтобы конструктор синглтона делал какие-то сложные вещи (вроде вызовов COM). Ну и можно, в принципе, поставить защиту в getInstance, чтобы он не только проверял равенство статического указателя нулю, но еще и изменял атомным образом какую-то статическую переменную - тогда и до возврата конструктора getInstance будет знать, что он уже был вызван - и сможет, например, вернуть ошибку.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]yigal_s@lj
2006-01-25 10:34 (ссылка)
Ну, вообще говоря, определить, что такое "сложные вещи" - довольно сложно. Представьте себе, к примеру, библиотечный класс синглетон с документацией вроде "синглетон не должен вызывать... что?!"

Лучше уж такую ошибку отлавливать, что и было сделано, естественно.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dimrub@lj
2006-01-25 10:37 (ссылка)
Библиотечный класс синглетон doesn't sound like a good idea :)

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]yigal_s@lj
2006-01-25 10:44 (ссылка)
почему вы так считаете?
по-моему, ровно наоборот.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dimrub@lj
2006-01-25 10:47 (ссылка)
Есть основания полагать, что легкость использования синглетона не позволит некоторым программистам вовремя подумать о его уместности. Хотя, конечно, от конкретных обстоятельств многое зависит.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]yigal_s@lj
2006-01-25 11:52 (ссылка)
можете привести пример, когда синглетон неуместен, но кто-то тем не менее может попытаться его применить?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dimrub@lj
2006-01-25 11:56 (ссылка)
Ну, много может быть ситуаций, в которых программист в начале работы полагает, что у него может быть только одна штука какого-то объекта, а потом требования меняются - и ему приходится много чего переделывать.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]yigal_s@lj
2006-01-25 12:03 (ссылка)
Да, согласен.

Но тогда выходит, что синглетон лучше вообще не применять, не так ли? (ну, кроме как для возврата чего-то подобного Class Object в С++).
Или есть еще какие-то критерии?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dimrub@lj
2006-01-25 12:14 (ссылка)
Честно говоря, затрудняюсь придумать какие-то общие правила. Думаю, тут каждый должен сам решать, в каких направлениях его проект может развиться - и чего ему это будет стоить.

(Ответить) (Уровень выше)


[info]disbalanced@lj
2006-01-25 11:07 (ссылка)
Есть в этом что-то. Если в ходе конструирования объекта предполагается, что он уже сконструирован, налицо прокол в (извините за выражение) дизайне. Интересно понять, как такую ситуацию корректно отработать - ведь возврат ошибки означает провал конструирования синглетона из-за того, что его на тот момент не существовало :) (не говоря уже о милой проблеме возврата ошибки из конструктора).

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dimrub@lj
2006-01-25 11:11 (ссылка)
Как раз с возвратом ошибки из конструктора нет проблем: конструктор бросает исключение, которое getInstance ловит и обрабатывает. Проблема именно с вызовом getInstance в процессе работы конструктора - из другого потока.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]disbalanced@lj
2006-01-25 11:14 (ссылка)
Из другого потока не страшно - можно подождать. Проблема возникает тогда, когда дело происходит в том же самом потоке. Кстати, а как getInstance может обработать такое исключение? Вернёт NULL? А если она возвращает не указатель, а reference?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dimrub@lj
2006-01-25 11:18 (ссылка)
Из другого потока не страшно - можно подождать.

Ну, это уже зависит от конкретной задачи. Если задача позволяет подождать - можно и подождать (вот только на чем ждать - семафор какой-то нужен, да? А где его инициализировать? Курица и яйцо).

Кстати, а как getInstance может обработать такое исключение? Вернёт NULL? А если она возвращает не указатель, а reference?

Либо может пробросить его (исключение) дальше - либо должен возвращать статус, а референс - через out argument.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]disbalanced@lj
2006-01-25 11:26 (ссылка)
Ну, это уже зависит от конкретной задачи. Если задача позволяет подождать - можно и подождать.
Ну, задача-то вряд ли может НЕ позволить ждать, поскольку для её решения нужен объект, который пока не сконструирован. Если речь идёт о срочной задаче, то там возврат ошибки выглядит совершенно естественно - для решения нужен объект, недоступный в допустимых временных рамках. Другое дело - объект, который принципиально не может быть сконструирован, ибо процесс его конструкции предполагает наличие его уже сконструированного и готового к употреблению...

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

Либо может пробросить его (исключение) дальше - либо должен возвращать статус, а референс - через out argument.
А Вы бы сами хотели пользоваться такой функцией getInstance? :)

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dimrub@lj
2006-01-25 11:30 (ссылка)
Ну, задача-то вряд ли может НЕ позволить ждать

Задачи разные бывают. Можно себе представить задачу, в которой вторичный вызов getInstance в процессе создания синглетона является ошибкой - и должен закончиться остановкой программы. О цикличном вызове я и не говорю - это просто тривиальный (ну, или не очень тривиальный) баг.

А Вы бы сами хотели пользоваться такой функцией getInstance? :)

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

(Ответить) (Уровень выше)


[info]peresmeshnik@lj
2006-01-25 09:50 (ссылка)
Ужос этот ваш С++...

(Ответить) (Ветвь дискуссии)


[info]yigal_s@lj
2006-01-25 10:14 (ссылка)
при чем тут С++? абсолютно не понимаю.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]peresmeshnik@lj
2006-01-25 10:35 (ссылка)
А что, это не С++? Звучит как типичное описание сидвуплясной штуки: синглтон + конструктор + класс.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]yigal_s@lj
2006-01-25 10:43 (ссылка)
это было написано на С++

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

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]peresmeshnik@lj
2006-01-25 10:50 (ссылка)
Да, конечно.
Но обычно на ассемблере ОО не пишут (хотя, естественно, можно, и даже, более того, это просто). И на С ОО используется самое большее на стадии дизайна. Как-то С++ больше для ОО подходит (о Жабе я и не говорю, она для другого не приспособлена).

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]yigal_s@lj
2006-01-25 11:03 (ссылка)
С++ больше подходит для ОО, но описанные здесь проблемы порождаются не парадигмами С++, и даже, по большому счету, не парадигмами ОО.

(Ответить) (Уровень выше)


[info]dimrub@lj
2006-01-25 11:12 (ссылка)
Я писал синглтоны на Яве. Точно так же их можно писать (и пишут) на C# и на многих других OO языках.

(Ответить) (Уровень выше)


[info]dimrub@lj
2006-01-25 11:12 (ссылка)
Да, и на C, кстати, тоже.

(Ответить) (Уровень выше)