Skip to content

Commit 5c04b92

Browse files
authored
Merge pull request #1202 from arkuznetsov/feature/streamTimeout18
Таймауты для чтения/записи потоков 1.8
2 parents b716f5f + c4f24af commit 5c04b92

File tree

6 files changed

+285
-13
lines changed

6 files changed

+285
-13
lines changed

src/ScriptEngine.HostedScript/Library/Binary/FileStreamContext.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ public FileStreamContext(string fileName, FileStream openedStream)
7474
[ContextProperty("ДоступноИзменениеПозиции", "CanSeek")]
7575
public bool CanSeek => _underlyingStream.CanSeek;
7676

77-
7877
/// <summary>
7978
///
8079
/// Признак доступности чтения из потока.
@@ -83,6 +82,37 @@ public FileStreamContext(string fileName, FileStream openedStream)
8382
[ContextProperty("ДоступноЧтение", "CanRead")]
8483
public bool CanRead => _underlyingStream.CanRead;
8584

85+
/// <summary>
86+
///
87+
/// Признак доступности установки таймаута чтения/записи в потоке.
88+
/// </summary>
89+
/// <value>Булево (Boolean)</value>
90+
[ContextProperty("ДоступенТаймаут", "CanTimeout")]
91+
public bool CanTimeout => _underlyingStream.CanTimeout;
92+
93+
/// <summary>
94+
///
95+
/// Время в миллисекундах, отведенное потоку на операцию чтения.
96+
/// </summary>
97+
/// <value>Число (int)</value>
98+
[ContextProperty("ТаймаутЧтения", "ReadTimeout")]
99+
public int ReadTimeout
100+
{
101+
get => _underlyingStream.ReadTimeout;
102+
set => _underlyingStream.ReadTimeout = value;
103+
}
104+
/// <summary>
105+
///
106+
/// Время в миллисекундах, отведенное потоку на операцию записи.
107+
/// </summary>
108+
/// <value>Число (int)</value>
109+
[ContextProperty("ТаймаутЗаписи", "WriteTimeout")]
110+
public int WriteTimeout
111+
{
112+
get => _underlyingStream.WriteTimeout;
113+
set => _underlyingStream.WriteTimeout = value;
114+
}
115+
86116
/// <summary>
87117
/// Содержит полное имя файла, включая путь
88118
/// </summary>

src/ScriptEngine.HostedScript/Library/Binary/GenericStream.cs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,44 @@ public GenericStream(Stream underlyingStream, bool readOnly)
6464
[ContextProperty("ДоступноИзменениеПозиции", "CanSeek")]
6565
public bool CanSeek => _underlyingStream.CanSeek;
6666

67-
6867
/// <summary>
6968
///
7069
/// Признак доступности чтения из потока.
7170
/// </summary>
7271
/// <value>Булево (Boolean)</value>
7372
[ContextProperty("ДоступноЧтение", "CanRead")]
74-
public bool CanRead => _underlyingStream.CanRead;
75-
73+
public bool CanRead => _underlyingStream.CanRead;
74+
75+
/// <summary>
76+
///
77+
/// Признак доступности установки таймаута чтения/записи в потоке.
78+
/// </summary>
79+
/// <value>Булево (Boolean)</value>
80+
[ContextProperty("ДоступенТаймаут", "CanTimeout")]
81+
public bool CanTimeout => _underlyingStream.CanTimeout;
82+
83+
/// <summary>
84+
///
85+
/// Время в миллисекундах, отведенное потоку на операцию чтения.
86+
/// </summary>
87+
/// <value>Число (int)</value>
88+
[ContextProperty("ТаймаутЧтения", "ReadTimeout")]
89+
public int ReadTimeout
90+
{
91+
get => _underlyingStream.ReadTimeout;
92+
set => _underlyingStream.ReadTimeout = value;
93+
}
94+
/// <summary>
95+
///
96+
/// Время в миллисекундах, отведенное потоку на операцию записи.
97+
/// </summary>
98+
/// <value>Число (int)</value>
99+
[ContextProperty("ТаймаутЗаписи", "WriteTimeout")]
100+
public int WriteTimeout
101+
{
102+
get => _underlyingStream.WriteTimeout;
103+
set => _underlyingStream.WriteTimeout = value;
104+
}
76105

