Zagursky

32

Transcript of Zagursky

Page 1: Zagursky
Page 2: Zagursky

Файберы

Невытесняющая многозадачность на Java

Page 3: Zagursky

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(); }

Page 4: Zagursky
Page 5: Zagursky

Причины • Хочется писать простой линейный Java-код

• ОС-потоки слишком накладны

• Требуется сохранить однопоточный дизайн

Page 6: Zagursky

С высоты птичьего полёта

Инструментатор Планировщик

Инспекции Рантайм

Page 7: Zagursky

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);

}

Page 8: Zagursky

Синтаксис (2) class Foo {

void bar(FiberManager f) {

f.scheduleFiber(doWork("Hi!"));

}

}

Page 9: Zagursky

Препарируемый метод

Async<String> baz(String second) {

String first = call(getFirst());

return result(join(first, second));

}

Page 10: Zagursky

Замена исходного метода

Async<String> baz(String second) {

return new baz_Context(second);

}

Page 11: Zagursky

Контекст вызова метода

class baz_Context extends Async<String> {

String _second, first;

@Override

void update() { state = baz_Async(this); }

}

Page 12: Zagursky

Инструментированный метод 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;

}

}

Page 13: Zagursky

Инструментирование (1) • Поиск точек разрыва

– call

– yield

• Вставка switch-case и return

Page 14: Zagursky

Инструментирование (2) • Как обойти вызов

блока finally?

• Не вызывать его!

Async<Integer> foo() {

try {

int t = call(bar());

return result(t);

} finally { close(); }

}

Page 15: Zagursky

Инструментирование (3) • Подмена доступа к параметрам метода и

локальным переменным

• int x; x = 42; context.x = 42;

Page 16: Zagursky

Инструментирование (4)

... x = call(bar()); ...

... case 42 Context r = new bar_Context(); c.pushCall(r); return 43; case 43: c.x = (String) c.takeResult(); ...

Page 17: Zagursky

Инструментирование (5) • Директивы, ссылающиеся на исходный файл,

вставляются инструментатором в сгенерированный метод

• Это позволяет использовать пошаговую отладку

• Ограничение: нельзя с помощью Step Over перейти через точку разрыва

Page 18: Zagursky

Исключения • Рантайм ловит все исключения, вылетевшие из

Async-метода

• Исключение заворачивается в служебный объект

• При запросе результата вызывающим Async-методом исключение выбрасывается заново

• Если исключение вылетает за пределы файбера, то это диагностируется как ошибка

Page 19: Zagursky

Прерывание файбера • В каждой точке входа в Async-метод

проверяется, не был ли прерван текущий файбер

• Если файбер был прерван, то выбрасывается служебное исключение FiberInterruptedException

Page 20: Zagursky

Функции FiberManager • Планировщик

• Обрабатывает приостановку, возобновление и прерывание файберов

• У всех файберов одинаковый приоритет

Page 21: Zagursky

Гарантии FiberManager • Файберы должны исполняться в порядке

добавления и возобновления

• Приостановленные файберы не должны исполняться

Page 22: Zagursky

Thread safety • Методы scheduleFiber() и resumeFiber()

могут выполняться из любого потока

• Все остальные методы – только из потока, к которому привязан планировщик

• На каждый поток есть свой планировщик

Page 23: Zagursky

StackTrace (1) • Stack trace из new Throwable().getStackTrace() не

достаточен • Программист хочет видеть:

– стек Async-методов – стек в момент порождения файбера

• Файбер может быть создан внутри другого файбера • Stack trace должен разрешаться (resolve) только

тогда, когда он реально необходим

Page 24: Zagursky

StackTrace (2) • Рантайм получает уже готовое исключение

• К этому исключению нужно добавить

– стек файбера

– стек момента порождения файбера

• Используется addSuppressed()

Page 25: Zagursky

StackTrace (3) • Переопределяется метод fillInStackTrace(),

чтобы не собирать обычный stack trace

• Массив StackTraceElement[] формируется только по запросу getCause() и при сериализации

Page 26: Zagursky

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)

Page 27: Zagursky

Узкие места (1) • Инструментатор меняет сигнатуру метода • Служебные методы call() и result() вырезаются,

чтобы байткод соответствовал новой сигнатуре • Об этом не догадывается IntelliJ Idea, которая

вставляет проверки на != null • Инструментатор вырезает такие проверки из

Async-методов

Page 28: Zagursky

Узкие места (2) • Если Async-метод декларирует throws

SomeUncheckedException – синтаксис java потребует его обработки

– catch-блок для этого исключения никогда не будет вызван

– исключение пролетит в рантайм, т. к. для него нет соответствующего catch

Page 29: Zagursky

Узкие места (3) • Синтаксис не запрещает вызов Async-

метода из обычного

• Вызываемый метод не исполнится

Page 30: Zagursky

Узкие места (4) • Точки разрыва не могут быть в блоке finally

• Причина: в этом случае исключение может быть проигнорировано

Page 31: Zagursky

Альтернативные решения • Apache Javaflow

• DaVinci VM

• Kilim

Page 32: Zagursky

Сергей Загурский

в е д у щ и й п р о г р а м м и с т, п р о е к т 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