Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/ScriptEngine.HostedScript/FileSystemDependencyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,17 @@ private LoadResult LoadByPath(string libraryPath, IBslProcess process)
}

private PackageInfo LoadLibraryInternal(string libraryPath, IBslProcess process)
{
// Библиотеки грузятся по одной: иначе поток, которому нужна библиотека, загружаемая
// сейчас в другом потоке, принимает ее за циклическую зависимость или грузит повторно.
// Вложенные #Использовать грузятся в том же потоке, блокировка их пропускает
lock (_libs)
{
return DoLoadLibrary(libraryPath, process);
}
}

private PackageInfo DoLoadLibrary(string libraryPath, IBslProcess process)
{
var id = GetLibraryId(libraryPath);
var existedLib = _libs.FirstOrDefault(x => x.id == id);
Expand All @@ -261,7 +272,6 @@ private PackageInfo LoadLibraryInternal(string libraryPath, IBslProcess process)
}

var newLib = new Library() { id = id, state = ProcessingState.Discovered };
int newLibIndex = _libs.Count;

var customLoaderFile = Path.Combine(libraryPath, PREDEFINED_LOADER_FILE);
if (File.Exists(customLoaderFile))
Expand All @@ -277,7 +287,7 @@ private PackageInfo LoadLibraryInternal(string libraryPath, IBslProcess process)
}
catch (Exception)
{
_libs.RemoveAt(newLibIndex);
_libs.Remove(newLib);
throw;
}

Expand Down
71 changes: 44 additions & 27 deletions src/ScriptEngine/RuntimeEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ private void CreateGlobalScopeIfNeeded()

lock (_injectedProperties)
{
_scopeOfGlobalProperties ??= _symbols.PushContext(_injectedProperties);
_contexts.Add(_injectedProperties);
if (_scopeOfGlobalProperties == null)
{
_scopeOfGlobalProperties = _symbols.PushContext(_injectedProperties);
_contexts.Add(_injectedProperties);
}
}
}

Expand Down Expand Up @@ -85,48 +88,62 @@ private void InjectPropertyInternal(
throw new ArgumentException("Invalid identifier", nameof(alias));
}
CreateGlobalScopeIfNeeded();
var num = _injectedProperties.Insert(value, identifier, true, !readOnly);

var bslPropertyInfo = _injectedProperties.GetPropertyInfo(num);
IVariableSymbol registeredSymbol;
if (ownerPackage == null)
// Номер свойства в значениях и номер символа в области видимости должны совпасть:
// вставки из разных потоков (библиотека, внешняя компонента) идут по одной
lock (_injectedProperties)
{
registeredSymbol = new WrappedPropertySymbol(bslPropertyInfo)
var num = _injectedProperties.Insert(value, identifier, true, !readOnly);

var bslPropertyInfo = _injectedProperties.GetPropertyInfo(num);
IVariableSymbol registeredSymbol;
if (ownerPackage == null)
{
Name = identifier,
Alias = alias
};
}
else
{
registeredSymbol = new WrappedLibraryPropertySymbol(bslPropertyInfo, ownerPackage)
registeredSymbol = new WrappedPropertySymbol(bslPropertyInfo)
{
Name = identifier,
Alias = alias
};
}
else
{
Name = identifier,
Alias = alias
};
registeredSymbol = new WrappedLibraryPropertySymbol(bslPropertyInfo, ownerPackage)
{
Name = identifier,
Alias = alias
};
}

_scopeOfGlobalProperties.DefineVariable(registeredSymbol);
}

_scopeOfGlobalProperties.DefineVariable(registeredSymbol);
}

public void InjectGlobalProperty(IValue value, BslPropertyInfo definition)
{
CreateGlobalScopeIfNeeded();
_injectedProperties.Insert(value, definition);

var symbol = new WrappedPropertySymbol(definition)
lock (_injectedProperties)
{
Name = definition.Name,
Alias = definition.Alias
};
_injectedProperties.Insert(value, definition);

var symbol = new WrappedPropertySymbol(definition)
{
Name = definition.Name,
Alias = definition.Alias
};

_scopeOfGlobalProperties.DefineVariable(symbol);
_scopeOfGlobalProperties.DefineVariable(symbol);
}
}