77106
/// <summary>
78107
///

src/ScriptEngine.HostedScript/Library/Binary/MemoryStreamContext.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ public static MemoryStreamContext Constructor()
9393
[ContextProperty("ДоступноИзменениеПозиции", "CanSeek")]
9494
public bool CanSeek => _underlyingStream.CanSeek;
9595

96-
9796
/// <summary>
9897
///
9998
/// Признак доступности чтения из потока.
@@ -102,6 +101,36 @@ public static MemoryStreamContext Constructor()
102101
[ContextProperty("ДоступноЧтение", "CanRead")]
103102
public bool CanRead => _underlyingStream.CanRead;
104103

104+
/// <summary>
105+
///
106+
/// Признак доступности установки таймаута чтения/записи в потоке.
107+
/// </summary>
108+
/// <value>Булево (Boolean)</value>
109+
[ContextProperty("ДоступенТаймаут", "CanTimeout")]
110+
public bool CanTimeout => _underlyingStream.CanTimeout;
111+
112+
/// <summary>
113+
///
114+
/// Время в миллисекундах, отведенное потоку на операцию чтения.
115+
/// </summary>
116+
/// <value>Число (int)</value>
117+
[ContextProperty("ТаймаутЧтения", "ReadTimeout")]
118+
public int ReadTimeout
119+
{
120+
get => _underlyingStream.ReadTimeout;
121+
set => _underlyingStream.ReadTimeout = value;
122+
}
123+
/// <summary>
124+
///
125+
/// Время в миллисекундах, отведенное потоку на операцию записи.
126+
/// </summary>
127+
/// <value>Число (int)</value>
128+
[ContextProperty("ТаймаутЗаписи", "WriteTimeout")]
129+
public int WriteTimeout
130+
{
131+
get => _underlyingStream.WriteTimeout;
132+
set => _underlyingStream.WriteTimeout = value;
133+
}
105134

106135
/// <summary>
107136
///
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*----------------------------------------------------------
2+
This Source Code Form is subject to the terms of the
3+
Mozilla Public License, v.2.0. If a copy of the MPL
4+
was not distributed with this file, You can obtain one
5+
at http://mozilla.org/MPL/2.0/.
6+
----------------------------------------------------------*/
7+
using System.IO;
8+
using System.Threading;
9+
10+
namespace ScriptEngine.HostedScript.Library.Binary
11+
{
12+
class StreamWithTimeout : Stream
13+
{
14+
private readonly Stream _underlyingStream;
15+
private int _readTimeout;
16+
17+
public StreamWithTimeout(Stream underlyingStream)
18+
{
19+
_underlyingStream = underlyingStream;
20+
}
21+
22+
public override bool CanRead => _underlyingStream.CanRead;
23+
24+
public override bool CanSeek => _underlyingStream.CanSeek;
25+
26+
public override bool CanWrite => _underlyingStream.CanWrite;
27+
28+
public override bool CanTimeout => true;
29+
30+
public override long Length => _underlyingStream.Length;
31+
32+
public override long Position
33+
{
34+
get
35+
{
36+
return _underlyingStream.Position;
37+
}
38+
set
39+
{
40+
_underlyingStream.Position = value;
41+
}
42+
}
43+
44+
public override int ReadTimeout
45+
{
46+
get
47+
{
48+
return _readTimeout;
49+
}
50+
set
51+
{
52+
_readTimeout = value;
53+
if (_underlyingStream.CanTimeout)
54+
_underlyingStream.ReadTimeout = value;
55+
}
56+
}
57+
58+
public override int WriteTimeout
59+
{
60+
get => _underlyingStream.WriteTimeout;
61+
set => _underlyingStream.WriteTimeout = value;
62+
}
63+
64+
public override void Flush()
65+
{
66+
_underlyingStream.Flush();
67+
}
68+
69+
public override int Read(byte[] buffer, int offset, int count)
70+
{
71+
if (_readTimeout > 0 && !_underlyingStream.CanTimeout)
72+
{
73+
return ReadWithTimeout(buffer, offset, count);
74+
}
75+
else
76+
return _underlyingStream.Read(buffer, offset, count);
77+
}
78+
79+
public override long Seek(long offset, SeekOrigin origin)
80+
{
81+
return _underlyingStream.Seek(offset, origin);
82+
}
83+
84+
public override void SetLength(long value)
85+
{
86+
_underlyingStream.SetLength(value);
87+
}
88+
89+
public override void Write(byte[] buffer, int offset, int count)
90+
{
91+
_underlyingStream.Write(buffer, offset, count);
92+
}
93+
94+
private int ReadWithTimeout(byte[] buffer, int offset, int count)
95+
{
96+
int read = 0;
97+
98+
AutoResetEvent gotInput = new AutoResetEvent(false);
99+
Thread inputThread = new Thread(() =>
100+
{
101+
try
102+
{
103+
read = _underlyingStream.Read(buffer, offset, count);
104+
gotInput.Set();
105+
}
106+
catch (ThreadAbortException)
107+
{
108+
Thread.ResetAbort();
109+
}
110+
})
111+
{
112+
IsBackground = true
113+
};
114+
115+
inputThread.Start();
116+
117+
// Timeout expired?
118+
if (!gotInput.WaitOne(_readTimeout))
119+
{
120+
inputThread.Abort();
121+
}
122+
123+
return read;
124+
125+
}
126+
127+
}
128+
}

