Продвинутым отцам: превратить блокировку в suspend
на Smalltalk-е такая либа должна быть - они любят там такие вещи делать.
Глупый вопрос: а для чего это нужно? Мне даже интересно стало...
Ну например, для веб-приложений.
В основном, это нужно - когда есть есть внешний поток событий, и при этом в программе нужно выполнять длительные действия (разнесенную по времени последовательность действий).
Так, например, в любом даже простеньком АИ такое надо.
ps
Фактически - это просто надстройка над автоматом, позволяющая в более наглядном виде записать последовательность действий.
Автоматные переходы очень ненаглядны - особенно если есть скобочные действия (последовательность вложенных контекстов - соответственно требуются какие-то действия при входе в контекст, и при выходе).
Так, например, в любом даже простеньком АИ такое надо.
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 сделать
попробуй например хотя бы 100000 сделатьскока там нулей? Не, не буду
Тебе вообще что-то существующее сконвертить надо, или сам что-то новое писать будешь?
Если второе, глянь Charm++, я его тут всем рекламирую
Прикольная модель вычислений, может помочь. Правда то, что ты называешь контекстом придется все таки пальцами указывать, зато контрольные точки автоматически, асинхронность тоже автоматически, еще заодно распараллелишься 
Писать хочется новое, но чтоб была возможность вызывать функции из готовых библиотек.
Интерфейс к перловым модулям бы
Charm++ - походу ни одному требованию не удовлетворяет, или я что не понял?
Интерфейс к перловым модулям бы

Charm++ - походу ни одному требованию не удовлетворяет, или я что не понял?
ни одному требованию не удовлетворяетИз этих обоих?
1) хорошо бы чтоб контекст умел выживать при перезагрузке машиныКонтекст - это С++ объект. Он умеет выживать при перезагрузке машины, это в шарме называется контрольная точка. Или что-то другое надо? Продолжить один контекст несколько раз тоже вроде без проблем, откопируй и продолжай. Charm++ - это чуть больше чем библиотека над С++, так что он дружен с перлом не больше чем сам С++ (а я не знаю что там между перлом и С++
2) неплохо бы иметь возможность продолжить один контекст несколько раз, по разным траекториям.
)Правда чтобы писать на шарме надо по-другому _думать_. Так что может это и не то что ты хотел. Но если ты расчитываешь на такую конвертацию, ты же в любом случае должен по-другому думать вроде бы...
> Правда чтобы писать на шарме надо по-другому _думать_.
Судя по примерам, там надо всего лишь писать в CPS. Думать по-другому для этого не надо,
хватает формальных преобразований.
Это недостойно человека, так как преобразование в CPS вполне можно сделать автоматически.
То есть, не выполняется главное требование.
Судя по примерам, там надо всего лишь писать в CPS. Думать по-другому для этого не надо,
хватает формальных преобразований.
Это недостойно человека, так как преобразование в CPS вполне можно сделать автоматически.
То есть, не выполняется главное требование.
> Асинхронные с чем?
Асинхронные по отношению к треду, из которого они исполняются.
> Ну чего же в них асинхронного, если они в обоих случаях друг за другом выполняются?
Они не обязательно должны выполняться непосредственно друг за другом.
Пока происходит ожидание может выполняться другая задача, анализ входных событий и т.д.
> Контекст замечательно хранят треды, зачем чего-то еще изобретать?
да, согласен.
Но треды сильно заинкапсулированы между друг другом. соответственно, треды удобны когда задачи независимы между собой. Если же задача сильно завязана на входной поток событий, то отдельный тред не подойдет, Т.к. треды не позволяют легко прервать (или изменить) контекст и выполняемую последовательность действий
Возмем, например, орка из warcraft-а - у этого орка есть следующие задачи (утрировано):
1. Передвигаться из левого нижнего угла карты в верхний правый
2. Двигать ноги
3. Размахивать топором
4. Орать боевую песню.
5. Бить по шее всех встречных
каждая из этих задач описывается примерно так:
1. Передвигаться
и т.д.
И эти задачи бессмысленно разносить по отдельным тредам: т.к. они сильно переплетены между собой, а также любой из действий все равно надо уметь прерывать в любой точке: если топор отобрали сразу после действия МахнутьВлево, то бессмысленно выполять все последующие действия.
Асинхронные по отношению к треду, из которого они исполняются.
> Ну чего же в них асинхронного, если они в обоих случаях друг за другом выполняются?
Они не обязательно должны выполняться непосредственно друг за другом.
Пока происходит ожидание может выполняться другая задача, анализ входных событий и т.д.
> Контекст замечательно хранят треды, зачем чего-то еще изобретать?
да, согласен.
Но треды сильно заинкапсулированы между друг другом. соответственно, треды удобны когда задачи независимы между собой. Если же задача сильно завязана на входной поток событий, то отдельный тред не подойдет, Т.к. треды не позволяют легко прервать (или изменить) контекст и выполняемую последовательность действий
Возмем, например, орка из warcraft-а - у этого орка есть следующие задачи (утрировано):
1. Передвигаться из левого нижнего угла карты в верхний правый
2. Двигать ноги
3. Размахивать топором
4. Орать боевую песню.
5. Бить по шее всех встречных
каждая из этих задач описывается примерно так:
1. Передвигаться
2. Двигать ногами
while(не правый верхний угол)
{
найтиСвободноеМестоКотороеВедетВНужномНаправлении;
ПерейтиНаСвободноеМесто;
}
2. Махать топором
while(надоДвигаться)
{
Левой;
Правой;
}
while(естьТопор)
{
МахнутьВлево;
МахнутьНазад;
МахнутьВправо;
МахнутьВперед;
}
и т.д.
И эти задачи бессмысленно разносить по отдельным тредам: т.к. они сильно переплетены между собой, а также любой из действий все равно надо уметь прерывать в любой точке: если топор отобрали сразу после действия МахнутьВлево, то бессмысленно выполять все последующие действия.
Ну ладно, рожайте... 

