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