2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / corlib / System.Runtime.Remoting.Contexts / SynchronizationAttribute.cs
1 //
2 // System.Runtime.Remoting.Contexts.SynchronizationAttribute.cs
3 //
4 // Author:
5 //   Lluis Sanchez Gual (lluis@ximian.com)
6 //
7 // (C) Novell, Inc.  http://www.ximian.com
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System.Threading;
34 using System.Runtime.Remoting.Messaging;
35 using System.Runtime.Remoting.Activation;
36
37 namespace System.Runtime.Remoting.Contexts
38 {
39         [AttributeUsage(AttributeTargets.Class)]
40         [Serializable]
41         [MonoTODO ("Fix serialization compatibility with MS.NET")]
42         public class SynchronizationAttribute: ContextAttribute, IContributeClientContextSink, IContributeServerContextSink
43         {
44                 public const int NOT_SUPPORTED = 1;
45                 public const int SUPPORTED = 2;
46                 public const int REQUIRED = 4;
47                 public const int REQUIRES_NEW = 8;
48                 
49                 bool _isReentrant;
50                 bool _locked;
51                 int _flag;
52                 int _lockCount = 0;
53                 
54                 Mutex _mutex = new Mutex (false);
55                 Thread _ownerThread;
56                 
57                 public SynchronizationAttribute ()
58                 : this (REQUIRES_NEW, false)
59                 {
60                 }
61                 
62                 public SynchronizationAttribute (bool reEntrant)
63                 : this (REQUIRES_NEW, reEntrant)
64                 {
65                 }
66                 
67                 public SynchronizationAttribute (int flag)
68                 : this (flag, false)
69                 {
70                 }
71                 
72                 public SynchronizationAttribute (int flag, bool reEntrant)
73                 : base ("Synchronization")
74                 {
75                         if (flag != NOT_SUPPORTED && flag != REQUIRED && flag != REQUIRES_NEW && flag != SUPPORTED)
76                                 throw new ArgumentException ("flag");
77                                 
78                         _isReentrant = reEntrant;
79                         _flag = flag;
80                 }
81                 
82                 public virtual bool IsReEntrant
83                 {
84                         get { return _isReentrant; }
85                 }
86                 
87                 public virtual bool Locked
88                 {
89                         get 
90                         { 
91                                 return _locked; 
92                         }
93                         
94                         set 
95                         {
96                                 if (value)
97                                 {
98                                         _mutex.WaitOne ();
99                                         lock (this)
100                                         {
101                                                 _lockCount++;
102                                                 if (_lockCount > 1)
103                                                         ReleaseLock (); // Thread already had the lock
104                                                         
105                                                 _ownerThread = Thread.CurrentThread;
106                                         }
107                                 }
108                                 else
109                                 {
110                                         lock (this)
111                                         {
112                                                 while (_lockCount > 0 && _ownerThread == Thread.CurrentThread)
113                                                 {
114                                                         _lockCount--;
115                                                         _mutex.ReleaseMutex ();
116                                                         _ownerThread = null;
117                                                 }
118                                         }
119                                 }
120                         }
121                 }
122                 
123                 internal void AcquireLock ()
124                 {
125                         _mutex.WaitOne ();
126                         
127                         lock (this)
128                         {
129                                 _ownerThread = Thread.CurrentThread;
130                                 _lockCount++;
131                         }
132                 }
133                 
134                 internal void ReleaseLock ()
135                 {
136                         lock (this)
137                         {
138                                 if (_lockCount > 0 && _ownerThread == Thread.CurrentThread) {
139                                         _lockCount--;
140                                         _mutex.ReleaseMutex ();
141                                         _ownerThread = null;
142                                 }
143                         }
144                 }
145                 
146                 public override void GetPropertiesForNewContext (IConstructionCallMessage ctorMsg)
147                 {
148                         if (_flag != NOT_SUPPORTED) {
149                                 ctorMsg.ContextProperties.Add (this);
150                         }
151                 }
152                 
153                 public virtual IMessageSink GetClientContextSink (IMessageSink nextSink)
154                 {
155                         return new SynchronizedClientContextSink (nextSink, this);
156                 }
157                 
158                 public virtual IMessageSink GetServerContextSink (IMessageSink nextSink)
159                 {
160                         return new SynchronizedServerContextSink (nextSink, this);
161                 }
162                 
163                 public override bool IsContextOK (Context ctx, IConstructionCallMessage msg)
164                 {
165                         SynchronizationAttribute prop = ctx.GetProperty ("Synchronization") as SynchronizationAttribute;
166                         switch (_flag)
167                         {
168                                 case NOT_SUPPORTED: return (prop == null);
169                                 case REQUIRED: return (prop != null);
170                                 case REQUIRES_NEW: return false;
171                                 case SUPPORTED: return true;
172                         }
173                         return false;
174                 }
175                 
176                 internal static void ExitContext ()
177                 {
178                         if (Thread.CurrentContext.IsDefaultContext) return;
179                         SynchronizationAttribute prop = Thread.CurrentContext.GetProperty ("Synchronization") as SynchronizationAttribute;
180                         if (prop == null) return;
181                         prop.Locked = false;
182                 }
183                 
184                 internal static void EnterContext ()
185                 {
186                         if (Thread.CurrentContext.IsDefaultContext) return;
187                         SynchronizationAttribute prop = Thread.CurrentContext.GetProperty ("Synchronization") as SynchronizationAttribute;
188                         if (prop == null) return;
189                         prop.Locked = true;
190                 }
191         }
192         
193         internal class SynchronizedClientContextSink: IMessageSink
194         {
195                 IMessageSink _next;
196                 SynchronizationAttribute _att;
197                 
198                 public SynchronizedClientContextSink (IMessageSink next, SynchronizationAttribute att)
199                 {
200                         _att = att;
201                         _next = next;
202                 }
203                 
204                 public IMessageSink NextSink 
205                 {
206                         get { return _next; }
207                 }
208                 
209                 public IMessageCtrl AsyncProcessMessage (IMessage msg, IMessageSink replySink)
210                 {
211                         if (_att.IsReEntrant)
212                         {
213                                 _att.ReleaseLock();     // Unlock when leaving the context
214                                 replySink = new SynchronizedContextReplySink (replySink, _att, true);
215                         }
216                         return _next.AsyncProcessMessage (msg, replySink);
217                 }
218
219                 public IMessage SyncProcessMessage (IMessage msg)
220                 {
221                         if (_att.IsReEntrant) 
222                                 _att.ReleaseLock ();    // Unlock when leaving the context
223                         
224                         try
225                         {
226                                 return _next.SyncProcessMessage (msg);
227                         }
228                         finally
229                         {
230                                 if (_att.IsReEntrant)
231                                         _att.AcquireLock ();
232                         }
233                 }
234         }
235         
236         internal class SynchronizedServerContextSink: IMessageSink
237         {
238                 IMessageSink _next;
239                 SynchronizationAttribute _att;
240                 
241                 public SynchronizedServerContextSink (IMessageSink next, SynchronizationAttribute att)
242                 {
243                         _att = att;
244                         _next = next;
245                 }
246                 
247                 public IMessageSink NextSink 
248                 {
249                         get { return _next; }
250                 }
251                 
252                 public IMessageCtrl AsyncProcessMessage (IMessage msg, IMessageSink replySink)
253                 {
254                         _att.AcquireLock ();
255                         replySink = new SynchronizedContextReplySink (replySink, _att, false);
256                         return _next.AsyncProcessMessage (msg, replySink);
257                 }
258
259                 public IMessage SyncProcessMessage (IMessage msg)
260                 {
261                         _att.AcquireLock ();
262                         try
263                         {
264                                 return _next.SyncProcessMessage (msg);
265                         }
266                         finally
267                         {
268                                 _att.ReleaseLock ();
269                         }
270                 }
271         }
272         
273         internal class SynchronizedContextReplySink: IMessageSink
274         {
275                 IMessageSink _next;
276                 bool _newLock;
277                 SynchronizationAttribute _att;
278         
279                 public SynchronizedContextReplySink (IMessageSink next, SynchronizationAttribute att, bool newLock)
280                 {
281                         _newLock = newLock;
282                         _next = next;
283                         _att = att;
284                 }
285                 
286                 public IMessageSink NextSink 
287                 {
288                         get { return _next; }
289                 }
290                 
291                 public IMessageCtrl AsyncProcessMessage (IMessage msg, IMessageSink replySink)
292                 {
293                         // Never called
294                         throw new NotSupportedException ();
295                 }
296
297                 public IMessage SyncProcessMessage (IMessage msg)
298                 {
299                         if (_newLock) _att.AcquireLock ();
300                         else _att.ReleaseLock ();
301
302                         try
303                         {
304                                 return _next.SyncProcessMessage (msg);
305                         }
306                         finally
307                         {
308                                 if (_newLock)
309                                         _att.ReleaseLock ();
310                         }
311                 }
312         }
313 }
314