src/ScriptEngine.HostedScript/Library/ConsoleContext.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,8 @@ public void Beep()
210210
public GenericStream OpenStandardInput()
211211
{
212212
var stream = Console.OpenStandardInput();
213-
return new GenericStream(stream, true);
213+
var streamWithTimeout = new StreamWithTimeout(stream);
214+
return new GenericStream(streamWithTimeout, true);
214215
}
215216

216217
/// <summary>

tests/console.os

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
ВсеТесты = Новый Массив;
88
ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоСтандартныйПотокВводаЭтоПоток");
9+
ВсеТесты.Добавить("ТестДолжен_ПроверитьТаймаутЧтенияСтандартногоПотокВвода");
910
ВсеТесты.Добавить("ТестДолжен_ПроверитьПередачуДанныхВСкриптЧерезСтандартныйПотокВвода");
1011
ВсеТесты.Добавить("ТестДолжен_ПроверитьПеренаправлениеВывода");
1112

@@ -16,15 +17,69 @@
1617
Процедура ТестДолжен_ПроверитьЧтоСтандартныйПотокВводаЭтоПоток() Экспорт
1718

1819
ПотокВвода = Консоль.ОткрытьСтандартныйПотокВвода();
19-
юТест.ПроверитьРавенство(Тип("Поток"), ТипЗнч(ПотокВвода), "Стандартный поток ввода - Поток");
20+
юТест.ПроверитьРавенство(Тип("Поток"), ТипЗнч(ПотокВвода), "Ошибка открытия стандартного потока ввода");
2021

2122
КонецПроцедуры
2223

23-
Процедура ТестДолжен_ПроверитьПередачуДанныхВСкриптЧерезСтандартныйПотокВвода() Экспорт
24+
Процедура ТестДолжен_ПроверитьТаймаутЧтенияСтандартногоПотокВвода() Экспорт
2425

25-
СистемнаяИнформация = Новый СистемнаяИнформация;
26+
ПутьКОскрипт = ОбъединитьПути(КаталогПрограммы(), "oscript.exe");
27+
#Если Linux Тогда
28+
ПутьКОскрипт = "mono " + ПутьКОскрипт;
29+
#КонецЕсли
2630

