1 // 2 // Copyright (c) ZeroC, Inc. All rights reserved. 3 // 4 5 namespace IceInternal 6 { 7 using System.Collections.Generic; 8 using System.Diagnostics; 9 10 public class RetryTask : TimerTask, CancellationHandler 11 { RetryTask(Instance instance, RetryQueue retryQueue, ProxyOutgoingAsyncBase outAsync)12 public RetryTask(Instance instance, RetryQueue retryQueue, ProxyOutgoingAsyncBase outAsync) 13 { 14 _instance = instance; 15 _retryQueue = retryQueue; 16 _outAsync = outAsync; 17 } 18 runTimerTask()19 public void runTimerTask() 20 { 21 _outAsync.retry(); 22 23 // 24 // NOTE: this must be called last, destroy() blocks until all task 25 // are removed to prevent the client thread pool to be destroyed 26 // (we still need the client thread pool at this point to call 27 // exception callbacks with CommunicatorDestroyedException). 28 // 29 _retryQueue.remove(this); 30 } 31 asyncRequestCanceled(OutgoingAsyncBase outAsync, Ice.LocalException ex)32 public void asyncRequestCanceled(OutgoingAsyncBase outAsync, Ice.LocalException ex) 33 { 34 Debug.Assert(_outAsync == outAsync); 35 if(_retryQueue.cancel(this)) 36 { 37 if(_instance.traceLevels().retry >= 1) 38 { 39 _instance.initializationData().logger.trace(_instance.traceLevels().retryCat, 40 string.Format("operation retry canceled\n{0}", ex)); 41 } 42 if(_outAsync.exception(ex)) 43 { 44 _outAsync.invokeExceptionAsync(); 45 } 46 } 47 } 48 destroy()49 public void destroy() 50 { 51 try 52 { 53 _outAsync.abort(new Ice.CommunicatorDestroyedException()); 54 } 55 catch(Ice.CommunicatorDestroyedException) 56 { 57 // Abort can throw if there's no callback, just ignore in this case 58 } 59 } 60 61 private Instance _instance; 62 private RetryQueue _retryQueue; 63 private ProxyOutgoingAsyncBase _outAsync; 64 } 65 66 public class RetryQueue 67 { RetryQueue(Instance instance)68 public RetryQueue(Instance instance) 69 { 70 _instance = instance; 71 } 72 add(ProxyOutgoingAsyncBase outAsync, int interval)73 public void add(ProxyOutgoingAsyncBase outAsync, int interval) 74 { 75 lock(this) 76 { 77 if(_instance == null) 78 { 79 throw new Ice.CommunicatorDestroyedException(); 80 } 81 RetryTask task = new RetryTask(_instance, this, outAsync); 82 outAsync.cancelable(task); // This will throw if the request is canceled. 83 _instance.timer().schedule(task, interval); 84 _requests.Add(task, null); 85 } 86 } 87 destroy()88 public void destroy() 89 { 90 lock(this) 91 { 92 Dictionary<RetryTask, object> keep = new Dictionary<RetryTask, object>(); 93 foreach(RetryTask task in _requests.Keys) 94 { 95 if(_instance.timer().cancel(task)) 96 { 97 task.destroy(); 98 } 99 else 100 { 101 keep.Add(task, null); 102 } 103 } 104 _requests = keep; 105 _instance = null; 106 while(_requests.Count > 0) 107 { 108 System.Threading.Monitor.Wait(this); 109 } 110 } 111 } 112 remove(RetryTask task)113 public void remove(RetryTask task) 114 { 115 lock(this) 116 { 117 if(_requests.Remove(task)) 118 { 119 if(_instance == null && _requests.Count == 0) 120 { 121 // If we are destroying the queue, destroy is probably waiting on the queue to be empty. 122 System.Threading.Monitor.Pulse(this); 123 } 124 } 125 } 126 } 127 cancel(RetryTask task)128 public bool cancel(RetryTask task) 129 { 130 lock(this) 131 { 132 if(_requests.Remove(task)) 133 { 134 if(_instance == null && _requests.Count == 0) 135 { 136 // If we are destroying the queue, destroy is probably waiting on the queue to be empty. 137 System.Threading.Monitor.Pulse(this); 138 } 139 return _instance.timer().cancel(task); 140 } 141 return false; 142 } 143 } 144 145 private Instance _instance; 146 private Dictionary<RetryTask, object> _requests = new Dictionary<RetryTask, object>(); 147 } 148 } 149