Presentation is loading. Please wait.

Presentation is loading. Please wait.

Subsystems: Improved exception handling for Java (DRAFT) Bart Jacobs, Frank Piessens.

Similar presentations


Presentation on theme: "Subsystems: Improved exception handling for Java (DRAFT) Bart Jacobs, Frank Piessens."— Presentation transcript:

1 Subsystems: Improved exception handling for Java (DRAFT) Bart Jacobs, Frank Piessens

2 Outline Try-Catch: Problem + Solution Locking: Problem + Solution Cancellation: Problem + Solution Cleaning Up: Problem + Solution

3 Outline Try-Catch: Problem + Solution Locking: Problem + Solution Cancellation: Problem + Solution Cleaning Up: Problem + Solution

4 Problem Statement: A Typical Try-Catch Pattern class Widget { … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } void removeWidget(Widget w) { … } } class Program { public static void main(String[] args) { WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } … } This program is broken! Why?

5 Problem Statement: An OutOfMemoryError… class Widget { … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } void removeWidget(Widget w) { … } } class Program { public static void main(String[] args) { WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } … } m.count == 11 m.widgets.length == 10

6 Problem Statement: An OutOfMemoryError… class Widget { … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } void removeWidget(Widget w) { … } } class Program { public static void main(String[] args) { WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } … } 1 2 3 4 m.count == 11 m.widgets.length == 10

7 Problem Statement: An OutOfMemoryError… class Widget { … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } void removeWidget(Widget w) { … } } class Program { public static void main(String[] args) { WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } … } 1 2 3 4 5 m.count == 12 m.widgets.length == 10

8 Problem Statement: A Typical Try-Catch Pattern class Widget { … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } void removeWidget(Widget w) { … } } class Program { public static void main(String[] args) { WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } … }

