2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / corlib / System / Lazy.cs
1 //
2 // Lazy.cs
3 //
4 // Authors:
5 //  Zoltan Varga (vargaz@gmail.com)
6 //  Marek Safar (marek.safar@gmail.com)
7 //
8 // Copyright (C) 2009 Novell
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 #if NET_4_0
31
32 using System;
33 using System.Runtime.Serialization;
34 using System.Runtime.InteropServices;
35 using System.Security.Permissions;
36 using System.Threading;
37 using System.Diagnostics;
38
39 namespace System
40 {
41         [SerializableAttribute]
42         [ComVisibleAttribute(false)]
43         [HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
44         public class Lazy<T> 
45         {
46                 T value;
47                 bool inited;
48                 LazyThreadSafetyMode mode;
49                 Func<T> factory;
50                 object monitor;
51                 Exception exception;
52
53                 public Lazy ()
54                         : this (LazyThreadSafetyMode.ExecutionAndPublication)
55                 {
56                 }
57
58                 public Lazy (Func<T> valueFactory)
59                         : this (valueFactory, LazyThreadSafetyMode.ExecutionAndPublication)
60                 {
61                 }
62
63                 public Lazy (bool isThreadSafe)
64                         : this (() => Activator.CreateInstance<T> (), isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)
65                 {
66                 }
67                 
68                 public Lazy (Func<T> valueFactory, bool isThreadSafe)
69                         : this (valueFactory, isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)
70                 {
71                 }
72
73                 public Lazy (LazyThreadSafetyMode mode)
74                         : this (() => Activator.CreateInstance<T> (), mode)
75                 {
76                 }
77
78                 
79
80                 public Lazy (Func<T> valueFactory, LazyThreadSafetyMode mode)
81                 {
82                         if (valueFactory == null)
83                                 throw new ArgumentNullException ("valueFactory");
84                         this.factory = valueFactory;
85                         if (mode != LazyThreadSafetyMode.None)
86                                 monitor = new object ();
87                         this.mode = mode;
88                 }
89
90                 // Don't trigger expensive initialization
91                 [DebuggerBrowsable (DebuggerBrowsableState.Never)]
92                 public T Value {
93                         get {
94                                 if (inited)
95                                         return value;
96                                 if (exception != null)
97                                         throw exception;
98
99                                 return InitValue ();
100                         }
101                 }
102
103                 T InitValue () {
104                         switch (mode) {
105                         case LazyThreadSafetyMode.None: {
106                                 var init_factory = factory;
107                                 if (init_factory == null) 
108                                         throw exception = new InvalidOperationException ("The initialization function tries to access Value on this instance");
109                                 try {
110                                         factory = null;
111                                         T v = init_factory ();
112                                         value = v;
113                                         Thread.MemoryBarrier ();
114                                         inited = true;
115                                 } catch (Exception ex) {
116                                         exception = ex;
117                                         throw;
118                                 }
119                                 break;
120                         } 
121                         case LazyThreadSafetyMode.PublicationOnly: {
122                                 var init_factory = factory;
123                                 T v;
124
125                                 //exceptions are ignored
126                                 if (init_factory != null)
127                                         v = init_factory ();
128                                 else
129                                         v = default (T);
130
131                                 lock (monitor) {
132                                         if (inited)
133                                                 return value;
134                                         value = v;
135                                         Thread.MemoryBarrier ();
136                                         inited = true;
137                                         factory = null;
138                                 }
139                                 break;
140                         }
141                         case LazyThreadSafetyMode.ExecutionAndPublication: {
142                                 lock (monitor) {
143                                         if (inited)
144                                                 return value;
145
146                                         if (factory == null)
147                                                 throw exception = new InvalidOperationException ("The initialization function tries to access Value on this instance");
148
149                                         var init_factory = factory;
150                                         try {
151                                                 factory = null;
152                                                 T v = init_factory ();
153                                                 value = v;
154                                                 Thread.MemoryBarrier ();
155                                                 inited = true;
156                                         } catch (Exception ex) {
157                                                 exception = ex;
158                                                 throw;
159                                         }
160                                 }
161                                 break;
162                         }
163                         default:
164                                 throw new InvalidOperationException ("Invalid LazyThreadSafetyMode " + mode);
165                         }
166
167                         if (monitor == null) {
168                                 value = factory ();
169                                 inited = true;
170                         } else {
171                                 lock (monitor) {
172                                         if (inited)
173                                                 return value;
174
175                                         if (factory == null)
176                                                 throw new InvalidOperationException ("The initialization function tries to access Value on this instance");
177
178                                         var init_factory = factory;
179                                         try {
180                                                 factory = null;
181                                                 T v = init_factory ();
182                                                 value = v;
183                                                 Thread.MemoryBarrier ();
184                                                 inited = true;
185                                         } catch {
186                                                 factory = init_factory;
187                                                 throw;
188                                         }
189                                 }
190                         }
191
192                         return value;
193                 }
194
195                 public bool IsValueCreated {
196                         get {
197                                 return inited;
198                         }
199                 }
200
201                 public override string ToString ()
202                 {
203                         if (inited)
204                                 return value.ToString ();
205                         else
206                                 return "Value is not created";
207                 }
208         }               
209 }
210         
211 #endif