Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Зависает Процесс.ПотокВывода.Прочитать при вызове "долгих" команд #402

Closed
artbear opened this issue Feb 10, 2017 · 23 comments
Assignees

Comments

@artbear
Copy link
Collaborator

artbear commented Feb 10, 2017

В результате расследования зависаний гитсинка с новым 1commands artbear/1commands#14
я нарвался на ошибку движка :(

У меня есть тестовый гит-репо, в котором core.autocrlf = true (это сделано специально!!)

Запускаю тестовый скрипт для этого репо

СтрокаЗапуска = "git add -A .";
Процесс = СоздатьПроцесс(СтрокаЗапуска, ".", Истина, Истина);
Процесс.Запустить();

ПериодОпросаВМиллисекундах = 200;
сообщить(20);
Если ПериодОпросаВМиллисекундах <> 0 Тогда
	Приостановить(ПериодОпросаВМиллисекундах);
КонецЕсли;
сообщить(30);
Если НЕ Процесс.Завершен ИЛИ Процесс.ПотокВывода.ЕстьДанные Тогда
	// Пока НЕ Процесс.Завершен ИЛИ Процесс.ПотокВывода.ЕстьДанные Цикл
	Если ПериодОпросаВМиллисекундах <> 0 Тогда
		Приостановить(ПериодОпросаВМиллисекундах);
	КонецЕсли;
	сообщить(40);
	
	ОчереднаяСтрокаВывода = Процесс.ПотокВывода.Прочитать(); // ВОТ ЗДЕСЬ ЗАВИСАЕМ !!
	сообщить(50); // TODO сюда выполнение не доходит!!!!!

	ОчереднаяСтрокаВывода = СтрЗаменить(ОчереднаяСтрокаВывода, Символы.ВК, "");
	сообщить(60);
	// Лог.Отладка("%2%1", ОчереднаяСтрокаВывода, Символы.ПС);
	// сообщить(70);
	
	// ЗаписьXML.ЗаписатьБезОбработки(ОчереднаяСтрокаВывода);
	сообщить(80);
КонецЕсли;
// КонецЦикла;

сообщить(90);

Как видно из кода, до Сообщить(50) выполнение не доходит, т.к. скрипт зависает вместе с гитом!

При этом при ручном запуске git add -A . выдает кучу сообщений по отличия CrLf для каждого файла и через небольшое время завершается успешно.

/cc @dmpas @EvilBeaver Помогите!!
Без вашей помощи никак.

Прикладываю тестовый архив. Там и файлы, и тест.скрипт, и батник для его запуска :)
process-bug.zip

@artbear
Copy link
Collaborator Author

artbear commented Feb 10, 2017

Приостановить можно убрать, он на зависание не влияет.

@EvilBeaver
Copy link
Owner

До исправления задачи по апи потоков ничего 100% хорошего не выйдет. Могу порекомендовать использовать не Прочитать, а ПрочитатьСтроку

@EvilBeaver
Copy link
Owner

А 100 вариант использовать перенаправление в файл

@khorevaa
Copy link
Contributor

А возможность перенаправить в файл есть?

@nixel2007
Copy link
Collaborator

ПрочитатьСтроку точно так же зависает. От него как раз и перешли Прочитать, чтобы реже вызывать чтение из буфера

@EvilBeaver
Copy link
Owner

Бяда. Задачка на это есть, надо уже сделать. Там просто низкоуровневое все и тестировать тяжело. Займусь

@JohnyDeath
Copy link
Contributor

JohnyDeath commented Feb 11, 2017

Можно чуть проще и без гита:

