Skip to content
Merged
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
4 changes: 4 additions & 0 deletions src/OneScript.Core/Commons/LruCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ This Source Code Form is subject to the terms of the

namespace OneScript.Commons
{
/// <summary>
/// Не потокобезопасен: даже чтение меняет порядок элементов.
/// Общий для нескольких потоков кэш нужно защищать блокировкой снаружи.
/// </summary>
public class LruCache<TKey, TValue>
{
private readonly int _capacity;
Expand Down
10 changes: 8 additions & 2 deletions src/OneScript.Native/Compiler/ReflectedMembersCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ namespace OneScript.Native.Compiler
{
public abstract class ReflectedMembersCache<T> where T : MemberInfo
{
// Кэши в ExpressionHelpers статические и общие для всех потоков
private readonly object _lock = new object();

private readonly LruCache<string, T> _cache;

public ReflectedMembersCache() : this(128)
Expand All @@ -32,8 +35,11 @@ public virtual T GetOrAdd(Type type, string name)
public T GetOrAdd(Type type, string name, BindingFlags flags)
{
var key = $"{type.Name}.{name}";
return _cache.GetOrAdd(key, x => SearchImpl(type, name, flags)
?? throw new InvalidOperationException($"No member found {key}"));
lock (_lock)
{
return _cache.GetOrAdd(key, x => SearchImpl(type, name, flags)
?? throw new InvalidOperationException($"No member found {key}"));
}
}

protected abstract T SearchImpl(Type type, string name, BindingFlags flags);
Expand Down
6 changes: 4 additions & 2 deletions src/ScriptEngine/Machine/Contexts/SimpleEnumsMarshaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This Source Code Form is subject to the terms of the
----------------------------------------------------------*/

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
Expand All @@ -21,8 +22,9 @@ public static class SimpleEnumsMarshaller
{
// TODO Наверное можно и прямо отсюда регистрировать SimpleEnum-ы а из ContextDiscoverer этот класс вызывать

private static LruCache<Type, Func<object, EnumerationValue>> _gettersCache
= new LruCache<Type, Func<object, EnumerationValue>>(32);
// Общий на все потоки. Типов перечислений конечное число, вытеснять нечего
private static readonly ConcurrentDictionary<Type, Func<object, EnumerationValue>> _gettersCache
= new ConcurrentDictionary<Type, Func<object, EnumerationValue>>();

/// <summary>
/// Получить IValue для значения clr-перечисления.
Expand Down
4 changes: 4 additions & 0 deletions src/ScriptEngine/Machine/LruCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ This Source Code Form is subject to the terms of the

namespace ScriptEngine.Machine
{

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А зачем у нас два LruCache? Кажется один из них это атавизм и надо испоьзовать везде тот, который из Commons

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, копия в ScriptEngine.Machine появилась позже, для кэша Выполнить/Вычислить, и использовалась только в MachineInstance. Убрал её в #1756.

/// <summary>
/// Не потокобезопасен: даже чтение меняет порядок элементов.
/// Общий для нескольких потоков кэш нужно защищать блокировкой снаружи.
/// </summary>
public class LruCache<TKey, TValue>
{
private readonly int _capacity;
Expand Down
37 changes: 37 additions & 0 deletions src/Tests/OneScript.Dynamic.Tests/ReflectedMembersCacheTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*----------------------------------------------------------
This Source Code Form is subject to the terms of the
Mozilla Public License, v.2.0. If a copy of the MPL
was not distributed with this file, You can obtain one
at http://mozilla.org/MPL/2.0/.
----------------------------------------------------------*/

using System;
using System.Threading.Tasks;
using FluentAssertions;
using OneScript.Native.Compiler;
using Xunit;

namespace OneScript.Dynamic.Tests;

public class ReflectedMembersCacheTest
{
[Fact]
public void CacheSurvivesParallelAccess()
{
// имен больше емкости: и попадания, и вытеснение
var cache = new ReflectedMethodsCache(4);
var names = new[] { "Sin", "Cos", "Tan", "Sqrt", "Exp", "Log10" };

Action act = () => Parallel.For(0, 8, _ =>
{
for (var i = 0; i < 100000; i++)
{
var name = names[i % names.Length];
if (cache.GetOrAdd(typeof(Math), name).Name != name)
throw new InvalidOperationException($"Wrong method for {name}");
}
});

act.Should().NotThrow();
}
}
25 changes: 25 additions & 0 deletions tests/tasks.os
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВИнформацииОбОшибкеЕстьСтекВызовов");
ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоОбработчикиСобытийВызываютсяВФоновомЗадании");
ВсеТесты.Добавить("ТестДолжен_ПроверитьПараллельноеЧтениеЗначенияСистемногоПеречисления");
ВсеТесты.Добавить("ТестДолжен_ПроверитьПараллельноеЧтениеСвойстваПеречисления");

Возврат ВсеТесты;

Expand Down Expand Up @@ -669,6 +670,30 @@

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

Процедура ПрочитатьСостояниеЗадания(Задание) Экспорт
Для Счетчик = 1 По 50000 Цикл
Состояние = Задание.Состояние;
КонецЦикла;
КонецПроцедуры

Процедура ТестДолжен_ПроверитьПараллельноеЧтениеСвойстваПеречисления() Экспорт

Задание = ФоновыеЗадания.Выполнить(ЭтотОбъект, "Пустышка");
Задание.ОжидатьЗавершения(60000);

Параметры = Новый Массив;
Параметры.Добавить(Задание);

Задания = Новый Массив;
Для НомерЗадания = 1 По 8 Цикл
Задания.Добавить(ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПрочитатьСостояниеЗадания", Параметры));
КонецЦикла;

ФоновыеЗадания.ОжидатьВсе(Задания);
ПроверитьОтсутствиеОшибокВЗаданиях(Задания);

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

Процедура Пустышка() Экспорт
КонецПроцедуры

Expand Down