@@ -239,95 +239,113 @@ func RunInTransaction(fn TxFn) (interface{}, error) {
239
239
func (tx * Tx ) Run (fn TxFn ) (interface {}, error ) {
240
240
241
241
done := false
242
- var ret interface {}
243
242
locked := make ([]* Ref , 0 , 10 )
244
243
// notify := make([]*Notify)
245
244
246
245
defer func () {
247
246
for k := len (locked ) - 1 ; k >= 0 ; k -- {
248
247
locked [k ].exitWriteLock ()
249
248
}
249
+
250
+ locked = nil
251
+ for r , _ := range tx .ensures {
252
+ r .exitReadLock ()
253
+ }
254
+ tx .ensures = nil
255
+ if done {
256
+ tx .Stop (txCommitted )
257
+ } else {
258
+ tx .Stop (txRetry )
259
+ }
260
+ if done {
261
+ // do notifies and agent actions, if we every implement
262
+ }
263
+
250
264
}()
251
265
252
- locked = nil
253
- for r , _ := range tx .ensures {
254
- r .exitReadLock ()
255
- }
256
- tx .ensures = nil
257
- if done {
258
- tx .Stop (txCommitted )
259
- } else {
260
- tx .Stop (txRetry )
261
- }
262
- if done {
263
- // do notifies and agent actions, if we every implement
266
+ for i := 0 ; ! done && i < retryLimit ; i ++ {
267
+ ret , err := tx .tryRun (i , fn , & locked )
268
+ if err == nil {
269
+ done = true
270
+ return ret , nil
271
+ }
264
272
}
265
273
266
- for i := 0 ; ! done && i < retryLimit ; i ++ {
274
+ return nil , errors .New ("Transaction failed after reaching retry limit" )
275
+ }
276
+
277
+ // One iteration of the Run loop
278
+ // Split out so that we can catch a retry panic
279
+ func (tx * Tx ) tryRun (i int , fn TxFn , locked * []* Ref ) (ret interface {}, err error ) {
267
280
268
- tx .getReadPoint ()
269
- if i == 0 {
270
- tx .startPoint = tx .readPoint
271
- tx .startTime = time .Now ()
281
+ ret , err = nil , nil
282
+
283
+ defer func () {
284
+ r := recover ()
285
+ if r == retryError {
286
+ ret , err = nil , retryError
287
+ } else if r != nil {
288
+ panic (r )
272
289
}
290
+ }()
273
291
274
- tx .info = newTxInfo (txRunning , tx .startPoint )
275
- ret = fn (tx )
292
+ tx .getReadPoint ()
293
+ if i == 0 {
294
+ tx .startPoint = tx .readPoint
295
+ tx .startTime = time .Now ()
296
+ }
276
297
277
- // make sure no one has killed us before this point, and can't from now on
278
- if atomic .CompareAndSwapUint32 (& tx .info .status , txRunning , txCommitting ) {
279
- for r , calls := range tx .commutes {
280
- if _ , ok := tx .sets [r ]; ok {
281
- continue
282
- }
283
- _ , wasEnsured := tx .ensures [r ]
284
- tx .releaseIfEnsured (r )
285
- tryWriteLock (r )
286
- locked = append (locked , r )
287
- if wasEnsured && r .currValPoint () > tx .readPoint {
288
- panic (retryError )
289
- }
298
+ tx .info = newTxInfo (txRunning , tx .startPoint )
299
+ ret = fn (tx )
290
300
291
- refInfo := r .tinfo
292
- if refInfo != nil && refInfo != tx .info && refInfo .isRunning () {
293
- if ! tx .barge (refInfo ) {
294
- panic (retryError )
295
- }
296
- }
297
- val := r .tryGetVal ()
298
- tx .vals [r ] = val
299
- for _ , call := range calls {
300
- tx .vals [r ] = call .fn (tx .vals [r ], call .args ... )
301
- }
301
+ // make sure no one has killed us before this point, and can't from now on
302
+ if atomic .CompareAndSwapUint32 (& tx .info .status , txRunning , txCommitting ) {
303
+ for r , calls := range tx .commutes {
304
+ if _ , ok := tx .sets [r ]; ok {
305
+ continue
302
306
}
303
-
304
- for r , _ := range tx .sets {
305
- tryWriteLock (r )
306
- locked = append (locked , r )
307
+ _ , wasEnsured := tx .ensures [r ]
308
+ tx .releaseIfEnsured (r )
309
+ tryWriteLock (r )
310
+ * locked = append (* locked , r )
311
+ if wasEnsured && r .currValPoint () > tx .readPoint {
312
+ panic (retryError )
307
313
}
308
314
309
- // if we do validations for refs, it goes here
310
-
311
- // at this point,
312
- // all values are calculated,
313
- // all refs to be written are locked
314
- // no more client code to be called
315
- commitPoint := getCommitPoint ()
316
- for r , newV := range tx .vals {
317
- //oldV := r.tryGetVal()
318
- r .setValue (newV , commitPoint )
319
- // todo: call notifies
315
+ refInfo := r .tinfo
316
+ if refInfo != nil && refInfo != tx .info && refInfo .isRunning () {
317
+ if ! tx .barge (refInfo ) {
318
+ panic (retryError )
319
+ }
320
+ }
321
+ val := r .tryGetVal ()
322
+ tx .vals [r ] = val
323
+ for _ , call := range calls {
324
+ tx .vals [r ] = call .fn (tx .vals [r ], call .args ... )
320
325
}
321
- done = true
322
- atomic .StoreUint32 (& tx .info .status , txCommitted )
323
326
}
324
327
325
- if done {
326
- return ret , nil
328
+ for r , _ := range tx .sets {
329
+ tryWriteLock (r )
330
+ * locked = append (* locked , r )
327
331
}
328
332
333
+ // if we do validations for refs, it goes here
334
+
335
+ // at this point,
336
+ // all values are calculated,
337
+ // all refs to be written are locked
338
+ // no more client code to be called
339
+ commitPoint := getCommitPoint ()
340
+ for r , newV := range tx .vals {
341
+ //oldV := r.tryGetVal()
342
+ r .setValue (newV , commitPoint )
343
+ // todo: call notifies
344
+ }
345
+ atomic .StoreUint32 (& tx .info .status , txCommitted )
329
346
}
330
- return nil , errors .New ("Transaction failed after reaching retry limit" )
347
+
348
+ return
331
349
}
332
350
333
351
// Get the value of a Ref (most recently sent in this transaction or value prior to entering)
0 commit comments