Zagursky
-
Upload
kuchinskaya -
Category
Documents
-
view
449 -
download
1
Transcript of Zagursky
Файберы
Невытесняющая многозадачность на Java
S R
MsgRequest
MsgResponse
ResponseHandler()
RequestHandler()
public final class RequestCreateAvatar { private final String avatarName; private final Character character; }
public final class CreateAvatarHandler { public void handle(RequestCreateAvatar msg) { CreateAvatarResult result = avatarManager.createAvatar(msg); sendMsg(new ResponseCreateAvatar(result)); } }
void createAvatar(String avatarName, Character character) { sendCmd(new RequestCreateAvatar(avatarName, character)); }
public final class CreateAvatarResponseHandler { public void handle(ResponseCreateAvatar msg) { sendMsgToClient(msg); } }
Async<Void> createAvatar(String avatarName, Character character) { CreateAvatarResult result = call(avatarManager.createAvatar( avatarName, character )); sendMsgToClient(result); return nothing(); }
Причины • Хочется писать простой линейный Java-код
• ОС-потоки слишком накладны
• Требуется сохранить однопоточный дизайн
С высоты птичьего полёта
Инструментатор Планировщик
Инспекции Рантайм
import fibers.Async; import static fibers.Fiber.*; class Foo { Async<Integer> calcResult(int factor) { return result(42*factor); } Async<Integer> doWork() { int tmp = call(calcResult(314)); return result(tmp); } }
Синтаксис (1)
Async<Integer> calcResult(int factor) {
return result(42*factor);
}
Async<Integer> doWork() {
int tmp = сall(calcResult(314));
return result(tmp);
}
Синтаксис (2) class Foo {
void bar(FiberManager f) {
f.scheduleFiber(doWork("Hi!"));
}
}
Препарируемый метод
Async<String> baz(String second) {
String first = call(getFirst());
return result(join(first, second));
}
Замена исходного метода
Async<String> baz(String second) {
return new baz_Context(second);
}
Контекст вызова метода
class baz_Context extends Async<String> {
String _second, first;
@Override
void update() { state = baz_Async(this); }
}
Инструментированный метод int baz_Async(baz_Context c) {
switch (c.state) {
case 0: c.result = getFirst(); return 1;
case 1: c.first = (String) c.takeResult();
c.result = join(c.first, c._second);
return -1;
}
}
Инструментирование (1) • Поиск точек разрыва
– call
– yield
• Вставка switch-case и return
Инструментирование (2) • Как обойти вызов
блока finally?
• Не вызывать его!
Async<Integer> foo() {
try {
int t = call(bar());
return result(t);
} finally { close(); }
}
Инструментирование (3) • Подмена доступа к параметрам метода и
локальным переменным
• int x; x = 42; context.x = 42;
Инструментирование (4)
... x = call(bar()); ...
... case 42 Context r = new bar_Context(); c.pushCall(r); return 43; case 43: c.x = (String) c.takeResult(); ...
Инструментирование (5) • Директивы, ссылающиеся на исходный файл,
вставляются инструментатором в сгенерированный метод
• Это позволяет использовать пошаговую отладку
• Ограничение: нельзя с помощью Step Over перейти через точку разрыва
Исключения • Рантайм ловит все исключения, вылетевшие из
Async-метода
• Исключение заворачивается в служебный объект
• При запросе результата вызывающим Async-методом исключение выбрасывается заново
• Если исключение вылетает за пределы файбера, то это диагностируется как ошибка
Прерывание файбера • В каждой точке входа в Async-метод
проверяется, не был ли прерван текущий файбер
• Если файбер был прерван, то выбрасывается служебное исключение FiberInterruptedException
Функции FiberManager • Планировщик
• Обрабатывает приостановку, возобновление и прерывание файберов
• У всех файберов одинаковый приоритет
Гарантии FiberManager • Файберы должны исполняться в порядке
добавления и возобновления
• Приостановленные файберы не должны исполняться
Thread safety • Методы scheduleFiber() и resumeFiber()
могут выполняться из любого потока
• Все остальные методы – только из потока, к которому привязан планировщик
• На каждый поток есть свой планировщик
StackTrace (1) • Stack trace из new Throwable().getStackTrace() не
достаточен • Программист хочет видеть:
– стек Async-методов – стек в момент порождения файбера
• Файбер может быть создан внутри другого файбера • Stack trace должен разрешаться (resolve) только
тогда, когда он реально необходим
StackTrace (2) • Рантайм получает уже готовое исключение
• К этому исключению нужно добавить
– стек файбера
– стек момента порождения файбера
• Используется addSuppressed()
StackTrace (3) • Переопределяется метод fillInStackTrace(),
чтобы не собирать обычный stack trace
• Массив StackTraceElement[] формируется только по запросу getCause() и при сериализации
java.lang.Throwable at asserts.Verify.fail(Verify.java:47) at gm.cc.c.t.CmdTeleportRandomByMapType.asyncRun$Async(CmdTeleportRandomByMapType.java:152) at gm.cc.c.t.CmdTeleportRandomByMapType$Context_asyncRun_LmsgSystem#locales#Abonent_.update(CmdTeleportRandomByMapType.java) at f.i.AsyncImpl.updateSafe(AsyncImpl.java:37) at f.i.FiberImpl.updateInternal(FiberImpl.java:154) at f.i.FiberImpl.update(FiberImpl.java:130) at f.i.FiberManagerAbstract.updateFiber(FiberManagerAbstract.java:297)
... at java.lang.Thread.run(Thread.java:722) Suppressed: f.i.FiberStackTraceHolderException: Fiber stack trace Caused by: f.i.FiberStackTraceHolderException$Helper at asserts.Verify.fail(Verify.java:47) at gm.cc.c.t.CmdTeleportRandomByMapType.asyncRun(CmdTeleportRandomByMapType.java:141) at ms.l.AsyncMsg.run(AsyncMsg.java:37) at ms.i.mp.ServerMsgPublisher.processMsg(ServerMsgPublisher.java:331) at ms.i.mp.ServerMsgPublisher.addMsgFast(ServerMsgPublisher.java:118) ... at java.lang.Thread.run(Thread.java:722)
Узкие места (1) • Инструментатор меняет сигнатуру метода • Служебные методы call() и result() вырезаются,
чтобы байткод соответствовал новой сигнатуре • Об этом не догадывается IntelliJ Idea, которая
вставляет проверки на != null • Инструментатор вырезает такие проверки из
Async-методов
Узкие места (2) • Если Async-метод декларирует throws
SomeUncheckedException – синтаксис java потребует его обработки
– catch-блок для этого исключения никогда не будет вызван
– исключение пролетит в рантайм, т. к. для него нет соответствующего catch
Узкие места (3) • Синтаксис не запрещает вызов Async-
метода из обычного
• Вызываемый метод не исполнится
Узкие места (4) • Точки разрыва не могут быть в блоке finally
• Причина: в этом случае исключение может быть проигнорировано
Альтернативные решения • Apache Javaflow
• DaVinci VM
• Kilim
Сергей Загурский
в е д у щ и й п р о г р а м м и с т, п р о е к т S k y f o r g e
s . z a g u r s k i y @ c o r p . m a i l . r u