27-
ПутьКОскрипт = """"+ОбъединитьПути(КаталогПрограммы(), "oscript.exe")+"""";
31+
КодСкрипта = "ВходящийПоток = Консоль.ОткрытьСтандартныйПотокВвода();
32+
|ВходящийПоток.ТаймаутЧтения = 100;
33+
|Чтение = Новый ЧтениеТекста();
34+
|Чтение.Открыть(ВходящийПоток);
35+
|Сообщить(СокрЛП(Чтение.Прочитать()));
36+
|";
37+
38+
ТекстСкрипта = Новый ТекстовыйДокумент();
39+
ТекстСкрипта.УстановитьТекст(КодСкрипта);
40+
41+
ВремФайл = ПолучитьИмяВременногоФайла("os");
42+
43+
ТекстСкрипта.Записать(ВремФайл);
44+
45+
ТестовыеДанные = "";
46+
47+
ИсполняемаяКоманда = СтрШаблон("""%1"" ""%2""", ПутьКОскрипт, ВремФайл);
48+
49+
#Если Windows Тогда
50+
ШаблонЗапуска = "cmd /c ""%1""";
51+
#Иначе
52+
ШаблонЗапуска = "sh -c '%1'";
53+
#КонецЕсли
54+
55+
ИсполняемаяКоманда = СтрШаблон(ШаблонЗапуска, ИсполняемаяКоманда);
56+
57+
Процесс = СоздатьПроцесс(ИсполняемаяКоманда, , Истина);
58+
Процесс.Запустить();
59+
60+
МаксимумОжидания = 1000;
61+
ИнтервалОжидания = 100;
62+
ВсегоОжидание = 0;
63+
Пока НЕ Процесс.Завершен Цикл
64+
Приостановить(ИнтервалОжидания);
65+
ВсегоОжидание = ВсегоОжидание + ИнтервалОжидания;
66+
Если ВсегоОжидание >= МаксимумОжидания Тогда
67+
Процесс.Завершить();
68+
юТест.ТестПровален("Ошибка чтения пустого стандартного потока ввода. Истекло время ожидания.");
69+
КонецЕсли;
70+
КонецЦикла;
71+
72+
ВыводКоманды = СокрЛП(Процесс.ПотокВывода.Прочитать());
73+
74+
УдалитьФайлы(ВремФайл);
75+
76+
юТест.ПроверитьРавенство(ВыводКоманды, ТестовыеДанные, "Ошибка чтения пустого стандартного потока ввода.");
77+
78+
КонецПроцедуры
79+
80+
Процедура ТестДолжен_ПроверитьПередачуДанныхВСкриптЧерезСтандартныйПотокВвода() Экспорт
81+
82+
ПутьКОскрипт = ОбъединитьПути(КаталогПрограммы(), "oscript.exe");
2883
#Если Linux Тогда
2984
ПутьКОскрипт = "mono " + ПутьКОскрипт;
3085
#КонецЕсли
@@ -43,7 +98,7 @@
4398

4499
ТестовыеДанные = "12346";
45100

46-
ИсполняемаяКоманда = СтрШаблон("echo %1 | %2 ""%3""", ТестовыеДанные, ПутьКОскрипт, ВремФайл);
101+
ИсполняемаяКоманда = СтрШаблон("echo %1 | %2 %3", ТестовыеДанные, ПутьКОскрипт, ВремФайл);
47102

48103
#Если Windows Тогда
49104
ШаблонЗапуска = "cmd /c ""%1""";
@@ -61,10 +116,10 @@
61116
КонецЦикла;
62117

63118
ВыводКоманды = СокрЛП(Процесс.ПотокВывода.Прочитать());
64-
119+
65120
УдалитьФайлы(ВремФайл);
66121

67-
юТест.ПроверитьРавенство(ВыводКоманды, ТестовыеДанные, "Вывод команды - тестовые данные");
122+
юТест.ПроверитьРавенство(ВыводКоманды, ТестовыеДанные, "Ошибка чтения стандартного потока ввода.");
68123

69124
КонецПроцедуры
70125

0 commit comments

Comments
 (0)