Функция ВыполнитьДинамически(КолИтераций)
	ИмяВремФайла = ПолучитьИмяВременногоФайла("os");
	ТекстФайла = СтрШаблон("Сообщить("">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Количество итераций %1"");", КолИтераций);
	Для й = 1 По КолИтераций Цикл
		ТекстФайла = ТекстФайла + СтрШаблон("Сообщить(""Итерация № %1"");", й) + Символы.LF;
	КонецЦикла;

	Текст = Новый ТекстовыйДокумент;
	Текст.ДобавитьСтроку(ТекстФайла);
	Текст.Записать(ИмяВремФайла);

	СтрокаЗапуска = "oscript " + ИмяВремФайла;
	Процесс = СоздатьПроцесс(СтрокаЗапуска, , Истина, Истина);
	Процесс.Запустить();

	Пока НЕ Процесс.Завершен ИЛИ Процесс.ПотокВывода.ЕстьДанные Цикл

		ОчереднаяСтрокаВывода = Процесс.ПотокВывода.ПрочитатьСтроку(); 
		сообщить("Из процесса: " + ОчереднаяСтрокаВывода); 

	КонецЦикла;

	УдалитьФайлы(ИмяВремФайла);
КонецФункции

ВыполнитьДинамически(50000);

ВыполнитьДинамически(10000); - выполняется
ВыполнитьДинамически(50000); - не дождался

Вообще я ожидал, что буду видеть процесс накапливания сообщений вида "Из процесса: Итерация №---"
Но в итоге все сообщения вываливаются разом в конце. Это какая-то особенность шелла/vsc.. ?

@nixel2007
Copy link
Collaborator

В всц 1.9 переделали терминал в сторону быстрого непосредственного вывода, но все равно такие вещи лучше тестировать прямо из консоли

@JohnyDeath
Copy link
Contributor

В консоли тоже самое (проверяю на 10000). Сначала висит, потом скопом выводит все строки

@dmpas
Copy link
Collaborator

dmpas commented Feb 11, 2017

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

@EvilBeaver
Copy link
Owner

@JohnyDeath а вот формировать в памяти построчную строку из 100500 конкатенаций - это жестоко :) В таких случаях лучше сразу писать в файл через ЗаписьТекста, а не мучить выделятор памяти.

@EvilBeaver EvilBeaver self-assigned this Feb 12, 2017
@EvilBeaver
Copy link
Owner

Закрываю:

Реализовал неблокирующее чтение из потоков процесса.
Коммиты: 65f0c46 c4be395

Особенность (возможно Breaking change):

Методы ЕстьДанные Прочитать и ПрочитатьСтроку возвращают результат немедленно, показывая "Срез" на момент вызова. Отсутствие данных не означает конца потока. Оно означает, что на момент вызова в потоке данных не было.

Данные в потоке появляются "построчно". Т.е. не будет оборванных на середине строк.

@nixel2007
Copy link
Collaborator

По поводу breaking change - вроде у нас в скриптах всегда идёт дополнительная проверка на Процесс.Завершен, так что не должно поломать что-либо

@nixel2007
Copy link
Collaborator

Отсутствие данных не означает конца потока. Оно означает, что на момент вызова в потоке данных не было.

А раньше было не так?

@EvilBeaver
Copy link
Owner

Раньше блокировалось и ждало пока данные поступят. Сейчас моментально возвращает результат. Часто пустой.

@EvilBeaver
Copy link
Owner

И по поводу Процесс.Завершен. Как только он завершен обязательно надо прочитать еще раз. Там хвост данных часто приезжает с задержкой уже после остановки процесса. Т.е. для длительного процесса цикл Пока Не Завершен () и потом еще раз Прочитать. Для короткого можно ждать щавершения и потом читать.

@nixel2007
Copy link
Collaborator

@artbear - обрати внимание на сообщение @EvilBeaver

@artbear
Copy link
Collaborator Author

artbear commented Feb 13, 2017

@EvilBeaver Проверил поведение на последнем ночнике.
Указанный мной код на указанном архиве стал проходить.

