Re[8]: Interlocked
От: RushDevion Россия  
Дата: 29.12.18 12:15
Оценка:
S>>>А тут rc не будет случаем? Может continuation прицепить к родителю (task)?
RD>>Так оно и цепляется к task
RD>>Я возможностей для rc не вижу.

S>Пользователь может воспользовать знач. task, до того как оно сброситься в null.

Так это нормально. Последовательность примерно такая:
1. Пришел поток1. Создал таску инициализации. Ждет ее результата.
2. Пришел поток2. Ему отдали готовую таску, созданную потоком1.
3. Таска зафейлилась. Каждый из потоков получил свой Exception. На первом потоке запустился continuation (ExecuteSynchronously), который должен сбросить task в null
4. В этот момент пришел поток3.
Для него возможны два сценария:
4.1. Поток1 еще не успел сбросить task в null. В этом случае поток3 получит тот же самый task, что потоки1/2 и сразу зафейлится (ну не повезло, че).
4.2. Поток1 успел сбросить task. В этом случае поток3 запустит таску инициализации по-новой.

S>Уточню, что прицепив его AttachToParent, task не будет считаться законченным, пока не закончаться его потомки. Как-то так...(могу ошибаться)

Да, пожалуй, есть смысл добавить и AttachedToParent.
Если потоки получают Value в цикле это исключит вероятность 4.1 для потока2 при повторной попытке получения Value.
Re[3]: Interlocked
От: Sharov Россия  
Дата: 29.12.18 12:33
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>interlocked — это указание компилятору о защищенной команде, что выливается на уровне ассемблера в генерацию префикса lock для текущей команды, например при инкрементации значения с возвратом lock xadd xxx,xxx Чрезвычайно быстрая хрень.


Что за дичь написана? Это атомарные инструкции процессора (буквально одна инструкция процессора), а не генерация префиска и т.д.
Кодом людям нужно помогать!
Re[4]: Interlocked
От: GlebZ Россия  
Дата: 29.12.18 13:02
Оценка:
Здравствуйте, Sharov, Вы писали:

GZ>>interlocked — это указание компилятору о защищенной команде, что выливается на уровне ассемблера в генерацию префикса lock для текущей команды, например при инкрементации значения с возвратом lock xadd xxx,xxx Чрезвычайно быстрая хрень.


S>Что за дичь написана? Это атомарные инструкции процессора (буквально одна инструкция процессора), а не генерация префиска и т.д.

Не понял? Это команда компилятору на генерацию префикса lock для текущей команды, и эта команда будет выполнена атомарно при взведенном флаге процессора lock. Что тут непонятного?
Re[5]: Interlocked
От: Sharov Россия  
Дата: 29.12.18 13:13
Оценка:
Здравствуйте, GlebZ, Вы писали:

S>>Что за дичь написана? Это атомарные инструкции процессора (буквально одна инструкция процессора), а не генерация префиска и т.д.

GZ>Не понял? Это команда компилятору на генерацию префикса lock для текущей команды, и эта команда будет выполнена атомарно при взведенном флаге процессора lock. Что тут непонятного?

За чем что-то генерировать, когда есть конкретне инсрукции процессора? Это для msil, где про енто можно почитать? Иначе это будет похоже на транзакционную память(или вроде ентого). Т.е. так
можно и FFT атомарно делать.

Еще раз, можно ссылку на генерацию lock и т.д. ибо я себе это нескольок иначе представлял. Вомзожно я неправ.
Кодом людям нужно помогать!
Re[6]: Interlocked
От: GlebZ Россия  
Дата: 29.12.18 13:22
Оценка: 6 (1)
Здравствуйте, Sharov, Вы писали:

S>За чем что-то генерировать, когда есть конкретне инсрукции процессора? Это для msil, где про енто можно почитать? Иначе это будет похоже на транзакционную память(или вроде ентого). Т.е. так

S>можно и FFT атомарно делать.

S>Еще раз, можно ссылку на генерацию lock и т.д. ибо я себе это нескольок иначе представлял. Вомзожно я неправ.

Первая ссылка из гугла
http://www.club155.ru/x86cmd/LOCK
Re[7]: Interlocked
От: Sharov Россия  
Дата: 29.12.18 13:29
Оценка: +1
Здравствуйте, GlebZ, Вы писали:

GZ>Первая ссылка из гугла

GZ>http://www.club155.ru/x86cmd/LOCK

