пришла идея по поводу YARMI.
сейчас сабж устроен так, что каждая сессия наследует один и тот же инвокер — что мне кажется не совсем разумным, ибо процедуры у каждой сессии один и те же.
думаю, переделать так, чтоб инвокер был один, и тогда каждая процедура должна иметь дополнительный аргумент — user_context, для которого производится инвокинг.
сейчас, менеджер сессий у меня реализован в global_context_base, который юзер должен унаследовать в свой глобальный контекс.
инвокеру, при создании, нужно указывать объект IO(который в данный момент является session_base) для того чтоб он мог слать ответы. и сейчас, я не очень представляю как это сделать удобней...
при одном общем инвокере, не получится его конструировать с ассоциацией с сокетом или сессией, значит, сокет/сессию нужно передавать еще одним дополнительным аргументом в процедуры.
пример сгенерированного инвокера.
тут, registration(), activation(), етц — адаптеры для вызова удаленных процедур.
в них, помимо сериализации, происходит вызов 'io.send()', который собственно и отправляет данные.
вопросов у меня три:
1. имеет ли смысл такая переделка?
2. есть ли предложения о том, что нужно сделать, чтоб сигнатуры процедур соответствовали описанным юзером(т.е. чтоб не поставлять дополнительные аргументы)?
3. если таки придется изменять сигнатуры процедур, делать ли это скрытно от пользователя, или заставить его вписывать эти дополнительные аргументы?
спасибо!
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
наверное я слишком сложно все описал, попробую псевдокодом.
сейчас оно вот так:
// сгенерированный инвокерtemplate<typename IO, typename Impl>
struct invoker {
invoker(IO &io, Impl &impl)
:io(io)
,impl(impl)
{}
// этот метод генерируется всегда, и, собственно он и производит инвокингvoid invoke(const char *ptr, const std::size_t size) {/* внутренности сейчас не интересуют */}
// эти методы генерируются по просьбе юзераvoid ping() {}
void stat() {}
private:
IO &io;
Impl &impl;
};
// сессияstruct session_base {
session_base(boost::asio::io_service &ios) {}
// при чтении данных вызывается этот методvirtual void on_received(const char *ptr, const std::size_t size) = 0;
};
// user_context описываемый пользователемstruct user_context: session_base, invoker {
user_context(boost::asio::io_service &ios)
:session_base(ios)
,invoker<user_context>(*this, *this)
{}
// к примеру, этот метод зовется по таймеруvoid timeout() {
// тут мы зовем удаленные процедуры, которые были сгенерированы в инвокере.
ping();
stat();
// ниже приведены процедуры которые вызываются в ответ
}
// сгенерированный инвокер рассчитывает на то, что у нас есть
// такие процедуры, которые вызываются противоположной стороной:void on_ping(const std::string &msg) {}
void on_stat(const std::vector<some type> &stat) {}
private:
// при чтении данных вызывается этот метод - реализацияvoid on_received(const char *ptr, const std::size_t size) {
// вызываем invoke() нашего инвокера
invoke(ptr, size);
}
};
т.е. тут все просто, локальные данные user_context`а юзер вписывает в сам user_context. а вызов удаленных процедур прост потому, что user_context наследует инвокер в котором и сгенерены вызывающие методы, и session_base в котором и живет сокет.
теперь я хочу чтоб инвокер существовал в единственном экземпляре.
это создаст проблему: в обрабатываемую процедуру нужно передавать и сам user_context для оперирования данными юзера, и session_base чтоб отправить данные противоположной стороне.
а вот и три вопроса, из топика:
1. имеет ли смысл такая переделка?
2. есть ли предложения о том, что нужно сделать, чтоб сигнатуры процедур соответствовали описанным юзером(т.е. чтоб не поставлять дополнительные аргументы)?
3. если таки придется изменять сигнатуры процедур, делать ли это скрытно от пользователя, или заставить его вписывать эти дополнительные аргументы?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, abrarov, Вы писали:
A>А смысл есть? Что экономится?
смысл чисто логический: зачем каждому user_context`у по инвокеру, с учетом того что все инвокеры абсолютно идентичны?
да и ничего не экономится, ибо инвокеры не имеют данных, а значит что будет один инвокер или 100к инвокеров — памяти расходуется столько же.
собственно, потому мой первый вопрос и заключался в: "имеет ли смысл такая переделка?"
A>Уходит необходимость наследоваться от invoker (вижу множественное наследование в https://github.com/niXman/yarmi/blob/master/examples/echo/server/user_context.hpp — не оно ли причина такого желания?)
нет, причина не в этом)
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>да и ничего не экономится, ибо инвокеры не имеют данных, а значит что будет один инвокер или 100к инвокеров — памяти расходуется столько же.
хотя нет.
мы наследуем session_base, у которого есть виртуальные методы, а значит, как минимум будет расходоваться память под vtbl, если я ничего не путаю...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
X>кстати, при использовании одного инвокера, не получится его использовать с многопоточным сервером X>наверное, таки нет смысла в этой переделке...
Тоже думал об этом, но пришел к выводу, что если очень захочется, то invoker можно сделать потокобезопасным (правда, сейчас непонятно, какая у этого будет цена).
Честно говоря, при моем текущем уровне знаний YARMI я не вижу необходимости в переделке. Да, наследование — это слишком тесная связь классов. Но эта, пожалуй, единственная возможная причина переделки не тянет на "необходимость".
Programs must be written for people to read, and only incidentally for machines to execute
Здравствуйте, niXman, Вы писали:
X>2. есть ли предложения о том, что нужно сделать, чтоб сигнатуры процедур соответствовали описанным юзером(т.е. чтоб не поставлять дополнительные аргументы)?
Сохраняй контекст сессии в TLS перед вызовом обработчика. Минус этого подхода в том, что если ты станешь использовать файберы вместо потоков, все сломается.
Здравствуйте, Lazin, Вы писали:
L>Сохраняй контекст сессии в TLS перед вызовом обработчика.
или я чего-то не понял, или...
во-первых — сервер однопоточный. во-вторых — даже если он многопоточный, то это не значит, что для каждого подключения создается отельный поток.
или что?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>Здравствуйте, Lazin, Вы писали:
L>>Сохраняй контекст сессии в TLS перед вызовом обработчика. X>или я чего-то не понял, или...
X>во-первых — сервер однопоточный. во-вторых — даже если он многопоточный, то это не значит, что для каждого подключения создается отельный поток. X>или что?
Ничего сложного. Сервер получил сообщение, сохранил весь необходимый контекст (клиентская сессия, сокет и тд) в TLS слот (или в глобальную переменную, в случае однопоточного сервера), вызвал обработчик, очистил TLS слот.
Это довольно распространенная практика, в cherrypy, например, request и response не передаются в обработчики через параметры а доступны как глобальные переменные.
Здравствуйте, Lazin, Вы писали:
L>Ничего сложного. Сервер получил сообщение, сохранил весь необходимый контекст (клиентская сессия, сокет и тд) в TLS слот (или в глобальную переменную, в случае однопоточного сервера), вызвал обработчик, очистил TLS слот. L>Это довольно распространенная практика, в cherrypy, например, request и response не передаются в обработчики через параметры а доступны как глобальные переменные.
жуть какая =)
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>Здравствуйте, Lazin, Вы писали:
L>>Ничего сложного. Сервер получил сообщение, сохранил весь необходимый контекст (клиентская сессия, сокет и тд) в TLS слот (или в глобальную переменную, в случае однопоточного сервера), вызвал обработчик, очистил TLS слот. L>>Это довольно распространенная практика, в cherrypy, например, request и response не передаются в обработчики через параметры а доступны как глобальные переменные.
X>жуть какая =)
чего это вдруг? cherrypy — очень приятная в работе штука
Здравствуйте, Lazin, Вы писали:
L>чего это вдруг? cherrypy — очень приятная в работе штука
ты ведь знаешь, что не константные глобальные переменные — зло.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>Здравствуйте, Lazin, Вы писали:
L>>чего это вдруг? cherrypy — очень приятная в работе штука X>ты ведь знаешь, что не константные глобальные переменные — зло.
Знаю, поэтому предложил положить данные в TLS. Можно хранить данные где-нибудь еще, в поле какого-нибудь объекта, который владеет этой сессией и вызывает обработчики, я же не знаю какая у тебя там архитектура. В общем, я предлагал просто сохранить где-нибудь все что нужно, а не тащить через параметры.
Здравствуйте, Lazin, Вы писали:
L>Знаю, поэтому предложил положить данные в TLS. Можно хранить данные где-нибудь еще, в поле какого-нибудь объекта, который владеет этой сессией и вызывает обработчики, я же не знаю какая у тебя там архитектура. В общем, я предлагал просто сохранить где-нибудь все что нужно, а не тащить через параметры.
я понял.
есть в этом кое-что здравое, но нужно продумать где хранить это, как для однопоточного, так и для многопоточного случая..
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)