Но когда в этом коде я вместо Если вернул Пока (закомментировано), баг остался :(

Проверял варианты

  • Пока НЕ Процесс.Завершен ИЛИ Процесс.ПотокВывода.ЕстьДанные Цикл
  • Пока НЕ Процесс.Завершен Цикл

Висим :(

Посмотришь?

PS предлагаю переоткрыть ишуз!

@EvilBeaver
Copy link
Owner

EvilBeaver commented Feb 13, 2017

@artbear сбрось сюда конечный вариант кода, который зависает.
И на какой строчке

@artbear
Copy link
Collaborator Author

artbear commented Feb 19, 2017

@EvilBeaver смотри TODO

СтрокаЗапуска = "git add -A .";
Процесс = СоздатьПроцесс(СтрокаЗапуска, ".", Истина, Ложь);
Процесс.Запустить();

ПериодОпросаВМиллисекундах = 200;
сообщить(20);
Если ПериодОпросаВМиллисекундах <> 0 Тогда
	Приостановить(ПериодОпросаВМиллисекундах);
КонецЕсли;
сообщить(30);
//Если НЕ Процесс.Завершен ИЛИ Процесс.ПотокВывода.ЕстьДанные Тогда
Индекс = 0;
Пока НЕ Процесс.Завершен ИЛИ Процесс.ПотокВывода.ЕстьДанные Цикл
	Индекс = Индекс + 1;
	// Пока НЕ Процесс.Завершен ИЛИ Процесс.ПотокВывода.ЕстьДанные Цикл
	Если ПериодОпросаВМиллисекундах <> 0 Тогда
		Приостановить(ПериодОпросаВМиллисекундах);
	КонецЕсли;
	сообщить("40 - " + Индекс);
	
	ОчереднаяСтрокаВывода = Процесс.ПотокВывода.Прочитать();
	сообщить(50); // TODO сюда выполнение не доходит!!!!!

	ОчереднаяСтрокаВывода = СтрЗаменить(ОчереднаяСтрокаВывода, Символы.ВК, "");
	сообщить(60);
	// Лог.Отладка("%2%1", ОчереднаяСтрокаВывода, Символы.ПС);
	// сообщить(70);
	
	// ЗаписьXML.ЗаписатьБезОбработки(ОчереднаяСтрокаВывода);
	сообщить(80);
//КонецЕсли;
КонецЦикла;

сообщить(90);

@artbear
Copy link
Collaborator Author

artbear commented Feb 19, 2017

Для приложенного к шапке архива

@artbear
Copy link
Collaborator Author

artbear commented Feb 19, 2017

Итак, правильное решение - читать поток вывода и поток ошибок.
Нужно задокументировать анализ @EvilBeaver

Процесс не завершается, пока поток stderr открыт для чтения
Процесс не завершен. Он висит в процессах и ждет пока ты освободишь его от текста в stderr

Правильный код:

Пока НЕ Процесс.Завершен ИЛИ Процесс.ПотокВывода.ЕстьДанные ИЛИ Процесс.ПотокОшибок.ЕстьДанные Цикл
			Если ПериодОпросаВМиллисекундах <> 0 Тогда
				Приостановить(ПериодОпросаВМиллисекундах);
			КонецЕсли;
сообщить(40);

			ОчереднаяСтрокаВывода = Процесс.ПотокВывода.Прочитать();
			ОчереднаяСтрокаОшибок = Процесс.ПотокОшибок.Прочитать();
			Если Не ПустаяСтрока(ОчереднаяСтрокаВывода) Тогда
				Сообщить(ОчереднаяСтрокаВывода, СтатусСообщения.Информация);
			КонецЕсли;
			
			Если Не ПустаяСтрока(ОчереднаяСтрокаОшибок) Тогда
				Сообщить(ОчереднаяСтрокаОшибок, СтатусСообщения.Важное);
			КонецЕсли;
сообщить(50);
			
сообщить(60);
			// Лог.Отладка("%2%1", ОчереднаяСтрокаВывода, Символы.ПС);
// сообщить(70);

			// ЗаписьXML.ЗаписатьБезОбработки(ОчереднаяСтрокаВывода);
сообщить(80);
		КонецЦикла;

artbear added a commit to artbear/1commands that referenced this issue Feb 19, 2017
+ Методы по управления кодировкой вывода для КомадногоФайла
+ Зависание исправлено согласно решению в EvilBeaver/OneScript#402 (comment)
@artbear
Copy link
Collaborator Author

artbear commented Feb 19, 2017

Теперь эта магия :) запрятана в 1commands!

Управляется методом ПоказыватьВыводНемедленно, который и вызывает код, аналогичный коду выше.

artbear added a commit to artbear/OneScript that referenced this issue Feb 21, 2017
EvilBeaver added a commit that referenced this issue Feb 23, 2017
Дока на пример правильной обработки цикла ожидания завершения процесса #402
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants