protect variables
От: c-smile Канада http://terrainformatica.com
Дата: 05.03.16 03:15
Оценка:
есть имплментация script virtual machine.

Эта VM использует moving garbage collector, т.е. объекты гуляют по памяти когда происходит GC.

Вот пример кода

typedef uint64 value; // if object then contains pointer to memory

value csf_native_function(VM* c, value obj, value p1, value p2) {

  if( !cs_is_string(p1) )
    p1 = cs_allocate_string(c, ' ', 256); // may cause GC()
  // obj, p1, p2 could be wrong at this point due to GC in the line above
  return csf_other_func(c, obj, p1, p2);
}


Т.к. cs_allocate_string() может вызывать GC() то значения obj, p1, p2 могут быть не валидными —
указывать на старые локации объектов.

Решаю эту проблему с помощью специадьного RAII объекта PROTECT:

value csf_native_function(VM* c, value obj, value p1, value p2) {
  PROTECT(c, obj, p1, p2);
  if( !cs_is_string(p1) )
    p1 = cs_allocate_string(c, ' ', 256); // may cause GC()
  // obj, p1, p2 could be wrong at this point due to GC in the line above
  return csf_other_func(c, obj, p1, p2);
}


Этот protect фактически делает следующее:

cs_push_var_address(c, &obj);
cs_push_var_address(c, &p1);
cs_push_var_address(c, &p2);

...

// at destructor
cs_pop_var_addresses(c,3)


Проблема в том что это PROTECT() нужно задавать явно. Иногда ошибаюсь и ловить проблему приходится долго и нудно.

Хочется построить что-то типа консервативного GC чтобы шерстил С/C++ стек на предмет чего-то похожего на pointers.
Но что-то мне говорит что ничего хорошего их этого не получится. Всякие там оптимизации, размещение переменных в регистрах и прочая.

Что-то еще можно придумть на эту тему? Приветствуются идеи любой степени сумашествия.
Отредактировано 05.03.2016 3:29 c-smile . Предыдущая версия . Еще …
Отредактировано 05.03.2016 3:16 c-smile . Предыдущая версия .
Re: protect variables
От: Шахтер Интернет  
Дата: 05.03.16 07:15
Оценка:
Здравствуйте, c-smile, Вы писали:

Я не вполне понял, как твоя механика работает, но можно было бы ввести два типа -- один для свободных объектов, а второй для приколотых.
И сделать доступ к приколотым объектам через RAII. Тогда не будешь ошибаться.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[2]: protect variables
От: c-smile Канада http://terrainformatica.com
Дата: 05.03.16 20:21
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Здравствуйте, c-smile, Вы писали:


Ш>Я не вполне понял, как твоя механика работает, но можно было бы ввести два типа -- один для свободных объектов, а второй для приколотых.

Ш>И сделать доступ к приколотым объектам через RAII. Тогда не будешь ошибаться.

А как тогда сигнатура такой функции должна выглядеть?

value csf_native_function(VM* c, value obj, value p1, value p2) {
  if( !cs_is_string(p1) )
    p1 = cs_allocate_string(c, ' ', 256); // may cause GC()
  // obj, p1, p2 could be wrong at this point due to GC in the line above
  return csf_other_func(c, obj, p1, p2);
}


проблема в том что VM может быть несколько. Т.е. чтобы сконструировать raii holder нужно VM* и само value:

struct managed_value {
  VM*    c;
  value  v;

  managed_value(VM* ct, value va): c(ct), v(va) { c->push_address(&v); }
  ~managed_value(): c(ct), v(va) { c->pop_address(); }
}


Передавать managed_values в параметрах как бы накладно, да и неудобно.

В качестве дикой идеи пробовал тупо сканировать C стек на предмет чего-то похожего на value-address, но в отличии от Boehm collector
у меня compacting GC. Т.е. нужно не только сканировать стек но и модифицировать его грязными руками.
Предварительные тесты показали жуткие результаты. Особенно с включенными оптимизациями и все такое.

Эх, что-то мне говорит что так и придется это всё руками продолжать делать...
Re[3]: protect variables
От: Шахтер Интернет  
Дата: 05.03.16 21:10
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>проблема в том что VM может быть несколько. Т.е. чтобы сконструировать raii holder нужно VM* и само value:


