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+
8+ using System ;
9+ using System . Collections . Generic ;
10+ using System . Linq ;
11+ using System . Threading ;
12+ using System . Threading . Tasks ;
13+ using OneScript . Commons ;
14+ using OneScript . StandardLibrary . Collections ;
15+ using ScriptEngine ;
16+ using ScriptEngine . Machine ;
17+ using ScriptEngine . Machine . Contexts ;
18+
19+ namespace OneScript . StandardLibrary . Tasks
20+ {
21+ [ ContextClass ( "МенеджерФоновыхЗаданий" , "BackgroundTasksManager" ) ]
22+ public class BackgroundTasksManager : AutoContext < BackgroundTasksManager > , IDisposable
23+ {
24+ private readonly ScriptingEngine _engine ;
25+ private List < BackgroundTask > _tasks = new List < BackgroundTask > ( ) ;
26+
27+ public BackgroundTasksManager ( ScriptingEngine engine )
28+ {
29+ _engine = engine ;
30+ }
31+
32+ /// <summary>
33+ /// Создать и стартовать задание
34+ /// </summary>
35+ /// <param name="target">Объект, метод которого нужно выполнить</param>
36+ /// <param name="methodName">Имя экспортного метода в объекте</param>
37+ /// <param name="parameters">Массив параметров метода</param>
38+ /// <returns>ФоновоеЗадание</returns>
39+ [ ContextMethod ( "Выполнить" , "Execute" ) ]
40+ public BackgroundTask Execute ( IRuntimeContextInstance target , string methodName , ArrayImpl parameters = null )
41+ {
42+ var task = new BackgroundTask ( target , methodName , parameters ) ;
43+ _tasks . Add ( task ) ;
44+
45+ var worker = new Task ( ( ) =>
46+ {
47+ if ( ! MachineInstance . Current . IsRunning )
48+ _engine . Environment . LoadMemory ( MachineInstance . Current ) ;
49+
50+ task . ExecuteOnCurrentThread ( ) ;
51+
52+ } , TaskCreationOptions . LongRunning ) ;
53+
54+ task . WorkerTask = worker ;
55+ worker . Start ( ) ;
56+
57+ return task ;
58+ }
59+
60+ [ ContextMethod ( "Очистить" , "Clear" ) ]
61+ public void Clear ( )
62+ {
63+ _tasks . Clear ( ) ;
64+ }
65+
66+ /// <summary>
67+ /// Ожидает завершения всех переданных заданий
68+ /// </summary>
69+ /// <param name="tasks">Массив заданий</param>
70+ /// <param name="timeout">Таймаут ожидания. 0 = ожидать бесконечно</param>
71+ /// <returns>Истина - дождались все задания, Ложь - истек таймаут</returns>
72+ [ ContextMethod ( "ОжидатьВсе" , "WaitAll" ) ]
73+ public bool WaitAll ( ArrayImpl tasks , int timeout = 0 )
74+ {
75+ var workers = GetWorkerTasks ( tasks ) ;
76+ timeout = ConvertTimeout ( timeout ) ;
77+
78+ // Фоновые задания перехватывают исключения внутри себя
79+ // и выставляют свойство ИнформацияОбОшибке
80+ // если WaitAll выбросит исключение, значит действительно что-то пошло не так на уровне самого Task
81+ return Task . WaitAll ( workers , timeout ) ;
82+ }
83+
84+ /// <summary>
85+ /// Ожидать хотя бы одно из переданных заданий.
86+ /// </summary>
87+ /// <param name="tasks">Массив заданий</param>
88+ /// <param name="timeout">Таймаут ожидания. 0 = ожидать бесконечно</param>
89+ /// <returns>Число. Индекс в массиве заданий, указывающий на элемент-задание, которое завершилось. -1 = сработал таймаут</returns>
90+ [ ContextMethod ( "ОжидатьЛюбое" , "WaitAny" ) ]
91+ public int WaitAny ( ArrayImpl tasks , int timeout = 0 )
92+ {
93+ var workers = GetWorkerTasks ( tasks ) ;
94+ timeout = ConvertTimeout ( timeout ) ;
95+
96+ // Фоновые задания перехватывают исключения внутри себя
97+ // и выставляют свойство ИнформацияОбОшибке
98+ // если WaitAny выбросит исключение, значит действительно что-то пошло не так на уровне самого Task
99+ return Task . WaitAny ( workers , timeout ) ;
100+ }
101+
102+ /// <summary>
103+ /// Блокирует поток до завершения всех заданий.
104+ /// Выбрасывает исключение, если какие-то задания завершились аварийно.
105+ /// Выброшенное исключение в свойстве Параметры содержит массив аварийных заданий.
106+ /// </summary>
107+ [ ContextMethod ( "ОжидатьЗавершенияЗадач" , "WaitCompletionOfTasks" ) ]
108+ public void WaitCompletionOfTasks ( )
109+ {
110+ lock ( _tasks )
111+ {
112+ var workers = GetWorkerTasks ( ) ;
113+ Task . WaitAll ( workers ) ;
114+
115+ var failedTasks = _tasks . Where ( x => x . State == TaskStateEnum . CompletedWithErrors )
116+ . ToList ( ) ;
117+
118+ if ( failedTasks . Any ( ) )
119+ {
120+ throw new ParametrizedRuntimeException (
121+ Locale . NStr ( "ru = 'Задания завершились с ошибками';en = 'Tasks are completed with errors'" ) ,
122+ new ArrayImpl ( failedTasks ) ) ;
123+ }
124+
125+ _tasks . Clear ( ) ;
126+ }
127+ }
128+
129+ [ ContextMethod ( "ПолучитьФоновыеЗадания" , "GetBackgroundJobs" ) ]
130+ public ArrayImpl GetBackgroundJobs ( StructureImpl filter = default )
131+ {
132+ if ( filter == default )
133+ return new ArrayImpl ( _tasks . ToArray ( ) ) ;
134+
135+ var arr = new ArrayImpl ( ) ;
136+ foreach ( var task in _tasks )
137+ {
138+ var result = true ;
139+ foreach ( var filterItem in filter )
140+ {
141+ switch ( filterItem . Key . AsString ( ) . ToLower ( ) )
142+ {
143+ case "состояние" :
144+ case "state" :
145+ var enumval = filterItem . Value as ClrEnumValueWrapper < TaskStateEnum > ;
146+ if ( enumval == default )
147+ continue ;
148+
149+ result = result && task . State == enumval . UnderlyingValue ;
150+ break ;
151+
152+ case "имяметода" :
153+ case "methodname" :
154+ result = result && task . MethodName . ToLower ( ) == filterItem . Value . AsString ( ) ;
155+ break ;
156+
157+ case "объект" :
158+ case "object" :
159+ result = result && task . Target . Equals ( filterItem . Value ) ;
160+ break ;
161+
162+ case "уникальныйидентификатор" :
163+ case "uuid" :
164+ result = result && task . Identifier . Equals ( filterItem . Value ) ;
165+ break ;
166+ }
167+ }
168+
169+ if ( result )
170+ arr . Add ( task ) ;
171+ }
172+
173+ return arr ;
174+ }
175+
176+ internal static int ConvertTimeout ( int timeout )
177+ {
178+ if ( timeout < 0 )
179+ throw RuntimeException . InvalidArgumentValue ( ) ;
180+
181+ return timeout == 0 ? Timeout . Infinite : timeout ;
182+ }
183+
184+ private static Task [ ] GetWorkerTasks ( ArrayImpl tasks )
185+ {
186+ return tasks
187+ . Cast < BackgroundTask > ( )
188+ . Select ( x => x . WorkerTask )
189+ . ToArray ( ) ;
190+ }
191+
192+ private static Task [ ] GetWorkerTasks ( IEnumerable < BackgroundTask > tasks )
193+ {
194+ return tasks . Select ( x => x . WorkerTask ) . ToArray ( ) ;
195+ }
196+
197+ private Task [ ] GetWorkerTasks ( )
198+ {
199+ return GetWorkerTasks ( _tasks ) ;
200+ }
201+
202+ public void Dispose ( )
203+ {
204+ Task . WaitAll ( GetWorkerTasks ( ) ) ;
205+ _tasks . Clear ( ) ;
206+ }
207+ }
208+ }
0 commit comments