9 class WidgetManager { Subsystem s = Subsystem.getCurrent(); int count; Widget[] widgets = new Widget[10]; Widget allocWidget() { reenter (s) { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } void removeWidget(Widget w) { … } } Proposed Solution: Re-entering the Owner Subsystem class Program { public static void main(String[] args) { WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } … } root subsyste m child subsyste m

10 class Program { public static void main(String[] args) { WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } … } class WidgetManager { Subsystem s = Subsystem.getCurrent(); int count; Widget[] widgets = new Widget[10]; Widget allocWidget() { reenter (s) { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } void removeWidget(Widget w) { … } } Proposed Solution: Re-entering an Outer Subsystem

11 Subsystems: Basic Operation class Program { public static void main(String[] args) { Subsystem s = Subsystem.getCurrent(); try { reenter (s) { throw new RuntimeException(); } } catch (Throwable t) { System.out.println(t + “ was caught.”); } Not caught

12 Subsystems: Basic Operation class Program { public static void main(String[] args) { try { Subsystem s = Subsystem.getCurrent(); try { reenter (s) { throw new RuntimeException(); } } catch (Throwable t) { System.out.println(t + “ caught by inner.”); } } catch (Throwable t) { System.out.println(t + “ caught by outer.”); } Caught by outer

13 Subsystems: Implementation class Subsystem { static Stack stack = new Stack (); static { stack.push(new Subsystem(); } Throwable exception; Subsystem parent; List children = new ArrayList (); static Subsystem getCurrent() { return stack.peek(); } static void enterNew() { Subsystem s = new Subsystem(); s.parent = getCurrent(); getCurrent().children.add(s); stack.push(s); } static void reenter(Subsystem s) { s.checkNotFailed(); stack.push(s); } static void exit(Throwable e) { if (e != null) getCurrent().setFailed(e); stack.pop(); getCurrent().checkNotFailed(); } void setFailed(Throwable e) { exception = e; for (Subsystem c : children) c.setFailed(e); } void checkNotFailed() { if (exception != null) throw exception; } static void finish(Throwable e) { getCurrent().parent.children.remove(getCurrent()); exit(e); } } Expansion [[ try { S } catch (Throwable e) { S’ } ]] = Subsystem.enterNew(); Throwable t = null; try { S } catch (Throwable e) { t = e; } Subsystem.finish(t); if (t != null) { Throwable e = t; S’ } Expansion [[ reenter (s) { S } ]] = Subsystem.reenter(s); Throwable t = null; try { S } catch (Throwable e) { t = e; } Subsystem.exit(t); if (t != null) { throw t; }

14 Outline Try-Catch: Problem + Solution Locking: Problem + Solution Cancellation: Problem + Solution Cleaning Up: Problem + Solution

15 Problem Statement: A Typical Locking Pattern class Widget { … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } synchronized void removeWidget(Widget w) { … } } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); new Thread() { public void run() { try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … } This program is broken! Why?

16 Problem Statement: An OutOfMemoryError… class Widget { … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } synchronized void removeWidget(Widget w) { … } } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); new Thread() { public void run() { try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … } In thread 1 m.count == 11 m.widgets.length == 10

17 Problem Statement: An OutOfMemoryError… class Widget { … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } synchronized void removeWidget(Widget w) { … } } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); new Thread() { public void run() { try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … } In thread 1 In thread 2 m.count == 12 m.widgets.length == 10

18 Subsystems To The Rescue class WidgetManager { Subsystem s = Subsystem.getCurrent(); int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { reenter (s) { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } … } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); new Thread() { public void run() { try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … }

19 Subsystems To The Rescue class WidgetManager { Subsystem s = Subsystem.getCurrent(); int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { reenter (s) { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } … } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); new Thread() { public void run() { try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … } In thread 1

20 Subsystems To The Rescue class WidgetManager { Subsystem s = Subsystem.getCurrent(); int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { reenter (s) { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } … } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); new Thread() { public void run() { try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … } In thread 1 In thread 2

21 Subsystems To The Rescue class WidgetManager { Subsystem s = Subsystem.getCurrent(); int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { reenter (s) { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } … } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); new Thread() { public void run() { try { … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … } In thread 1 In thread 2 In main thread

22 Subsystems When an exception occurs in a subsystem s For safety: New attempts to enter s (or a descendant) rethrow the exception, and For liveness: Computations executing in s (or a descendant) in other threads are stopped I.e.: In each thread that is executing in s (or a descendant), an exception is thrown (using Thread.stop()), which is caught when leaving s

23 Outline Try-Catch: Problem + Solution Locking: Problem + Solution Cancellation: Problem + Solution Cleaning Up: Problem + Solution

24 Problem Statement: A Typical Cancellation Pattern class Task { boolean cancel; Thread thread; synchronized void cancel() { cancel = true; if (thread != null) thread.stop(); } synchronized void setThread(Thread t) { thread = t; if (cancel) throw new ThreadDeath(); } } class Widget { … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } … } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); List tasks = new ArrayList (); while (true) { String cmd = getUserCommand(); if (cmd.equals(“cancelAll”)) { for (Task t : tasks) t.cancel(); continue; } final Task task = new Task(); tasks.add(task); new Thread() { public void run() { try { task.setThread(Thread.currentThread()); … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … } This program is broken! Why?

25 Problem Statement: A ThreadDeath… class Task { boolean cancel; Thread thread; synchronized void cancel() { cancel = true; if (thread != null) thread.stop(); } synchronized void setThread(Thread t) { thread = t; if (cancel) throw new ThreadDeath(); } } class Widget { … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } … } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); List tasks = new ArrayList (); while (true) { String cmd = getUserCommand(); if (cmd.equals(“cancelAll”)) { for (Task t : tasks) t.cancel(); continue; } final Task task = new Task(); tasks.add(task); new Thread() { public void run() { try { task.setThread(Thread.currentThread()); … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … } ThreadDeat h in thread 1 m.count == 11 m.widgets.length == 10

26 Problem Statement: A ThreadDeath… class Task { boolean cancel; Thread thread; synchronized void cancel() { cancel = true; if (thread != null) thread.stop(); } synchronized void setThread(Thread t) { thread = t; if (cancel) throw new ThreadDeath(); } } class Widget { … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } … } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); List tasks = new ArrayList (); while (true) { String cmd = getUserCommand(); if (cmd.equals(“cancelAll”)) { for (Task t : tasks) t.cancel(); continue; } final Task task = new Task(); tasks.add(task); new Thread() { public void run() { try { task.setThread(Thread.currentThread()); … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … } ThreadDeat h in thread 1 m.count == 12 m.widgets.length == 10 In thread 2

27 Proposed Solution: Subsystem Cancellation class Task { boolean cancel; Subsystem subsystem; synchronized void cancel() { cancel = true; if (subsystem != null) subsystem.cancel(); } synchronized void setSubsystem(Subsystem s) { subsystem = s; if (cancel) throw new ThreadDeath(); } } class Widget { … } class WidgetManager { Subsystem s = Subsystem.getCurrent(); int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { reenter (s) { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } … } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); List tasks = new ArrayList (); while (true) { String cmd = getUserCommand(); if (cmd.equals(“cancelAll”)) { for (Task t : tasks) t.cancel(); continue; } final Task task = new Task(); tasks.add(task); new Thread() { public void run() { try { task.setSubsystem(Subsystem.getCurrent()); … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … }

28 Proposed Solution: Subsystem Cancellation class Task { boolean cancel; Subsystem subsystem; synchronized void cancel() { cancel = true; if (subsystem != null) subsystem.cancel(); } synchronized void setSubsystem(Subsystem s) { subsystem = s; if (cancel) throw new ThreadDeath(); } } class Widget { … } class WidgetManager { Subsystem s = Subsystem.getCurrent(); int count; Widget[] widgets = new Widget[10]; synchronized Widget allocWidget() { reenter (s) { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } … } class Program { public static void main(String[] args) { final WidgetManager m = new WidgetManager(); List tasks = new ArrayList (); while (true) { String cmd = getUserCommand(); if (cmd.equals(“cancelAll”)) { for (Task t : tasks) t.cancel(); continue; } final Task task = new Task(); tasks.add(task); new Thread() { public void run() { try { task.setSubsystem(Subsystem.getCurrent()); … … m.allocWidget() … … … m.removeWidget(…) … … } catch (Throwable t) { showErrorMessage(t); } }.start(); } … } (Child subsystem is cancelled) ThreadDeath on entry to child

29 Outline Try-Catch: Problem + Solution Locking: Problem + Solution Cancellation: Problem + Solution Cleaning Up: Problem + Solution

30 Problem Statement: A Typical Cleanup Pattern class Widget implements Closeable { WidgetManager m; Widget(WidgetManager m) { this.m = m; } public void close() { m.removeWidget(this); } … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } void removeWidget(Widget w) { … } } class Program { static Widget allocGreenWidget(WidgetManager m) { Widget w = m.allocWidget(); w.setColor(Color.green); return w; } public static void main(String[] args) { WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); try { … Widget w = allocGreenWidget(m); try { … } finally { w.close(); } … } catch (Throwable t) { showErrorMessage(t); } … } This program is broken! Why?

31 Problem Statement: A StackOverflowError… class Widget implements Closeable { WidgetManager m; Widget(WidgetManager m) { this.m = m; } public void close() { m.removeWidget(this); } … } class WidgetManager { int count; Widget[] widgets = new Widget[10]; Widget allocWidget() { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } void removeWidget(Widget w) { … } } class Program { static Widget allocGreenWidget(WidgetManager m) { Widget w = m.allocWidget(); w.setColor(Color.green); return w; } public static void main(String[] args) { WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); try { … Widget w = allocGreenWidget(m); try { … } finally { w.close(); } … } catch (Throwable t) { showErrorMessage(t); } … }

32 Proposed Solution: Subsystem Cleanup Routines class Widget implements Closeable { Subsystem client = Subsystem.getCaller(); WidgetManager m; Widget(WidgetManager m) { client.registerCleanup(this); this.m = m; } public void close() { m.removeWidget(this); } … } class WidgetManager { Subsystem s = Subsystem.getCurrent(); int count; Widget[] widgets = new Widget[10]; Widget allocWidget() { reenter (s) { Widget w = new Widget(); count++; if (count == widgets.length + 1) { Widget[] ws = new Widget[count * 2]; System.arraycopy(widgets, 0, ws, 0, widgets.length); widgets = ws; } widgets[count – 1] = w; return w; } void removeWidget(Widget w) { reenter (s) { w.client.unregisterCleanup(w); … } class Program { static Widget allocGreenWidget(WidgetManager m) { Widget w = m.allocWidget(); w.setColor(Color.green); return w; } public static void main(String[] args) { WidgetManager m = new WidgetManager(); while (true) { String cmd = getUserCommand(); try { … Widget w = allocGreenWidget(m); try { … } finally { w.close(); } … } catch (Throwable t) { showErrorMessage(t); } … } class Subsystem { static void finish(Throwable t) { Subsystem s = getCurrent(); … while (!s.cleanupStack.isEmpty()) s.cleanupStack.peek().close(); }

33 Related Work (Under Construction) Re-entering: Similar to performing a remote procedure call in systems where each subsystem is a separate process/thread Cancellation: Rudys, Wallach. Termination in language-based systems. ACM TISS 5(2), 2002. Wick, Flatt. Memory accounting without partitions. ISMM 2004. Flatt, Findler. Kill-safe synchronization abstractions. PLDI 2004. Flatt, Findler, Krishnamurthi, Felleisen. Programming languages as operating systems. ICFP 1999. Subsystem cleanup stacks: Similar to compensation stacks in Weimer. Finding and preventing run-time error handling mistakes. OOPSLA 2004.

34 Conclusion It’s hard in Java to catch unchecked exceptions safely Many (most?) existing try-catch blocks for unchecked exceptions are probably unsafe Many synchronized blocks are probably unsafe There is no easy and safe way to cancel a computation Subsystems make it easy to fix these problems for the example programs Future work: Assess the severity of the problem and the effectiveness of subsystems in large programs


Download ppt "Subsystems: Improved exception handling for Java (DRAFT) Bart Jacobs, Frank Piessens."

Similar presentations


Ads by Google