CS>
CS>struct managed_value {
CS>  VM*    c;
CS>  value  v;

CS>  managed_value(VM* ct, value va): c(ct), v(va) { c->push_address(&v); }
CS>  ~managed_value(): c(ct), v(va) { c->pop_address(); }
CS>} 
CS>


CS>Передавать managed_values в параметрах как бы накладно, да и неудобно.


Можно сделать три типа: float_value, pinned_value и protect.


struct float_value
 {
  value v;
 };

struct pinned_value
 {
  value v;
 };

class protect : NoCopy
 {
   VM *vm;
   pinned_value val;

  public:

   protect(VM *vm,float_value val);

   ~protect();

   operator pinned_value() const { return val; } 
 };


Ну или даже в качестве pinned_value выбрать само value.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[4]: protect variables
От: c-smile Канада http://terrainformatica.com
Дата: 06.03.16 02:13
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Можно сделать три типа: float_value, pinned_value и protect.


Ну именно pinned у меня и так есть:

https://github.com/c-smile/sciter-sdk/blob/master/include/tiscript.hpp#L217

Но с ним как раз проблема та что я описал изначально — приходится помнить что если там может оказаться GCable thing то надо его pin.
В этом то и проблема что нужно очень аккуратно с этим делом.
Re: protect variables
От: uzhas Ниоткуда  
Дата: 06.03.16 12:00
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Что-то еще можно придумть на эту тему? Приветствуются идеи любой степени сумашествия.

ничего сумасшедшего посоветовать не могу — используй подход managed С++. ключевые слова gcroot, GC.KeepAlive, GCHandle, __pin
Re: protect variables
От: Erop Россия  
Дата: 07.03.16 20:40
Оценка:
Здравствуйте, c-smile, Вы писали:


CS>
CS>value csf_native_function(VM* c, value obj, value p1, value p2) {
CS>  PROTECT(c, obj, p1, p2);
CS>


1) А тебе не кажется, что залочить obj, p1 и p2 должны при вызове функции? А то мало ли откуда они пришли?
2) Я бы сделал такой прокси-объект, который в конструкторе лочит, в деструкторе отпускает, и умеет каститься к ссылке на указуемое, и в operator-> к указателю, на него же.

Тогда при вызове можно будет отдавать прокси, который скастится куда надо.
Одна проблема, как передавать VM*. Ну можно через статический на нить параметр, например.
Но главная проблема в другом, так жить-то нельзя? Так придётся всё лочить, что на стеке лежит, что противоречит самой идее GC.
Как в той VM вообще жить предлагается? Может можно как-то в ней регить указатели на GC-объекты, или есть какие-то стабильные хэндлы, например, или ещё какая-то стратегия?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: protect variables
От: c-smile Канада http://terrainformatica.com
Дата: 08.03.16 01:21
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, c-smile, Вы писали:



CS>>
CS>>value csf_native_function(VM* c, value obj, value p1, value p2) {
CS>>  PROTECT(c, obj, p1, p2);
CS>>


E>1) А тебе не кажется, что залочить obj, p1 и p2 должны при вызове функции? А то мало ли откуда они пришли?


В бощем и целом — нет, caller ничего не знает про то что функция делает. Если она только читает то ничего лочить не надо.
Проблема в том что GC происходит редко. Идеально никакой допополнительный код не должен исполнятся если не происходит GC. Т.е. zero overhead.

E>2) Я бы сделал такой прокси-объект, который в конструкторе лочит, в деструкторе отпускает, и умеет каститься к ссылке на указуемое, и в operator-> к указателю, на него же.


E>Но главная проблема в другом, так жить-то нельзя? Так придётся всё лочить, что на стеке лежит, что противоречит самой идее GC.


В момент вызова таких функций известен диапазон C/C++ стека где такие value находятся.
Т.е. идеально было бы что-то типа этого

void vm::collect_garbage() {
  ...
  value dummy;
  void* p = &dummy; // c stack top 
  void* pend = c_stack_bottom;
  for(; p < pend; ++p )
    if( looks_like_value_address(p))
      *((value*)p) = copy_value( *((value*)p) );
  ...
}


Но все дело в функции looks_like_value_address() — она, в отличие от Boehm GC ошибаться не имеет права — стек попортится.

E>Как в той VM вообще жить предлагается? Может можно как-то в ней регить указатели на GC-объекты, или есть какие-то стабильные хэндлы, например, или ещё какая-то стратегия?


