exceptions and side-effects in atomic blocks tim harris
Post on 20-Dec-2015
216 views
TRANSCRIPT
Exceptions and side-effectsin atomic blocks
Tim Harris
Atomic blocksvoid put(int val){ atomic (!this.full) { this.full = true; this.val = val; }}
int get(){ int result; atomic (this.full) { this.full = false; return this.val; }}
Basic syntax: ‘atomic (cond) { statements; }’ Block until the condition is true… …then execute the statements (doing
all this as a single atomic step) The statements can access fields &
local variables, invoke methods, instantiate objects etc.
Atomic blocks (ii)General principle in single-threaded
code:
atomic (E) { S } behaves the same as {E;S} after blocking until a state in which E is
known to yield true The compiler and VM are responsible for making this
work well in multi-threaded systems:
first implementation used a software transactional memory
memory accesses in E and S remain thread-private
most non-conflicting operations can commit in parallel
Atomic blocks (iii)
A motivating (and stylized!) example:void serverLoop(ServerSocket s){ while (true) { Socket c = s.acceptConnection(); Thread t = new Thread() { public void run() { atomic { dealWithClient(c); } } }}
Try to deal with non-conflicting client requests concurrently
If something goes wrong (e.g. client triggers DivisionByZero) then try to contain failure
atomic means “all or nothing”
Aborting atomic blocks
What about this code to move an object between collections:boolean move(Collection s, Collection d, Object o)
{
atomic {
if (!s.remove(o)) {
return false;
} else {
if (d.insert(o)) {
return true;
} else { ? }
}
}
}
Option I: put it back again
What if source rejects it too?
Conceptually read-only but underlying transaction is not
if (s.insert(o)) { return false;} else { ???}
Option II: abort the transaction
Should STM be exposed?
Does it prevent non-STM implementations?
STM.abort();return false;
Option III: use exceptions
Semantics don’t involve STM
Make AbError checked => don’t ordinarily need log for roll-back
throw new AbError();
throws AbError
Aborting atomic blocks (ii)
Here still be dragons:void badException() throws AbError {
atomic {
AbError e = new AbError() {
Object payload = new String(“Aborted atomic block“);
};
throw e;
}
}
try { badException(); } catch (AbError exn) { … }
Solution: propagated exception behaves as a deep-copy of the one thrown
“Allocated in …
External side-effects
Original implementation forbade native methods
Some I/O ‘lucky’ and absorbed by buffers
Want a more general solution – e.g. Full range of operations potentially available Controllable semantics, e.g. for debugging
or client/server example Appropriate level for buffering and
conflict detection
External side-effects (ii)
General model: Allow a customized library implementation (or
wrapper) to tell if it’s executed within an atomic block
Allow it to temporarily execute ‘global’ operations, e.g. to perform its own buffering of output associated with the block
Register call-backs to receive notification when the block is committed or aborted
Many similar problems to exception propagation: Code running outside the block mustn’t see
partial updates made within the block
Execution contexts
Execution contexts represent the views that different threads may have on the heap at the same time
Invariant: objects and stack frames can only hold references to objects in ‘more permanent’ contexts
o1
Heap StackExecution outside atomic block
o3 Execution in specialized library
o1 o2 Execution within atomic block
External actionspublic class ExampleAction { static int x = 0;
static VoidExternalAction printX = new VoidExternalAction() { public void action(Context caller_context) { System.out.println (“x=“ + x); }};
static void increment_x() { atomic { x++; printX.doAction(); } printX.doAction(); }}
Combined examplevoid doCombinedUpdate(Connection conn,
int new_value, boolean do_commit) throws …
{ atomic { PreparedStatement pst; pst = conn.prepareStatement (“UPDATE TESTDATA “
“SET field = ? WHERE ID = 1“); pst.setInt(1, newValue); pst.executeUpdate(); shadow = new_value;
if (!do_commit) { throw new AbError (“Not committed“); } }}
Performance evaluation in a real application
Experiment with ‘external object’ abstraction Export and bind to in RPC-like style May make parameter passing mode more intuitive
Avoid problems of writing lots of wrappers
Static analysis for avoiding taking copies Parameters usually seem to be handed off to callee Result usually seems to be handed off to caller Allocate & initialise directly in target context
Future directions