Здравствуйте, _Raz_, Вы писали:
_R_>Ну вот ты реально только на первые части фраз внимание обращаешь? Как твой ответ про мою упертость и поддержку ИДЕ в Нитре, связана с "Зачем в Нитре повторять то, что сделано из-за ограничений Electron'а?"
Вот из чего сделан вывод, что сделано "как где-то" или "из-за ограничений Electron-а"?
Предпринимаю последнюю попытку объяснить предпосылки и показать как делается выбор.
Мы имеем задачу выбора дизайнерского решения. Нам нужно создать универсальное решение позволяющее с минимумом затрат создавать IDE-плагины для языков разрабатываемые на базе Nitra.
Какие цели мы имеем?
Цели
1. Многоплатформность и переносимость. Одна из целей Nitra позволить в будущем создавать переносимые решения. На разных платформах имеются разные IDE API которых координально отличаются. Более того у этих IDE отличаются и программные платформы API. Это может быть .Net (для VS и SharpDevelop), Tipe Script/Java Script (для VS Code), Java (для IDEA, Eclipse, NetBeans и еще более 50-и) и нативные API для кучи других IDE.
Сейчас мы реализовывать движки для всех этих IDE не будем, но нам надо принять дизайнерское решение упрощающее эту задачу в будущем.
2. Поддержка VS с его 32-битностью. Приоритетной IDE для нас является VS (Microsoft Visual Studio) хотя бы потому, что мы сами ее используем и это основная IDE для .Net-разработки.
У VS есть особенность — она 32-битная. Это значит, что адресное пространство у нас ограничено. В это пространство грузятся интеграции ко всем языкам (включая огромный Разор для C#), а так же плагины вроде ReSharper, которые сами по себе могут отъедать всю память (все адресное пространство) в процессе.
3. Надежность. Nitra — это исходно расширяемая среда. Мы проектируем ее в расчета на то, что когда-нибудь она может быть перенесена на другие платформы. Среди платформ могут быть и небезопасные нативные платформы на основе LLVM или генерации С-кода. Пользовательские расширения могут содержать баги. Застраховаться от вылетов в режиме разработки мы не можем. Но нам нужно как-то защитить код от повреждений в случае некорректной работы пользовательских расширений.
4. Многопоточность. Мы хотим получать преимущества от наличия нескольких процессоров в одной системе. Их число постоянно растет и хотелось бы создать решение, которое будет хорошо масштабироваться. Стало быть мы хотим использовать в IDE-движке и компиляторах многопоточность. При этом наша задача добиться максимального контроля над многопоточной обработкой и при этом не очень пере усложнять код. Использование ручных блокировок, разных async/await-ов хотя и упрощает задачу, но не уберегает от ошибок. Нам же желательно иметь решение, которое было бы сравнимо с однопоточной разработкой по простате, но давало бы нам преимущества многопоточной обработки.
5. Чистота API. Мы хотим добиться максимальной чистоты API нашего движка IDE: четкого отделения движка (по сути являющегося контроллером) от представления, наличия четких мест общения движка с IDE, минимизации API, гарантии того, минимальной связанности движка и внешнего года.
6. Web-интерфейс. Для продвижения языков, да и самой Nitra, нам выгодно реализовать в будущем вариант работы через Web. В идеале мы должны предоставлять простенькую микро-IDE позволяющую человеку не устанавливая ничего на своей машине поиграться с Nitra или любым языком созданным на базе Nitra.
Поиск решения
Далее мы начинаем искать решение удовлетворяющее всем этим условиям. При поиске мы обращаем внимание на решения имеющиеся у других разработчиков. Находим, что значительная часть этих решений мигрирует к клиент-серверному подхода. Пробуем понять почему это так и приходим к выводу о том, что клиент-срверное решение (в купе с реализацией многопоточности на базе системы акторов) позволяет (потенциально) достичь всех наших целей.
Реализуя многопоточность на основе передачи событий и обработки очередей в разных потоках мы избавляемся от необходимости явной синхронизации. Вместо этого мы вводим синхронизирующий монопольный поток, который осуществляет все операции, которые нужно делать синхронно. Он получает сообщения от рабочих потоков и передает сообщения им же. Передача эта осуществляется асинхронно и не требует явных блокировок. Все что нужно сделать, чтобы сообщения можно было передавать из других процессов — это создать систему сериализации сообщений. Учитывая требование кросплатформности и переносимости сериализация должна быть осуществлена или какими-то кросплатформными средствам (не дотнет-ными), или прямо нами. Я выбрал ручную бинарную сериализацию на основе макросов. Возможно я сделал не самый правильный выбор. Сейчас я склоняюсь к тому, что вместо бинарной сераилизации лучше было сделать сериализацию в JSON или XML. В прочем, это не сложно будет переделать в будущем, когда дойдут руки. Все что для этого нужно сделать — переписать макрос серилизации.
Теперь берем каждую из целей и задаемся вопросом реализуем ли она в виде внутри-процессной библиотеки? Уже на вопросе о 32-битном адресном пространстве мы получаем отрицательный ответ. Ряд других целей хотя теоретически и достижимы при реализации в виде внутри-процессной библиотеки, но могут потребовать написания кучи кода для обхода проблем.
Далее проверяем насколько легко достигаются эти цели при выборе клиент-серверной реализации. Оказывается, что все они не только достигаются, но и достигаются проще нежели при выборе внутри-процессного решения. Особенно хорошо на клиент-сервер ложатся пункты 2, 3 и 6. 32-битность не помеха.
Даже в 32-битной системе движок Нитры будут иметь собственное адресное пространство не конфликтующее с движками других языков.
Надежность у такого решения выше, так как даже фатальных сбоях вроде: порчи памяти, переполнения стэка или переполнения памяти можно будет тупо срубить весь процесс и запустить его вновь.
Работа через Веб сама по себе является клиент-серверным решением, что ложится на выбранный подход 1 в 1.
Остается проверить остальные пункты.
Многопоточность, будучи реализованная на базе модели акторов, прозрачным образом масштабируется с локальной на межпроцессную и даже на межкопьютерную. Единственная проблема данной модели — у межпроцессных и межмашинных интерфейсов высокая латентность. Стало быть нам нужно обеспечить общение между клиентом и сервером как можно более крупными блоками передающимися относительно редко. То есть не стоит делать 100500 вызовов для решения одной задачи, а надо передавать одно сообщение с данными и обрабатывать его на сервере возвращая относительно крупный результат. Кроме того реализация должна быть максимально асинхронной и кэшировать информацию на клиенте. Это будет сглаживать латентность.
Выбранное решение позволяет упростить достижение и пункта 5. Ведь модель акторов заставляет описать все взаимодействие между акторами в виде сериализуемых сообщений. А это гарантирует слабую связанность, четкое описание всего API и прочие плюшки чистого API. Таким образом сам выбор клиент-серверного подхода в купе с моделью акторов упрощает достижение этой цели и гарантирует чистоту.
ЗЫ
Мне кажется, что теперь я разжевал все до мелочей. Все кто способен понять и хочет это сделать, думаю, поняли. Прошу тех кому все еще хочется полезть в бутылку и поспорить просто зачислить меня в клинические идиоты и не посещать данный форум. Я по про прежнему готов отвечать на разумные вопросы. Но очень не хочу повторяться вновь. Это занимает много времени и несет мало пользы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.