Был неправ, т.е. не знал детали. Век живи -- век учись.
Кодом людям нужно помогать!
Re[5]: Interlocked
От: RAza  
Дата: 29.12.18 22:18
Оценка:
Здравствуйте, RushDevion, Вы писали:

RD>Как идея: можно возвращать Task. Тогда проброс ошибок будет автоматическим.

Покрутил идею с Task. Итоговый дизайн публичного API мне не нравится. Хочется сохранить условие что данный тип предоставляет потребителям либо значение, либо исключение.

В итоге опираясь на "академический" пример переписал реализацию добавив обработку ошибок. Существующие тесты она проходит, хотя в ближайшее время количество тест кейсов конечно стоит увеличить. Если у вас будет время и возможность посмотрите на код — сильно опасаюсь что я где то накосячил.

public sealed class MyLazy<T>
{
    private volatile Exception error = null;

    private volatile Holder holder = null;

    private volatile int waiting = 0;

    private readonly ManualResetEvent onError = new ManualResetEvent(false);

    private readonly Func<T> getAction = null;

    private readonly bool canBeReseted;

    private int isExist = 0;

    public bool HasValue => holder != null;

    public T Value
    {
        get
        {
            // Предыдущая попытка получить значение вызвала исключение. Все еще имеются ожидающие инициализации потоки. Ждем, пока для каждого будет брошено исключение.
            while (error != null)
            {
                Thread.Sleep(TimeSpan.FromMilliseconds(5));
            }

            var current = holder;

            if (current != null)
                return current.Value;

            if (Interlocked.CompareExchange(ref isExist, 1, 0) == 0)
            {
                try
                {
                    // Захватили право на инициализацию, инициируем
                    holder =
                        new Holder
                            {
                                Value = getAction()
                            };
                }
                catch (Exception ex)
                {
                    Interlocked.Exchange(ref isExist, 0);

                    if (waiting > 0)
                    {
                        onError.Set();

                        error = ex;
                    }

                    throw;
                }

                return holder.Value;
            }

            Interlocked.Increment(ref waiting);

            // Право на инициализацию захватил кто-то другой, ждем, пока инициализация не завершится
            while (true)
            {
                try
                {
                    if (error != null)
                        throw error;

                    var result = holder;

                    if (result != null)
                        return result.Value;

                    onError.WaitOne(TimeSpan.FromMilliseconds(5));
                }
                finally
                {
                    Interlocked.Decrement(ref waiting);

                    if (waiting == 0)
                    {
                        onError.Reset();

                        error = null;
                    }

                }
            }
        }
        set
        {
            Interlocked.Exchange(ref isExist, 1);

            holder =
                new Holder
                    {
                        Value = value
                    };
        }
    }

    public MyLazy(Func<T> getAction, bool canBeReseted)
            : this(getAction)
        => this.canBeReseted = canBeReseted;

    public MyLazy(Func<T> getAction)
        => this.getAction = getAction;

    public void Reset()
    {
        if (canBeReseted == false)
            return;

        Interlocked.Exchange(ref isExist, 0);

        holder = null;
    }

    private class Holder
    {
        public T Value;
    }
}


Из появившихся вопросов:

В чем сакральный смысл копирования ссылки на объект с имеющимся значением в локальную переменную? Для текущего потока это значение попадет в его "локальный кеш"?



Речь про вот этот участок кода:
var current = holder;
if (current != null)
    return current.Value;
Отредактировано 29.12.2018 22:25 RAza . Предыдущая версия .
Re[6]: Interlocked
От: vorona  
Дата: 29.12.18 23:18
Оценка:
Здравствуйте, RAza, Вы писали:

RA>Речь про вот этот участок кода:

RA>
RA>var current = holder;
RA>if (current != null)
RA>    return current.Value;
RA>


It’s important to use the handler local variable, as if instead you access the field twice, it’s possible that the last subscriber will unsubscribe between the check and the invocation:
Re[6]: Interlocked
От: Sharov Россия  
Дата: 04.01.19 17:36
Оценка: +1
Здравствуйте, RAza, Вы писали:


RA> catch (Exception ex)

RA> {
RA> Interlocked.Exchange(ref isExist, 0);

if (waiting > 0)
{
onError.Set();
error = ex;
]
}

RA> throw;

RA> }


Может местами поменять публикацию ошибку и нотификацию?
Кодом людям нужно помогать!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.