private void RegisterObject(IAttachableContext context)
{
_symbols.PushContext(context);
_contexts.Add(context);
// Номер области видимости и номер контекста должны совпасть
lock (_injectedProperties)
{
_symbols.PushContext(context);
_contexts.Add(context);
}
}

public void SetGlobalProperty(string propertyName, IValue value)
Expand Down
65 changes: 63 additions & 2 deletions tests/librarytest.os
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
#Использовать "customlib"

Перем юТест;
Перем ГотовыеЗадания;

Функция ПолучитьСписокТестов(Знач Тесты) Экспорт
юТест = Тесты;

Список = Новый Массив;
Список.Добавить("ТестДолжен_ИспользоватьВстроенныйЗагрузчик");
Список.Добавить("ТестДолжен_ИспользоватьПользовательскийЗагрузчик");

Список.Добавить("ТестДолжен_ЗагрузитьБиблиотекуИзНесколькихЗаданийОдновременно");

Возврат Список;

КонецФункции
Expand All @@ -33,5 +35,64 @@

НекийОбщийМодуль.УстановитьПоСсылке(Супер.А);
юТест.ПроверитьРавенство(100, Супер.А);


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

Процедура ТестДолжен_ЗагрузитьБиблиотекуИзНесколькихЗаданийОдновременно() Экспорт

Путь = ОбъединитьПути(ТекущийСценарий().Каталог, "slowlib");
ГотовыеЗадания = Новый Массив(4);
Для Индекс = 0 По ГотовыеЗадания.ВГраница() Цикл
ГотовыеЗадания[Индекс] = Ложь;
КонецЦикла;

Задания = Новый Массив;
Для Индекс = 0 По ГотовыеЗадания.ВГраница() Цикл
Параметры = Новый Массив;
Параметры.Добавить(Путь);
Параметры.Добавить(Индекс);
Задания.Добавить(ФоновыеЗадания.Выполнить(ЭтотОбъект, "ИспользоватьМедленнуюБиблиотеку", Параметры));
КонецЦикла;
ФоновыеЗадания.ОжидатьВсе(Задания);

Для Каждого Задание Из Задания Цикл
Если Задание.ИнформацияОбОшибке <> Неопределено Тогда
ВызватьИсключение Задание.ИнформацияОбОшибке.ПодробноеОписаниеОшибки();
КонецЕсли;
юТест.ПроверитьРавенство("Привет", Задание.Результат);
КонецЦикла;

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

Функция ИспользоватьМедленнуюБиблиотеку(Путь, Индекс) Экспорт

// Все задания начинают подключать библиотеку одновременно
ГотовыеЗадания[Индекс] = Истина;
Для Номер = 1 По 500 Цикл
Если ВсеЗаданияГотовы() Тогда
Прервать;
КонецЕсли;
Приостановить(10);
КонецЦикла;

Сценарий = ЗагрузитьСценарийИзСтроки(
"#Использовать """ + Путь + """
|Функция Привет() Экспорт
| Возврат МедленныйМодуль.Привет();
|КонецФункции");
Возврат Сценарий.Привет();

КонецФункции

Функция ВсеЗаданияГотовы()

// По индексу, а не Для Каждого: другие задания в это время пишут в массив
Для Индекс = 0 По ГотовыеЗадания.ВГраница() Цикл
Если Не ГотовыеЗадания[Индекс] Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;

Возврат Истина;

КонецФункции
3 changes: 3 additions & 0 deletions tests/slowlib/module.os
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Функция Привет() Экспорт
Возврат "Привет";
КонецФункции
8 changes: 8 additions & 0 deletions tests/slowlib/package-loader.os
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Процедура ПриЗагрузкеБиблиотеки(Знач Путь, СтандартнаяОбработка, Отказ)

// Загрузка долгая: пока она идет, библиотеку запрашивают из других потоков
Приостановить(300);
СтандартнаяОбработка = Ложь;
ДобавитьМодуль(ОбъединитьПути(Путь, "module.os"), "МедленныйМодуль");

КонецПроцедуры
Loading