Ты же вроде был поклонник ФЯ - они-то точно должны такое уметь делать.
Чем они тебе не подходят? низкоуровневостью?
Чем они тебе не подходят? низкоуровневостью?
Наверное, ты ищешь function и call-with-current-continuation.
Ну, и ещё что-то совсем классическое лисповое.
Чтобы "всё есть список" и Маккарти пророк его.
---
...Я работаю антинаучным аферистом...
Ну, и ещё что-то совсем классическое лисповое.
Чтобы "всё есть список" и Маккарти пророк его.
---
...Я работаю антинаучным аферистом...
Сами по себе, конечно, они такого не делают.
Преобразовать код - надежда есть.
Преобразовать код - надежда есть.
call-with-current-continuation - звучит заманчиво, поищу
Спрашивать у бродячих схематиков.
---
...Я работаю антинаучным аферистом...
---
...Я работаю антинаучным аферистом...
например
google: continuation passing style ML
выдает большое кол-во ссылок по теме
по остальным фун. языкам тоже самое должно быть.
google: continuation passing style ML
выдает большое кол-во ссылок по теме
по остальным фун. языкам тоже самое должно быть.
Да, про алгоритмы преобразования кода там есть, кто бы сомневался.
кроме алгоритмов программы тоже встречаются.
например, видел преобразование ML в Java-у и т.д.
Или хочешь сказать, что все это уровня студенческих поделок?
например, видел преобразование ML в Java-у и т.д.
Или хочешь сказать, что все это уровня студенческих поделок?
эти преобразователи скорее всего нужно сильно модифицировать, чтобы добиться нужного результата
вот я и ищу, как проще
вот я и ищу, как проще
Вот, говорят что тут ( http://seaside.st/ ) это есть, для веб-приложений, на smalltalk.
Но что-то не понятно.
Но что-то не понятно.
Напиши, пожалуйста, более конкретно, как ты представляешь себе выполнение такой программы. Т.е. исходник -> реальность на псевдокоде.
Суммирование элементов из списка.
Исходник:
Результат:
в отдельной программе, здесь не показан.
Конструкция POSTPONE означает ожидание события, с которым, в зависимости от исхода,
связано несколько continuation'ов.
Исходник:
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, у которого ещё будут какие-нить полезные свойства.
Ещё можно придумать надцать уровней абстракции и некий фреймворк, после чего код на куски придётся разбивать ручками, но это будет выглядеть достаточно логично.
Ну то есть все локальные переменные хранить в сериализабельном объекте State, у которого ещё будут какие-нить полезные свойства.
"Languages are coming closer and closer to reinventing Lisp
along a slow and painful path."
---
...Я работаю антинаучным аферистом...
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 хранится, можешь посмотреть?
Туда внутрь дают лазить?
А в каком виде continuation хранится, можешь посмотреть?
Туда внутрь дают лазить?
В Схеме не знаю, они там встроенные. На Lisp я видел реализацию только в книге On Lisp (доступна online). Там все сделано просто, по рабоче-крестьянски. Фактически тебе дают вместо обычных макросов особые - =defun, =values, =bind, =lambda, =funcall, =applay. Они неявно манипулируют переменной *cont*, она теперь передается в каждую функцию и при возвращении =value вызывает *cont* от результата. Благодаря связыванию переменных все гладко работает.
Т.е. фактически программа в полуавтоматическом режиме переводится в CPS форму. Можно реализовать и полностью автомотический перевод, но это сложно, хотя может кто-то уже и написал нужную библиотеку.
Вот пример обхода дерева (dft2 начинает обход). Для каждого листа выполнение прерывается и возобновляется через restart.
Т.е. фактически программа в полуавтоматическом режиме переводится в 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, насколько помню, всё встроено.
---
...Я работаю...
В Guile, насколько помню, всё встроено.
---
...Я работаю...
Только осторожно с ней. Она посвящена макросам и основы Лиспа в ней не даются. Несколько глав начиная с Continuations посвященны применению этой техники (точнее там цель иная, но continuations дают возможность делать все лучше и быстрее).
Состояние виртуальной машины можно сохранять и в Лиспе и это не хак, там компилятор даже так работает.
Состояние виртуальной машины можно сохранять и в Лиспе и это не хак, там компилятор даже так работает.
> Состояние виртуальной машины можно сохранять и в Лиспе и это не хак, там компилятор даже так работает.
Само по себе - не хак.
А вот использовать это для того, что я хочу - хак.
Само по себе - не хак.
А вот использовать это для того, что я хочу - хак.
Мне тут подсказывают: "Yes, SISC allows you to serialize
continuations to any output port."
---
...Я работаю антинаучным аферистом...
continuations to any output port."
---
...Я работаю антинаучным аферистом...
Ну как бы хочется не только сериализации, а ещё и выкинуть ненужное.
Что нужно, а что нет, вполне может определить разработчик, а то и транслятор.
Поэтому я и заинтересовался рабоче-крестьянским вариантом.
Я пока представляю себе язык, похожий на традиционный Бейсик,
который компилируется в Лисп.
Что нужно, а что нет, вполне может определить разработчик, а то и транслятор.
Поэтому я и заинтересовался рабоче-крестьянским вариантом.
Я пока представляю себе язык, похожий на традиционный Бейсик,
который компилируется в Лисп.
В "Мире Лиспа" известных финских сочинителей есть пример,
как делается лисп на лиспе.
Можешь заглянуть туда, если потребуется.
Обычно мусор собирается.
Нонеча системы умные пошли.
---
...Я работаю антинаучным аферистом...
как делается лисп на лиспе.
Можешь заглянуть туда, если потребуется.
Обычно мусор собирается.
Нонеча системы умные пошли.
---
...Я работаю антинаучным аферистом...
Кстати, вот Web на Lisp через Continuations. На странице описание методики, а сам пакет называется UCW.
http://lisp.tech.coop/Web%2FContinuation
http://lisp.tech.coop/Web%2FContinuation
Ну круто, осталось найти как перловые модули вызывать 

Кстати, написал для OCaml + p4 тоже самое, что в On Lisp. Выглядит корявее, но только из-за того, что не знаю как заменить вызов функции на вызов с *cont*. Пример с деревьями работает, хочу теперь переключение процессов реализовать.
Дочитал On Lisp до этого места.
Проблема в том, что внутрь closure не заглянуть, то есть плохо с сохранением/восстановлением.
То есть нужно всё руками делать, а тогда практически пофиг, в какой язык компилировать.
Проблема в том, что внутрь closure не заглянуть, то есть плохо с сохранением/восстановлением.
То есть нужно всё руками делать, а тогда практически пофиг, в какой язык компилировать.
В классическом лиспе, например у финнов, замыкание --- это
список, у которого первым идёт атом function, а потом всё
остальное.
У них же показано, как делается лисп на лиспе.
Это уже проще.
Я, конечно, знаю ещё один способ поизвращаться,
но он тебе вряд ли подойдёт.
---
...Я работаю антинаучным аферистом...
список, у которого первым идёт атом function, а потом всё
остальное.
У них же показано, как делается лисп на лиспе.
Это уже проще.
Я, конечно, знаю ещё один способ поизвращаться,
но он тебе вряд ли подойдёт.
---
...Я работаю антинаучным аферистом...
Оставить комментарий
Marinavo_0507
Хочется советов, что бы такое поизучать, чтоб сделать следущее.Пишешь программу, которая вызывает некие блокирующиеся функции (типа ожидание ввода/вывода).
А умный компилятор или среда выполнения сама преобразует это в continuation passing style,
то есть сохраняет контекст, и передаёт выполнение некоей внешней программе, в которой можно разместить
типичный или не очень event loop, чтоб в нужный момент продолжить выполнение из сохранённого контекста.
Низкоуровневые хаки не интересуют. Язык чем культурнее, тем лучше. Сделать с нуля понятное дело можно,
но меня заломает.
Желательные свойства:
1) хорошо бы чтоб контекст умел выживать при перезагрузке машины
2) неплохо бы иметь возможность продолжить один контекст несколько раз, по разным траекториям.
Кто-нибудь видел что-то подобное?