Да нормально оно всё живет (в Sciter, уже лет шесть как), есть pinned_value's которые держат объекты периметра. Для стека же у меня есть PROTECT(list of variables).
Просто при добавлении новых функций приходится быть предельно аккуратным и не забывать делать PROTECT().
Недавно вот ловил багу которая очень хитро и спорадически воспроизводилась. GC() он достаточно редко срабатывает и момент срабатывания зависит от входных данных — мечта дебагера.
Re: protect variables
От: jazzer Россия Skype: enerjazzer
Дата: 08.03.16 18:36
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Проблема в том что это PROTECT() нужно задавать явно. Иногда ошибаюсь и ловить проблему приходится долго и нудно.


CS>Что-то еще можно придумть на эту тему? Приветствуются идеи любой степени сумашествия.


Ну конкретно эти две строчки можно упаковать в единый макрос:
CS>
CS>value csf_native_function(VM* c, value obj, value p1, value p2) {
CS>  PROTECT(c, obj, p1, p2);
CS>

либо внутри PROTECT заюзать __PRETTY_FUNCTION__ и сравнить ее конец с __FUNCTION__ "(" (тут цикл abi::cxx_demangle(typeid<decltype(obj)>().name()) по всем аргументам PROTECT) ")"

это все для случая, который ты показал — что все, что нужно защитить, есть в аргументах.
Если нужно защищать еще какие-то локальные объекты на стеке, возникающие по мере выполнения функции — тогда надо их объявление упаковать в макрос, который сразу же после объявления сделает ему PROTECT.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: protect variables
От: jazzer Россия Skype: enerjazzer
Дата: 08.03.16 18:40
Оценка: +1
Здравствуйте, c-smile, Вы писали:

CS>Недавно вот ловил багу которая очень хитро и спорадически воспроизводилась. GC() он достаточно редко срабатывает и момент срабатывания зависит от входных данных — мечта дебагера.


Ну эта конкретная проблема решается специальной отладочной версией GC, который, во-первых, всегда срабатывает, а во-вторых, опять же всегда двигает все объекты в памяти, причем желательно двигать в какой-нть мусор, чтоб прога точно навернулась.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: protect variables
От: Evgeny.Panasyuk Россия  
Дата: 08.03.16 19:23
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>проблема в том что VM может быть несколько. Т.е. чтобы сконструировать raii holder нужно VM* и само value:


Необязательно. У тебя же отдельные кучи для разных VM, так? Тогда принадлежность к VM можно определить по диапазону в который попадает адрес.
Re: protect variables
От: Evgeny.Panasyuk Россия  
Дата: 08.03.16 19:39
Оценка: +1
Здравствуйте, c-smile, Вы писали:

CS>Т.к. cs_allocate_string() может вызывать GC() то значения obj, p1, p2 могут быть не валидными —

CS>указывать на старые локации объектов.
CS>Решаю эту проблему с помощью специадьного RAII объекта PROTECT:

Какой у тебя эффект от PROTECT? Эти указатели обновляются после перемещения объектов или объекты по указателям не перемещаются?
Re[2]: protect variables
От: c-smile Канада http://terrainformatica.com
Дата: 08.03.16 22:28
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

CS>>Решаю эту проблему с помощью специадьного RAII объекта PROTECT:


EP>Какой у тебя эффект от PROTECT? Эти указатели обновляются после перемещения объектов или объекты по указателям не перемещаются?


Значения переменных могут изменятся. После cs_push_var_address их адреса становятся известными GC процессору. Поэтому если объект переезжает на новое место все переменные которые на него смотрят меняют значение.

value obj, p1, p2;

// PROTECT ctor:
cs_push_var_address(c, &obj);
cs_push_var_address(c, &p1);
cs_push_var_address(c, &p2);

some_allocating_function(c);
// obj, p1, p2 may have different values here due to GC

// PROTECT dtor:
cs_pop_var_addresses(c,3)
Re[2]: protect variables
От: Evgeny.Panasyuk Россия  
Дата: 08.03.16 22:34
Оценка:
Здравствуйте, Erop, Вы писали:

CS>>
CS>>value csf_native_function(VM* c, value obj, value p1, value p2) {
CS>>  PROTECT(c, obj, p1, p2);
CS>>

E>1) А тебе не кажется, что залочить obj, p1 и p2 должны при вызове функции? А то мало ли откуда они пришли?

У него они не лочатся. После GC обновляется каждая копия указателя.
Re[2]: protect variables
От: c-smile Канада http://terrainformatica.com
Дата: 09.03.16 00:56
Оценка:
Здравствуйте, jazzer, Вы писали:

J>это все для случая, который ты показал — что все, что нужно защитить, есть в аргументах.

J>Если нужно защищать еще какие-то локальные объекты на стеке, возникающие по мере выполнения функции — тогда надо их объявление упаковать в макрос, который сразу же после объявления сделает ему PROTECT.

аргументы или перменные — не важно.
Они все на стеке лежат если рассматривать идеальную "C машину". Практика показывает что не всегда на стеке, а например в регистрах.
Не сильно хочется влазить в специфику процессоров или компиляторов. Сейчас оно компилируется всем что шевелится, т.е. чистый и незамутнённый C/C++.
Но если без этого никак то придётся наверное.

В принципе я могу текущую VM* положить в TLS если это поможет. Чтобы не таскать параметр для PROTECT например.
Re[3]: protect variables
От: Evgeny.Panasyuk Россия  
Дата: 09.03.16 01:35
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>аргументы или перменные — не важно.

CS>Они все на стеке лежат если рассматривать идеальную "C машину".

Не идеальную, а простую.

CS>Практика показывает что не всегда на стеке, а например в регистрах.

CS>Не сильно хочется влазить в специфику процессоров или компиляторов. Сейчас оно компилируется всем что шевелится, т.е. чистый и незамутнённый C/C++.
CS>Но если без этого никак то придётся наверное.

Даже если будешь некроссплатформенно сканировать регистры и стек, то проблема с консервативностью всё равно останется

CS>В принципе я могу текущую VM* положить в TLS если это поможет. Чтобы не таскать параметр для PROTECT например.


Либо через TLS, либо через быструю маску по адресу — проблема доп параметра решается. Ещё есть какие-нибудь барьеры для использования RAII обёрток?

P.S. В SpiderMonkey используются именно RAII обёртки:

All GC thing pointers stored on the stack (i.e., local variables and parameters to functions) must use the JS::Rooted<T> class.
...
SpiderMonkey makes it easy to remember to use JS::Rooted<T> types instead of a raw pointer because all of the API methods that may GC take a JS::Handle<T>, as described below.
...
All GC thing pointers that are parameters to a function must be wrapped in JS::Handle<T>. A JS::Handle<T> is a reference to a JS::Rooted<T>. All JS::Handle<T> are created implicitly by referencing a JS::Rooted<T>: It is not valid to create a JS::Handle<T> manually. Like JS::Rooted<T>, a JS::Handle<T> can be used as if it were the underlying pointer.

Since only a JS::Rooted<T> will cast to a JS::Handle<T>, the compiler will enforce correct rooting of any parameters passed to a function that may trigger GC. JS::Handle<T> exists because creating and destroying a JS::Rooted<T> is not free (though it only costs a few cycles). Thus, it makes more sense to only root the GC thing once and reuse it through an indirect reference. Like a reference, a JS::Handle is immutable: it can only ever refer to the JS::Rooted<T> that it was created for.

Вот релевантный код — там например строится интрузивный стек из Rooted.
Отредактировано 09.03.2016 1:49 Evgeny.Panasyuk . Предыдущая версия . Еще …
Отредактировано 09.03.2016 1:49 Evgeny.Panasyuk . Предыдущая версия .
Отредактировано 09.03.2016 1:36 Evgeny.Panasyuk . Предыдущая версия .
Re[4]: protect variables
От: c-smile Канада http://terrainformatica.com
Дата: 09.03.16 03:49
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, c-smile, Вы писали:


CS>>аргументы или перменные — не важно.

CS>>Они все на стеке лежат если рассматривать идеальную "C машину".

EP>Не идеальную, а простую.


CS>>Практика показывает что не всегда на стеке, а например в регистрах.

CS>>Не сильно хочется влазить в специфику процессоров или компиляторов. Сейчас оно компилируется всем что шевелится, т.е. чистый и незамутнённый C/C++.
CS>>Но если без этого никак то придётся наверное.

EP>Даже если будешь некроссплатформенно сканировать регистры и стек, то проблема с консервативностью всё равно останется


Согласен.

EP>Либо через TLS, либо через быструю маску по адресу — проблема доп параметра решается. Ещё есть какие-нибудь барьеры для использования RAII обёрток?


EP>P.S. В SpiderMonkey используются именно RAII обёртки:


RAII оно не бесплатное к сожалению. Мне не нравится что приходится создавать RAII объекты когда технически они будут действительно нужны в 0.01 % случаев.
Обычно исполнение кода GC не вызывает. Ну и потом в JS/SS много функций с переменным числом параметров. И далеко не всегда известно что там будет.

Вот
  имплементация функции
static value CSF_concat(VM *c)
{
    value vector = ThisVector(c);
    value nvector = 0;
    PROTECT(vector,nvector);

    FETCH(c, vector);
    int n = c->argc - 3 + 1;
    if( n == 0)
      return vector;

    int_t d = CsVectorSize(c,vector);
    int i;

    int_t extra = 0;
    for( i = 3; i <= c->argc; ++i )
    {
      value t = CsGetArg(c,i);
      if( CsVectorP(t) )
        extra += CsVectorSize(c,t); 
      else
        extra ++; 
    }
    nvector = CsMakeVector(c,d+extra, CsVectorClass(vector));
    
    n = d;
    for( i = 0; i < d; ++i )
      CsSetVectorElement(c,nvector, i, CsVectorElement(c,vector,i));

    for( i = 3; i <= c->argc; ++i )
    {
      value t = CsGetArg(c,i);
      if( CsVectorP(t) )
      {
        for(int j = 0; j < CsVectorSize(c,t); ++j)
          CsSetVectorElement(c,nvector,n++,CsVectorElement(c,t,j));
      }
      else
        CsSetVectorElement(c,nvector,n++,t);
    }
    return nvector;
}

array.concat(p1,p2,p3, ...)

там вообще параметров нет, берутся непосредственно со стека VM.

Эх, что-то мне говорит что придется так и жить с этим PROTECT ... Нет шастя в жизни ...
Re: protect variables
От: Кодт Россия  
Дата: 09.03.16 09:36
Оценка: 42 (1)
Здравствуйте, c-smile, Вы писали:

CS>есть имплментация script virtual machine.

CS>Эта VM использует moving garbage collector, т.е. объекты гуляют по памяти когда происходит GC.

Получается, что есть три вида указателей:
— управляемые перемещаемые (за которыми мусорщик следит, потому что знает о них)
— прибитые гвоздями (о которых мусорщик тоже знает)
— копии управляемых (о которых мусорщик не знает, ради оптимизации)

Решение номер раз: избавиться от копий. Это значит, что передавать указатели по ссылке.
Тут вопрос, что дешевле — держать мусорщик в курсе обо всём, или двойное разыменование. Думаю, что двойное разыменование дешевле.

void foo(VM* vm, managed_pointer const& p1, managed_pointer const& p2) {
  // если хочется работать с локальной копией - создаём и знакомим мусорщик с ней
  managed_pointer q1, q3, q4;
  BEGIN_TRACK(vm, &q1, &q3, &q4); // не залочили, а запомнили, что на стеке есть такая штука
                                  // пару BEGIN_TRACK - END_TRACK можно засахарить в раии SCOPED_TRACK
  q1 = p1;
  ...
  assert(p1 == q1); // мусорщик должен синхронно обновлять значения (адреса объектов) подконтрольных указателей
  ...
  bar(vm, p2);
  ...
  bar(vm, p2);
  ...
  END_TRACK(vm, &q1, &q3, &q4);
}


Номер два: чтобы случайно не передать указатель по значению, — использовать прокси-ссылку. Везде. Цена этого — слежение в уме за временем жизни ссылаемого указателя. Поэтому совет может быть как полезным, так и вредным.
using managed_pointer_ref = std::cref(managed_pointer);

void foo(VM* vm, managed_pointer_ref p1, managed_pointer_ref p2);


Номер три: использовать на стеке только scoped-переменные. И вообще запретить любую работу с примитивными managed_pointer-ами, кроме как передавать их по ссылке.
void foo(VM* vm, managed_pointer const& p1, managed_pointer const& p2) {
  scoped_managed_pointer q1(vm), q3(vm); // которые нельзя просто так сконструировать - только через знакомство с мусорщиком
  scoped_managed_pointer q2(vm, p2);
  q1 = p1;
}

Цена вопроса — наивная реализация потребует для N переменных запомнить N раз указатель на мусорщик.

Номер четыре: сделать scoped синтаксическим сахаром. Грубо говоря,
#define SCOPED_MP(vm, name) managed_pointer name; BEGIN_TRACK(vm, &name); SCOPED_EXIT({ END_TRACK(vm, &name); })

void foo(VM* vm) {
  SCOPED_MP(vm, q1);
  SCOPED_MP(vm, q3);
  SCOPED_MP(vm, q4);
  ...
}


Номер пять: избавиться от протягивания мусорщика явным параметром. Положить его в TLS, например.
Перекуём баги на фичи!
Re[2]: protect variables
От: Evgeny.Panasyuk Россия  
Дата: 09.03.16 11:04
Оценка:
Здравствуйте, Кодт, Вы писали:

К>чтобы случайно не передать указатель по значению, — использовать прокси-ссылку. Везде. Цена этого — слежение в уме за временем жизни ссылаемого указателя. Поэтому совет может быть как полезным, так и вредным.

К>
К>using managed_pointer_ref = std::cref(managed_pointer);

К>void foo(VM* vm, managed_pointer_ref p1, managed_pointer_ref p2);
К>


В SpiderMonkey именно так и сделанно:

All GC thing pointers that are parameters to a function must be wrapped in JS::Handle<T>. A JS::Handle<T> is a reference to a JS::Rooted<T>. All JS::Handle<T> are created implicitly by referencing a JS::Rooted<T>: It is not valid to create a JS::Handle<T> manually. Like JS::Rooted<T>, a JS::Handle<T> can be used as if it were the underlying pointer.

Since only a JS::Rooted<T> will cast to a JS::Handle<T>, the compiler will enforce correct rooting of any parameters passed to a function that may trigger GC. JS::Handle<T> exists because creating and destroying a JS::Rooted<T> is not free (though it only costs a few cycles). Thus, it makes more sense to only root the GC thing once and reuse it through an indirect reference. Like a reference, a JS::Handle is immutable: it can only ever refer to the JS::Rooted<T> that it was created for.

Re[5]: protect variables
От: Evgeny.Panasyuk Россия  
Дата: 09.03.16 11:34
Оценка: 42 (1) +1
Здравствуйте, c-smile, Вы писали:

EP>>Либо через TLS, либо через быструю маску по адресу — проблема доп параметра решается. Ещё есть какие-нибудь барьеры для использования RAII обёрток?

EP>>P.S. В SpiderMonkey используются именно RAII обёртки:
CS>RAII оно не бесплатное к сожалению.

Ты и так уже используешь PROTECT, у которого внутри то же RAII.
Есть вариант заменить PROTECT на Rooted<T>, как в SpiderMonkey, что избавит от проблем с PROTECT которые ты описал.

CS>Мне не нравится что приходится создавать RAII объекты когда технически они будут действительно нужны в 0.01 % случаев.


Понимаю и разделяю эту тоску Если нужен точный перемещающий GC, то альтернативой явной регистрации указателей будет модификация компилятора (например генерация карт кадров стека для каждой функции, по типу как с исключениями).

CS>Ну и потом в JS/SS много функций с переменным числом параметров. И далеко не всегда известно что там будет.

CS>array.concat(p1,p2,p3, ...)
CS>там вообще параметров нет, берутся непосредственно со стека VM.

Не пойму в чём проблема — любой указатель возвращаемый из VM сразу оборачивай в специальный smart_ptr, вот прямо в API самой VM.

CS>Эх, что-то мне говорит что придется так и жить с этим PROTECT ... Нет шастя в жизни ...


В SpiderMonkey живут без PROTECT, со специальными smart_ptr JS::Rooted<T>, что проще и надёжней.
Re[3]: protect variables
От: Erop Россия  
Дата: 09.03.16 22:16
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Но все дело в функции looks_like_value_address() — она, в отличие от Boehm GC ошибаться не имеет права — стек попортится.

Если это дешевле, то можно поддерживать свой список поверх стека таких указателей, например...

То есть, когда ты создаёшь свой smart_VM_ptr<T>, он внутри себя заводит T* на управляемый объект, а сказатель на этот T* добавляет в список, который просматривается, в результате чего получаем полный список управляемых указателей
Но есть же ещё и всякие ужосы, вроде массивов управляемых указателей, которые их двигают туда-сюда по памяти, например... Там ловко написав конструкторы копии и перемещающие, можно всё разрулить, но как-то небанально всё выходит...

Ещё есть другой вариант. Можно прямо в VM аллокировать массив указателей, занулить его, и запереть, что бы не двигался. Дальше, при создании smart_VM_ptr<T> занимать в этом массиве одну ячейку и хранить реальный указатель там, а в smart_VM_ptr<T> хранить указатель на ячейку, или даже индекс ячейки в массиве...



CS>Просто при добавлении новых функций приходится быть предельно аккуратным и не забывать делать PROTECT().

CS>Недавно вот ловил багу которая очень хитро и спорадически воспроизводилась. GC() он достаточно редко срабатывает и момент срабатывания зависит от входных данных — мечта дебагера.

Ты так и не ответил этот PROTECT — дорогой выходит? Или можно просто в каждой проксе его звать?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: protect variables
От: c-smile Канада http://terrainformatica.com
Дата: 10.03.16 07:08
Оценка:
Здравствуйте, Erop, Вы писали:

E>Ещё есть другой вариант. Можно прямо в VM аллокировать массив указателей, занулить его, и запереть, что бы не двигался. Дальше, при создании smart_VM_ptr<T> занимать в этом массиве одну ячейку и хранить реальный указатель там, а в smart_VM_ptr<T> хранить указатель на ячейку, или даже индекс ячейки в массиве...


Ну это как в LUA сделано. Там вообще к перменным только на стеке VM можно обращаться. По индексу. Мрак если честно.

E>Ты так и не ответил этот PROTECT — дорогой выходит? Или можно просто в каждой проксе его звать?


PROTECT достаточно дешевый:

void cs_push_var_address(c, value &var) {
  c->vars_on_c_stack[ c->vars_on_c_stack_top++ ] = &var;
}

void cs_pop_var_addresses(c, int n) {
  c->vars_on_c_stack_top -= n;
}
Re[5]: protect variables
От: Erop Россия  
Дата: 10.03.16 10:42
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>PROTECT достаточно дешевый:


CS>
CS>void cs_push_var_address(c, value &var) {
  c->>vars_on_c_stack[ c->vars_on_c_stack_top++ ] = &var;
CS>}

CS>void cs_pop_var_addresses(c, int n) {
  c->>vars_on_c_stack_top -= n;
CS>}
CS>


А как это с многопоточностью дружит? Или это не надо?
Если дешёвый, то в чём проблема? Возвращай из умного указателя прокси-объект, который в конструкторе заталкивает указатель куда надо, а в деструкторе — вынимает...
Вот набросок...

template<class T>
class VM_proxy {
       T volatile *& ptr;
       VM* const c;

       void operator = ( const VM_proxy& );  // = delete
   public:
       VM_proxy( const VM_proxy& o ) : ptr( o.ptr), c( o.c ) // по уму надо сделать доступным только для двух методов smart_VM_ptr, но тут лень. 
           { cs_push_var_address( c, &ptr ); }


       VM_proxy( T volatile *& ptr_, VM* c_ ) : ptr( ptr_ ), c( c_ ) 
           { cs_push_var_address( c, &ptr ); }
       ~VM_proxy() 
           { cs_pop_var_addresses( c, 1 ); }

      operator T*() { retrun ptr; }
      T* operator->() { return ptr; }
};

class VM_switcher {
    static VM* current; // = 0;
    VM* prev;
 
    VM_switcher( const VM_switcher& ); // = delete;
    void operator = (const VM_switcher& ); // = delete;
public:
    static GetCurrent() { assert( current!= 0 ); return current; }
    VM_switcher( VM* newCurrent ) : prev( current )
        { assert( newCurrent != 0 ); current = newCurrent; }
    ~VM_switcher() { current = prev; }
};

template<class T> 
class smart_VM_ptr {
    T volatile * ptr;
public:
   // конструкторы по вкусу

   VM_proxy<T> Get( VM* c = VM_switcher::GetCurrent() ) 
       { return proxy_VM<T>( ptr, c ); }
   VM_proxy<T> operator->() 
       { return proxyVM<T>( ptr,  VM_switcher::GetCurrent() ); }
   VM_proxy<const T> Get( VM* c = VM_switcher::GetCurrent() ) const 
       { return proxy_VM<const T>( ptr, c ); }
   VM_proxy<cont T> operator->() const 
       { return proxyVM<const T>( ptr,  VM_switcher::GetCurrent() ); }
   
};


Но мне так кажется, что проще сами VM_ptr регать. А чтобы компилятор не прятал их в регистры там и т. п. объявлять их ptr volatile...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.