Продвинутым отцам: превратить блокировку в suspend
на Smalltalk-е такая либа должна быть - они любят там такие вещи делать.
Глупый вопрос: а для чего это нужно? Мне даже интересно стало...
Ну например, для веб-приложений.
Так, например, в любом даже простеньком АИ такое надо.
ps
Фактически - это просто надстройка над автоматом, позволяющая в более наглядном виде записать последовательность действий.
Автоматные переходы очень ненаглядны - особенно если есть скобочные действия (последовательность вложенных контекстов - соответственно требуются какие-то действия при входе в контекст, и при выходе).
превратить блокировку в suspend
(типа ожидание ввода/вывода)Что-то я не понял, ожидание ввода/вывода оно и так suspend, чего сделать то надо?
void Main
{
using (BinaryReader reader = new BinaryReader("input.data"
{
int version = reader.ReadInt;
string name = reader.ReadString;
byte[] data;
if(name.StartWith("DataStream"
data = reader.ReadBytes(100);
else
data = reader.ReadBytes(15);
}
}
}
но при этом этот код должен формировать примерно следующую программу:
struct Context
{
BinaryReader reader;
int version;
string name;
byte[] data;
AsyncOperation operation;
}
enum RunState
{
Start,
End,
OpenStream,
ReadVersion,
ReadVersion_Wait,
ReadName
//и т.д.
}
void Main
{
RunState state = RunState.Start;
Context context = new Context;
while (state != RunState.End)
{
switch (state)
{
case RunState.ReadVersion;
context.operation = reader.StartAsyncReadInt;
state = RunState.ReadVersion_Wait;
break;
case RunState.ReadVersion_Wait:
if (operation.IsComplete
{
version = operation.EndAsyncReadInt;
state = RunState.ReadName;
}
case RunState.Start:
state = RunState.OpenStream;
break;
case RunState.OpenStream;
context.reader = new BinaryReader("input.data");
state = RunState.ReadVersion;
break;
}
}
}
т.е. фактически хочется, чтобы длительные операции переводились автоматом в асинхронные.
переводились автоматом в асинхронныеНу чего же в них асинхронного, если они в обоих случаях друг за другом выполняются? Асинхронные с чем? Контекст замечательно хранят треды, зачем чего-то еще изобретать?
Хреново хранят:
на диск в общем случае не сохранить - перезагрузку не выдерживают
разветвить поток не могут
слишком мало тредов одновременно возможно - попробуй например хотя бы 100000 сделать
попробуй например хотя бы 100000 сделатьскока там нулей? Не, не буду
Тебе вообще что-то существующее сконвертить надо, или сам что-то новое писать будешь?
Если второе, глянь Charm++, я его тут всем рекламирую Прикольная модель вычислений, может помочь. Правда то, что ты называешь контекстом придется все таки пальцами указывать, зато контрольные точки автоматически, асинхронность тоже автоматически, еще заодно распараллелишься
Интерфейс к перловым модулям бы
Charm++ - походу ни одному требованию не удовлетворяет, или я что не понял?
ни одному требованию не удовлетворяетИз этих обоих?
1) хорошо бы чтоб контекст умел выживать при перезагрузке машиныКонтекст - это С++ объект. Он умеет выживать при перезагрузке машины, это в шарме называется контрольная точка. Или что-то другое надо? Продолжить один контекст несколько раз тоже вроде без проблем, откопируй и продолжай. Charm++ - это чуть больше чем библиотека над С++, так что он дружен с перлом не больше чем сам С++ (а я не знаю что там между перлом и С++ )
2) неплохо бы иметь возможность продолжить один контекст несколько раз, по разным траекториям.
Правда чтобы писать на шарме надо по-другому _думать_. Так что может это и не то что ты хотел. Но если ты расчитываешь на такую конвертацию, ты же в любом случае должен по-другому думать вроде бы...
Судя по примерам, там надо всего лишь писать в CPS. Думать по-другому для этого не надо,
хватает формальных преобразований.
Это недостойно человека, так как преобразование в CPS вполне можно сделать автоматически.
То есть, не выполняется главное требование.
Асинхронные по отношению к треду, из которого они исполняются.
> Ну чего же в них асинхронного, если они в обоих случаях друг за другом выполняются?
Они не обязательно должны выполняться непосредственно друг за другом.
Пока происходит ожидание может выполняться другая задача, анализ входных событий и т.д.
> Контекст замечательно хранят треды, зачем чего-то еще изобретать?
да, согласен.
Но треды сильно заинкапсулированы между друг другом. соответственно, треды удобны когда задачи независимы между собой. Если же задача сильно завязана на входной поток событий, то отдельный тред не подойдет, Т.к. треды не позволяют легко прервать (или изменить) контекст и выполняемую последовательность действий
Возмем, например, орка из warcraft-а - у этого орка есть следующие задачи (утрировано):
1. Передвигаться из левого нижнего угла карты в верхний правый
2. Двигать ноги
3. Размахивать топором
4. Орать боевую песню.
5. Бить по шее всех встречных
каждая из этих задач описывается примерно так:
1. Передвигаться
2. Двигать ногами
while(не правый верхний угол)
{
найтиСвободноеМестоКотороеВедетВНужномНаправлении;
ПерейтиНаСвободноеМесто;
}
2. Махать топором
while(надоДвигаться)
{
Левой;
Правой;
}
while(естьТопор)
{
МахнутьВлево;
МахнутьНазад;
МахнутьВправо;
МахнутьВперед;
}
и т.д.
И эти задачи бессмысленно разносить по отдельным тредам: т.к. они сильно переплетены между собой, а также любой из действий все равно надо уметь прерывать в любой точке: если топор отобрали сразу после действия МахнутьВлево, то бессмысленно выполять все последующие действия.
Ну ладно, рожайте...
Чем они тебе не подходят? низкоуровневостью?
Ну, и ещё что-то совсем классическое лисповое.
Чтобы "всё есть список" и Маккарти пророк его.
---
...Я работаю антинаучным аферистом...
Преобразовать код - надежда есть.
call-with-current-continuation - звучит заманчиво, поищу
---
...Я работаю антинаучным аферистом...
google: continuation passing style ML
выдает большое кол-во ссылок по теме
по остальным фун. языкам тоже самое должно быть.
Да, про алгоритмы преобразования кода там есть, кто бы сомневался.
например, видел преобразование ML в Java-у и т.д.
Или хочешь сказать, что все это уровня студенческих поделок?
вот я и ищу, как проще
http://seaside.st/ ) это есть, для веб-приложений, на smalltalk.
Но что-то не понятно.
Вот, говорят что тут ( Но что-то не понятно.
Напиши, пожалуйста, более конкретно, как ты представляешь себе выполнение такой программы. Т.е. исходник -> реальность на псевдокоде.
Исходник:
int sum = 0;
while(true) {
POSTPONE next_item IN
Item(int a) : { sum += a; }
Eof : { break; }
END
}
print(sum);
Результат:
Сам event-loop, хранящий состояния (в данном случае единственное: NEXT_ITEM
machine Sum {
int sum;
states:
NEXT_ITEM: { next_item }
actions:
INIT: init {
sum = 0;
CHANGE_TO NEXT_ITEM
}
WHEN NEXT_ITEM: Item (int a) {
sum += a;
CHANGE_TO NEXT_ITEM
}
WHEN NEXT_ITEM: Eof {
print(sum);
END
}
}
в отдельной программе, здесь не показан.
Конструкция POSTPONE означает ожидание события, с которым, в зависимости от исхода,
связано несколько continuation'ов.
Ещё можно придумать надцать уровней абстракции и некий фреймворк, после чего код на куски придётся разбивать ручками, но это будет выглядеть достаточно логично.
Ну то есть все локальные переменные хранить в сериализабельном объекте State, у которого ещё будут какие-нить полезные свойства.
along a slow and painful path."
---
...Я работаю антинаучным аферистом...
На псевдо Scheme.
(let (bindings here)
(define main-loop
(local (dispatch (val)
(cond eql val 'next-item) ...)
eql val 'init) ...)
eql val 'eof) ...
(init ...)
(while (t)
(let val (call-with-cc
(lambda (cc)
(set where-to-save cc) ('INIT
(dispatch val
(define main-event-loop
(main-loop)
(loop
(let event (get-event
(where-to-save event
Что-то в этом роде? Я не совсем в курсе, как continuations работают в Scheme, но на Common Lisp такое можно реализовать, только будет немного корявее. Каждый раз, как мы вызываем where-to-save continuation, val в main-loop имеет значение текущего event. Контекст сохраняется автоматически, поскольку это вообще особенность всех языков с понятием closure. Можно написать специальные макросы для автоматизации создания подобной херни, добавить в них сохранение переменных и загрузку.
(cond eql val 'next-item) ...)
eql val 'init) ...)
eql val 'eof) ...
Это вручную построенный автомат?
Это аналог вот этого. Не забывай также, что в Лиспе с помощью макросов можно горы свернуть. Если уж CLOS можно на чистом Лиспе реализовать, то автоматную модель вообще как два пальца.
Item(int a) : { sum += a; }
Eof : { break; }
А в каком виде continuation хранится, можешь посмотреть?
Туда внутрь дают лазить?
Т.е. фактически программа в полуавтоматическом режиме переводится в CPS форму. Можно реализовать и полностью автомотический перевод, но это сложно, хотя может кто-то уже и написал нужную библиотеку.
Вот пример обхода дерева (dft2 начинает обход). Для каждого листа выполнение прерывается и возобновляется через restart.
(setq *saved* nil)
(=defun dft-node (tree)
(cond null tree) (restart
atom tree) (=values tree
(t (push #'(lambda (dft-node (cdr tree *saved*)
(dft-node (car tree
(=defun restart
(if *saved*
(funcall (pop *saved*
(=values 'done
(=defun dft2 (tree)
(setq *saved* nil)
(=bind (node) (dft-node tree)
(cond eq node 'done) (=values nil
(t (princ node) (restart
Вспомнил такую вещь: в какой-то схеме видел сохранение образа
виртуальной машины на диск.
Это насчёт перезагрузок.
---
...Я работаю антинаучным аферистом...
Сначала почитаю книжку про рабоче-крестьянский способ.
Маза это то, что надо.
> Вспомнил такую вещь: в какой-то схеме видел сохранение образа
> виртуальной машины на диск.
Это то, что я называю низкоуровневыми хаками.
В Guile, насколько помню, всё встроено.
---
...Я работаю...
Состояние виртуальной машины можно сохранять и в Лиспе и это не хак, там компилятор даже так работает.
Само по себе - не хак.
А вот использовать это для того, что я хочу - хак.
continuations to any output port."
---
...Я работаю антинаучным аферистом...
Что нужно, а что нет, вполне может определить разработчик, а то и транслятор.
Поэтому я и заинтересовался рабоче-крестьянским вариантом.
Я пока представляю себе язык, похожий на традиционный Бейсик,
который компилируется в Лисп.
как делается лисп на лиспе.
Можешь заглянуть туда, если потребуется.
Обычно мусор собирается.
Нонеча системы умные пошли.
---
...Я работаю антинаучным аферистом...
Ну круто, осталось найти как перловые модули вызывать
Кстати, написал для OCaml + p4 тоже самое, что в On Lisp. Выглядит корявее, но только из-за того, что не знаю как заменить вызов функции на вызов с *cont*. Пример с деревьями работает, хочу теперь переключение процессов реализовать.
Проблема в том, что внутрь closure не заглянуть, то есть плохо с сохранением/восстановлением.
То есть нужно всё руками делать, а тогда практически пофиг, в какой язык компилировать.
список, у которого первым идёт атом function, а потом всё
остальное.
У них же показано, как делается лисп на лиспе.
Это уже проще.
Я, конечно, знаю ещё один способ поизвращаться,
но он тебе вряд ли подойдёт.
---
...Я работаю антинаучным аферистом...
Оставить комментарий
Marinavo_0507
Хочется советов, что бы такое поизучать, чтоб сделать следущее.Пишешь программу, которая вызывает некие блокирующиеся функции (типа ожидание ввода/вывода).
А умный компилятор или среда выполнения сама преобразует это в continuation passing style,
то есть сохраняет контекст, и передаёт выполнение некоей внешней программе, в которой можно разместить
типичный или не очень event loop, чтоб в нужный момент продолжить выполнение из сохранённого контекста.
Низкоуровневые хаки не интересуют. Язык чем культурнее, тем лучше. Сделать с нуля понятное дело можно,
но меня заломает.
Желательные свойства:
1) хорошо бы чтоб контекст умел выживать при перезагрузке машины
2) неплохо бы иметь возможность продолжить один контекст несколько раз, по разным траекториям.
Кто-нибудь видел что-то подобное?