Moving BSTR conv to native code in SecureStringToBSTR.
[mono.git] / mcs / class / referencesource / System / InternalApis / NDP_Common / inc / PinnableBufferCache.cs
1 #define ENABLE
2 #define MINBUFFERS
3 using System;
4 #if !FEATURE_CORECLR
5 using System.Diagnostics.Tracing;
6 #endif
7 using System.Runtime.InteropServices;
8 using System.Runtime.ConstrainedExecution;
9 using System.Collections.Generic;
10 using System.Collections.Concurrent;
11 using System.Threading;
12 using System.Runtime.CompilerServices;
13 using System.Diagnostics;
14 using System.Security.Permissions;
15
16 #if PINNABLEBUFFERCACHE_MSCORLIB
17 namespace System.Threading
18 #else
19 namespace System
20 #endif
21 {
22     internal sealed class PinnableBufferCache
23     {
24         /// <summary>
25         /// Create a new cache for pinned byte[] buffers
26         /// </summary>
27         /// <param name="cacheName">A name used in diagnostic messages</param>
28         /// <param name="numberOfElements">The size of byte[] buffers in the cache (they are all the same size)</param>
29         public PinnableBufferCache(string cacheName, int numberOfElements) : this(cacheName, () => new byte[numberOfElements]) { }
30
31         /// <summary>
32         /// Get a buffer from the buffer manager.  If no buffers exist, allocate a new one.
33         /// </summary>
34         public byte[] AllocateBuffer() { return (byte[])Allocate(); }
35
36         /// <summary>
37         /// Return a buffer back to the buffer manager.
38         /// </summary>
39         public void FreeBuffer(byte[] buffer) { Free(buffer); }
40
41         /// <summary>
42         /// Create a PinnableBufferCache that works on any object (it is intended for OverlappedData)
43         /// This is only used in mscorlib.
44         /// </summary>
45 #if (ENABLE || MINBUFFERS)
46         [EnvironmentPermission(SecurityAction.Assert, Unrestricted = true)]
47         [System.Security.SecuritySafeCritical]
48 #endif
49         internal PinnableBufferCache(string cacheName, Func<object> factory)
50         {
51             m_NotGen2 = new List<object>(DefaultNumberOfBuffers);
52             m_factory = factory;
53 #if ENABLE
54             // Check to see if we should disable the cache.
55             string envVarName = "PinnableBufferCache_" + cacheName + "_Disabled";
56             try
57             {
58                 string envVar = Environment.GetEnvironmentVariable(envVarName);
59                 if (envVar != null)
60                 {
61                     PinnableBufferCacheEventSource.Log.DebugMessage("Creating " + cacheName + " PinnableBufferCacheDisabled=" + envVar);
62                     int index = envVar.IndexOf(cacheName, StringComparison.OrdinalIgnoreCase);
63                     if (0 <= index)
64                     {
65                         // The cache is disabled because we haven't set the cache name.
66                         PinnableBufferCacheEventSource.Log.DebugMessage("Disabling " + cacheName);
67                         return;
68                     }
69                 }
70             }
71             catch
72             {
73                 // Ignore failures when reading the environment variable.
74             }
75 #endif
76 #if MINBUFFERS
77             // Allow the environment to specify a minimum buffer count.
78             string minEnvVarName = "PinnableBufferCache_" + cacheName + "_MinCount";
79             try
80             {
81                 string minEnvVar = Environment.GetEnvironmentVariable(minEnvVarName);
82                 if (minEnvVar != null)
83                 {
84                     if (int.TryParse(minEnvVar, out m_minBufferCount))
85                         CreateNewBuffers();
86                 }
87             }
88             catch
89             {
90                 // Ignore failures when reading the environment variable.
91             }
92 #endif
93
94             PinnableBufferCacheEventSource.Log.Create(cacheName);
95             m_CacheName = cacheName;
96         }
97
98         /// <summary>
99         /// Get a object from the buffer manager.  If no buffers exist, allocate a new one.
100         /// </summary>
101         [System.Security.SecuritySafeCritical]
102         internal object Allocate()
103         {
104 #if ENABLE
105             // Check to see whether or not the cache is disabled.
106             if (m_CacheName == null)
107                 return m_factory();
108 #endif
109             // Fast path, get it from our Gen2 aged m_FreeList.  
110             object returnBuffer;
111             if (!m_FreeList.TryPop(out returnBuffer))
112                 Restock(out returnBuffer);
113
114             // Computing free count is expensive enough that we don't want to compute it unless logging is on.
115             if (PinnableBufferCacheEventSource.Log.IsEnabled())
116             {
117                 int numAllocCalls = Interlocked.Increment(ref m_numAllocCalls);
118                 if (numAllocCalls >= 1024)
119                 {
120                     lock (this)
121                     {
122                         int previousNumAllocCalls = Interlocked.Exchange(ref m_numAllocCalls, 0);
123                         if (previousNumAllocCalls >= 1024)
124                         {
125                             int nonGen2Count = 0;
126                             foreach (object o in m_FreeList)
127                             {
128                                 if (GC.GetGeneration(o) < GC.MaxGeneration)
129                                 {
130                                     nonGen2Count++;
131                                 }
132                             }
133
134                             PinnableBufferCacheEventSource.Log.WalkFreeListResult(m_CacheName, m_FreeList.Count, nonGen2Count);
135                         }
136                     }
137                 }
138
139                 PinnableBufferCacheEventSource.Log.AllocateBuffer(m_CacheName, PinnableBufferCacheEventSource.AddressOf(returnBuffer), returnBuffer.GetHashCode(), GC.GetGeneration(returnBuffer), m_FreeList.Count);
140             }
141             return returnBuffer;
142         }
143
144         /// <summary>
145         /// Return a buffer back to the buffer manager.
146         /// </summary>
147         [System.Security.SecuritySafeCritical]
148         internal void Free(object buffer)
149         {
150 #if ENABLE
151             // Check to see whether or not the cache is disabled.
152             if (m_CacheName == null)
153                 return;
154 #endif
155             if (PinnableBufferCacheEventSource.Log.IsEnabled())
156                 PinnableBufferCacheEventSource.Log.FreeBuffer(m_CacheName, PinnableBufferCacheEventSource.AddressOf(buffer), buffer.GetHashCode(), m_FreeList.Count);
157
158
159             // After we've done 3 gen1 GCs, assume that all buffers have aged into gen2 on the free path.
160             if ((m_gen1CountAtLastRestock + 3) > GC.CollectionCount(GC.MaxGeneration - 1))
161             {
162                 lock (this)
163                 {
164                     if (GC.GetGeneration(buffer) < GC.MaxGeneration)
165                     {
166                         // The buffer is not aged, so put it in the non-aged free list.
167                         m_moreThanFreeListNeeded = true;
168                         PinnableBufferCacheEventSource.Log.FreeBufferStillTooYoung(m_CacheName, m_NotGen2.Count);
169                         m_NotGen2.Add(buffer);
170                         m_gen1CountAtLastRestock = GC.CollectionCount(GC.MaxGeneration - 1);
171                         return;
172                     }
173                 }
174             }
175
176             // If we discovered that it is indeed Gen2, great, put it in the Gen2 list.  
177             m_FreeList.Push(buffer);
178         }
179
180         #region Private
181
182         /// <summary>
183         /// Called when we don't have any buffers in our free list to give out.    
184         /// </summary>
185         /// <returns></returns>
186         [System.Security.SecuritySafeCritical]
187         private void Restock(out object returnBuffer)
188         {
189             lock (this)
190             {
191                 // Try again after getting the lock as another thread could have just filled the free list.  If we don't check
192                 // then we unnecessarily grab a new set of buffers because we think we are out.     
193                 if (m_FreeList.TryPop(out returnBuffer))
194                     return;
195
196                 // Lazy init, Ask that TrimFreeListIfNeeded be called on every Gen 2 GC.  
197                 if (m_restockSize == 0)
198                     Gen2GcCallback.Register(Gen2GcCallbackFunc, this);
199
200                 // Indicate to the trimming policy that the free list is insufficent.   
201                 m_moreThanFreeListNeeded = true;
202                 PinnableBufferCacheEventSource.Log.AllocateBufferFreeListEmpty(m_CacheName, m_NotGen2.Count);
203
204                 // Get more buffers if needed.
205                 if (m_NotGen2.Count == 0)
206                     CreateNewBuffers();
207
208                 // We have no buffers in the aged freelist, so get one from the newer list.   Try to pick the best one.
209                 // Debug.Assert(m_NotGen2.Count != 0);
210                 int idx = m_NotGen2.Count - 1;
211                 if (GC.GetGeneration(m_NotGen2[idx]) < GC.MaxGeneration && GC.GetGeneration(m_NotGen2[0]) == GC.MaxGeneration)
212                     idx = 0;
213                 returnBuffer = m_NotGen2[idx];
214                 m_NotGen2.RemoveAt(idx);
215
216                 // Remember any sub-optimial buffer so we don't put it on the free list when it gets freed.   
217                 if (PinnableBufferCacheEventSource.Log.IsEnabled() && GC.GetGeneration(returnBuffer) < GC.MaxGeneration)
218                 {
219                     PinnableBufferCacheEventSource.Log.AllocateBufferFromNotGen2(m_CacheName, m_NotGen2.Count);
220                 }
221
222                 // If we have a Gen1 collection, then everything on m_NotGen2 should have aged.  Move them to the m_Free list.  
223                 if (!AgePendingBuffers())
224                 {
225                     // Before we could age at set of buffers, we have handed out half of them.
226                     // This implies we should be proactive about allocating more (since we will trim them if we over-allocate).  
227                     if (m_NotGen2.Count == m_restockSize / 2)
228                     {
229                         PinnableBufferCacheEventSource.Log.DebugMessage("Proactively adding more buffers to aging pool");
230                         CreateNewBuffers();
231                     }
232                 }
233             }
234         }
235
236         /// <summary>
237         /// See if we can promote the buffers to the free list.  Returns true if sucessful. 
238         /// </summary>
239         [System.Security.SecuritySafeCritical]
240         private bool AgePendingBuffers()
241         {
242             if (m_gen1CountAtLastRestock < GC.CollectionCount(GC.MaxGeneration - 1))
243             {
244                 // Allocate a temp list of buffers that are not actually in gen2, and swap it in once
245                 // we're done scanning all buffers.
246                 int promotedCount = 0;
247                 List<object> notInGen2 = new List<object>();
248                 PinnableBufferCacheEventSource.Log.AllocateBufferAged(m_CacheName, m_NotGen2.Count);
249                 for (int i = 0; i < m_NotGen2.Count; i++)
250                 {
251                     // We actually check every object to ensure that we aren't putting non-aged buffers into the free list.
252                     object currentBuffer = m_NotGen2[i];
253                     if (GC.GetGeneration(currentBuffer) >= GC.MaxGeneration)
254                     {
255                         m_FreeList.Push(currentBuffer);
256                         promotedCount++;
257                     }
258                     else
259                     {
260                         notInGen2.Add(currentBuffer);
261                     }
262                 }
263                 PinnableBufferCacheEventSource.Log.AgePendingBuffersResults(m_CacheName, promotedCount, notInGen2.Count);
264                 m_NotGen2 = notInGen2;
265
266                 return true;
267             }
268             return false;
269         }
270
271         /// <summary>
272         /// Generates some buffers to age into Gen2.
273         /// </summary>
274         private void CreateNewBuffers()
275         {
276             // We choose a very modest number of buffers initially because for the client case.  This is often enough.
277             if (m_restockSize == 0)
278                 m_restockSize = 4;
279             else if (m_restockSize < DefaultNumberOfBuffers)
280                 m_restockSize = DefaultNumberOfBuffers;
281             else if (m_restockSize < 256)
282                 m_restockSize = m_restockSize * 2;                // Grow quickly at small sizes
283             else if (m_restockSize < 4096)
284                 m_restockSize = m_restockSize * 3 / 2;            // Less agressively at large ones
285             else
286                 m_restockSize = 4096;                             // Cap how agressive we are
287
288             // Ensure we hit our minimums
289             if (m_minBufferCount > m_buffersUnderManagement)
290                 m_restockSize = Math.Max(m_restockSize, m_minBufferCount - m_buffersUnderManagement);
291
292             PinnableBufferCacheEventSource.Log.AllocateBufferCreatingNewBuffers(m_CacheName, m_buffersUnderManagement, m_restockSize);
293             for (int i = 0; i < m_restockSize; i++)
294             {
295                 // Make a new buffer.
296                 object newBuffer = m_factory();
297
298                 // Create space between the objects.  We do this because otherwise it forms a single plug (group of objects)
299                 // and the GC pins the entire plug making them NOT move to Gen1 and Gen2.   by putting space between them
300                 // we ensure that object get a chance to move independently (even if some are pinned).  
301                 var dummyObject = new object();
302                 m_NotGen2.Add(newBuffer);
303             }
304             m_buffersUnderManagement += m_restockSize;
305             m_gen1CountAtLastRestock = GC.CollectionCount(GC.MaxGeneration - 1);
306         }
307
308         /// <summary>
309         /// This is the static function that is called from the gen2 GC callback.
310         /// The input object is the cache itself.
311         /// NOTE: The reason that we make this functionstatic and take the cache as a parameter is that
312         /// otherwise, we root the cache to the Gen2GcCallback object, and leak the cache even when
313         /// the application no longer needs it.
314         /// </summary>
315         [System.Security.SecuritySafeCritical]
316         private static bool Gen2GcCallbackFunc(object targetObj)
317         {
318             return ((PinnableBufferCache)(targetObj)).TrimFreeListIfNeeded();
319         }
320
321         /// <summary>
322         /// This is called on every gen2 GC to see if we need to trim the free list.
323         /// NOTE: DO NOT CALL THIS DIRECTLY FROM THE GEN2GCCALLBACK.  INSTEAD CALL IT VIA A STATIC FUNCTION (SEE ABOVE).
324         /// If you register a non-static function as a callback, then this object will be leaked.
325         /// </summary>
326         [System.Security.SecuritySafeCritical]
327         private bool TrimFreeListIfNeeded()
328         {
329             int curMSec = Environment.TickCount;
330             int deltaMSec = curMSec - m_msecNoUseBeyondFreeListSinceThisTime;
331             PinnableBufferCacheEventSource.Log.TrimCheck(m_CacheName, m_buffersUnderManagement, m_moreThanFreeListNeeded, deltaMSec);
332
333             // If we needed more than just the set of aged buffers since the last time we were called,
334             // we obviously should not be trimming any memory, so do nothing except reset the flag 
335             if (m_moreThanFreeListNeeded)
336             {
337                 m_moreThanFreeListNeeded = false;
338                 m_trimmingExperimentInProgress = false;
339                 m_msecNoUseBeyondFreeListSinceThisTime = curMSec;
340                 return true;
341             }
342
343             // We require a minimum amount of clock time to pass  (10 seconds) before we trim.  Ideally this time
344             // is larger than the typical buffer hold time.  
345             if (0 <= deltaMSec && deltaMSec < 10000)
346                 return true;
347
348             // If we got here we have spend the last few second without needing to lengthen the free list.   Thus
349             // we have 'enough' buffers, but maybe we have too many. 
350             // See if we can trim
351             lock (this)
352             {
353                 // Hit a ----, try again later.  
354                 if (m_moreThanFreeListNeeded)
355                 {
356                     m_moreThanFreeListNeeded = false;
357                     m_trimmingExperimentInProgress = false;
358                     m_msecNoUseBeyondFreeListSinceThisTime = curMSec;
359                     return true;
360                 }
361
362                 var freeCount = m_FreeList.Count;   // This is expensive to fetch, do it once.
363
364                 // If there is something in m_NotGen2 it was not used for the last few seconds, it is trimable.  
365                 if (m_NotGen2.Count > 0)
366                 {
367                     // If we are not performing an experiment and we have stuff that is waiting to go into the
368                     // free list but has not made it there, it could be becasue the 'slow path' of restocking
369                     // has not happened, so force this (which should flush the list) and start over.  
370                     if (!m_trimmingExperimentInProgress)
371                     {
372                         PinnableBufferCacheEventSource.Log.TrimFlush(m_CacheName, m_buffersUnderManagement, freeCount, m_NotGen2.Count);
373                         AgePendingBuffers();
374                         m_trimmingExperimentInProgress = true;
375                         return true;
376                     }
377
378                     PinnableBufferCacheEventSource.Log.TrimFree(m_CacheName, m_buffersUnderManagement, freeCount, m_NotGen2.Count);
379                     m_buffersUnderManagement -= m_NotGen2.Count;
380
381                     // Possibly revise the restocking down.  We don't want to grow agressively if we are trimming.  
382                     var newRestockSize = m_buffersUnderManagement / 4;
383                     if (newRestockSize < m_restockSize)
384                         m_restockSize = Math.Max(newRestockSize, DefaultNumberOfBuffers);
385
386                     m_NotGen2.Clear();
387                     m_trimmingExperimentInProgress = false;
388                     return true;
389                 }
390
391                 // Set up an experiment where we use 25% less buffers in our free list.   We put them in 
392                 // m_NotGen2, and if they are needed they will be put back in the free list again.  
393                 var trimSize = freeCount / 4 + 1;
394
395                 // We are OK with a 15% overhead, do nothing in that case.  
396                 if (freeCount * 15 <= m_buffersUnderManagement || m_buffersUnderManagement - trimSize <= m_minBufferCount)
397                 {
398                     PinnableBufferCacheEventSource.Log.TrimFreeSizeOK(m_CacheName, m_buffersUnderManagement, freeCount);
399                     return true;
400                 }
401
402                 // Move buffers from teh free list back to the non-aged list.  If we don't use them by next time, then we'll consider trimming them.
403                 PinnableBufferCacheEventSource.Log.TrimExperiment(m_CacheName, m_buffersUnderManagement, freeCount, trimSize);
404                 object buffer;
405                 for (int i = 0; i < trimSize; i++)
406                 {
407                     if (m_FreeList.TryPop(out buffer))
408                         m_NotGen2.Add(buffer);
409                 }
410                 m_msecNoUseBeyondFreeListSinceThisTime = curMSec;
411                 m_trimmingExperimentInProgress = true;
412             }
413
414             // Indicate that we want to be called back on the next Gen 2 GC.  
415             return true;
416         }
417
418         private const int DefaultNumberOfBuffers = 16;
419         private string m_CacheName;
420         private Func<object> m_factory;
421
422         /// <summary>
423         /// Contains 'good' buffers to reuse.  They are guarenteed to be Gen 2 ENFORCED!
424         /// </summary>
425         private ConcurrentStack<object> m_FreeList = new ConcurrentStack<object>();
426         /// <summary>
427         /// Contains buffers that are not gen 2 and thus we do not wish to give out unless we have to.
428         /// To implement trimming we sometimes put aged buffers in here as a place to 'park' them
429         /// before true deletion.  
430         /// </summary>
431         private List<object> m_NotGen2;
432         /// <summary>
433         /// What whas the gen 1 count the last time re restocked?  If it is now greater, then
434         /// we know that all objects are in Gen 2 so we don't have to check.  Should be updated
435         /// every time something gets added to the m_NotGen2 list.
436         /// </summary>
437         private int m_gen1CountAtLastRestock;
438
439         /// <summary>
440         /// Used to ensure we have a minimum time between trimmings.  
441         /// </summary>
442         private int m_msecNoUseBeyondFreeListSinceThisTime;
443         /// <summary>
444         /// To trim, we remove things from the free list (which is Gen 2) and see if we 'hit bottom'
445         /// This flag indicates that we hit bottom (we really needed a bigger free list).
446         /// </summary>
447         private bool m_moreThanFreeListNeeded;
448         /// <summary>
449         /// The total number of buffers that this cache has ever allocated.
450         /// Used in trimming heuristics. 
451         /// </summary>
452         private int m_buffersUnderManagement;
453         /// <summary>
454         /// The number of buffers we added the last time we restocked.
455         /// </summary>
456         private int m_restockSize;
457         /// <summary>
458         /// Did we put some buffers into m_NotGen2 to see if we can trim?
459         /// </summary>
460         private bool m_trimmingExperimentInProgress;
461         /// <summary>
462         /// A forced minimum number of buffers.
463         /// </summary>
464         private int m_minBufferCount;
465         /// <summary>
466         /// The number of calls to Allocate.
467         /// </summary>
468         private int m_numAllocCalls;
469
470         #endregion
471     }
472
473     /// <summary>
474     /// Schedules a callback roughly every gen 2 GC (you may see a Gen 0 an Gen 1 but only once)
475     /// (We can fix this by capturing the Gen 2 count at startup and testing, but I mostly don't care)
476     /// </summary>
477     internal sealed class Gen2GcCallback : CriticalFinalizerObject
478     {
479         [System.Security.SecuritySafeCritical]
480         public Gen2GcCallback()
481             : base()
482         {
483         }
484
485         /// <summary>
486         /// Schedule 'callback' to be called in the next GC.  If the callback returns true it is 
487         /// rescheduled for the next Gen 2 GC.  Otherwise the callbacks stop. 
488         /// 
489         /// NOTE: This callback will be kept alive until either the callback function returns false,
490         /// or the target object dies.
491         /// </summary>
492         public static void Register(Func<object, bool> callback, object targetObj)
493         {
494             // Create a unreachable object that remembers the callback function and target object.
495             Gen2GcCallback gcCallback = new Gen2GcCallback();
496             gcCallback.Setup(callback, targetObj);
497         }
498
499         #region Private
500
501         private Func<object, bool> m_callback;
502         private GCHandle m_weakTargetObj;
503
504         [System.Security.SecuritySafeCritical]
505         private void Setup(Func<object, bool> callback, object targetObj)
506         {
507             m_callback = callback;
508             m_weakTargetObj = GCHandle.Alloc(targetObj, GCHandleType.Weak);
509         }
510
511         [System.Security.SecuritySafeCritical]
512         ~Gen2GcCallback()
513         {
514             // Check to see if the target object is still alive.
515             if (!m_weakTargetObj.IsAllocated)
516             {
517                 return;
518             }
519
520             object targetObj = m_weakTargetObj.Target;
521             if (targetObj == null)
522             {
523                 // The target object is dead, so this callback object is no longer needed.
524                 m_weakTargetObj.Free();
525                 return;
526             }
527
528             // Execute the callback method.
529             try
530             {
531                 if (!m_callback(targetObj))
532                 {
533                     // If the callback returns false, this callback object is no longer needed.
534                     return;
535                 }
536             }
537             catch
538             {
539                 // Ensure that we still get a chance to resurrect this object, even if the callback throws an exception.
540             }
541
542             // Resurrect ourselves by re-registering for finalization.
543             if (!Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload())
544             {
545                 GC.ReRegisterForFinalize(this);
546             }
547         }
548
549         #endregion
550     }
551
552
553 #if FEATURE_CORECLR
554     internal sealed class PinnableBufferCacheEventSource
555     {
556         public static readonly PinnableBufferCacheEventSource Log = new PinnableBufferCacheEventSource();
557
558         public bool IsEnabled() { return false; }
559         public void DebugMessage(string message) {}
560         public void DebugMessage1(string message, long value) {}
561         public void DebugMessage2(string message, long value1, long value2) {}
562         public void DebugMessage3(string message, long value1, long value2, long value3) {}
563         public void Create(string cacheName) {}
564         public void AllocateBuffer(string cacheName, ulong objectId, int objectHash, int objectGen, int freeCountAfter) {}
565         public void AllocateBufferFromNotGen2(string cacheName, int notGen2CountAfter) {}
566         public void AllocateBufferCreatingNewBuffers(string cacheName, int totalBuffsBefore, int objectCount) {}
567         public void AllocateBufferAged(string cacheName, int agedCount) {}
568         public void AllocateBufferFreeListEmpty(string cacheName, int notGen2CountBefore) {}
569         public void FreeBuffer(string cacheName, ulong objectId, int objectHash, int freeCountBefore) {}
570         public void FreeBufferStillTooYoung(string cacheName, int notGen2CountBefore) {}
571         public void TrimCheck(string cacheName, int totalBuffs, bool neededMoreThanFreeList, int deltaMSec) {}
572         public void TrimFree(string cacheName, int totalBuffs, int freeListCount, int toBeFreed) {}
573         public void TrimExperiment(string cacheName, int totalBuffs, int freeListCount, int numTrimTrial) {}
574         public void TrimFreeSizeOK(string cacheName, int totalBuffs, int freeListCount) {}
575         public void TrimFlush(string cacheName, int totalBuffs, int freeListCount, int notGen2CountBefore) {}
576         public void AgePendingBuffersResults(string cacheName, int promotedToFreeListCount, int heldBackCount) {}
577         public void WalkFreeListResult(string cacheName, int freeListCount, int gen0BuffersInFreeList) {}
578
579         static internal ulong AddressOf(object obj)
580         {
581             return 0;
582         }
583
584         [System.Security.SecuritySafeCritical]
585         static internal unsafe long AddressOfObject(byte[] array)
586         {
587             return 0;
588         }
589     }
590 #else
591     /// <summary>
592     /// PinnableBufferCacheEventSource is a private eventSource that we are using to
593     /// debug and monitor the effectiveness of PinnableBufferCache
594     /// </summary>
595 #if PINNABLEBUFFERCACHE_MSCORLIB
596     [EventSource(Name = "Microsoft-DotNETRuntime-PinnableBufferCache")]
597 #else
598     [EventSource(Name = "Microsoft-DotNETRuntime-PinnableBufferCache-System")]
599 #endif
600     internal sealed class PinnableBufferCacheEventSource : EventSource
601     {
602         public static readonly PinnableBufferCacheEventSource Log = new PinnableBufferCacheEventSource();
603
604         [Event(1, Level = EventLevel.Verbose)]
605         public void DebugMessage(string message) { if (IsEnabled()) WriteEvent(1, message); }
606         [Event(2, Level = EventLevel.Verbose)]
607         public void DebugMessage1(string message, long value) { if (IsEnabled()) WriteEvent(2, message, value); }
608         [Event(3, Level = EventLevel.Verbose)]
609         public void DebugMessage2(string message, long value1, long value2) { if (IsEnabled()) WriteEvent(3, message, value1, value2); }
610         [Event(18, Level = EventLevel.Verbose)]
611         public void DebugMessage3(string message, long value1, long value2, long value3) { if (IsEnabled()) WriteEvent(18, message, value1, value2, value3); }
612
613         [Event(4)]
614         public void Create(string cacheName) { if (IsEnabled()) WriteEvent(4, cacheName); }
615
616         [Event(5, Level = EventLevel.Verbose)]
617         public void AllocateBuffer(string cacheName, ulong objectId, int objectHash, int objectGen, int freeCountAfter) { if (IsEnabled()) WriteEvent(5, cacheName, objectId, objectHash, objectGen, freeCountAfter); }
618         [Event(6)]
619         public void AllocateBufferFromNotGen2(string cacheName, int notGen2CountAfter) { if (IsEnabled()) WriteEvent(6, cacheName, notGen2CountAfter); }
620         [Event(7)]
621         public void AllocateBufferCreatingNewBuffers(string cacheName, int totalBuffsBefore, int objectCount) { if (IsEnabled()) WriteEvent(7, cacheName, totalBuffsBefore, objectCount); }
622         [Event(8)]
623         public void AllocateBufferAged(string cacheName, int agedCount) { if (IsEnabled()) WriteEvent(8, cacheName, agedCount); }
624         [Event(9)]
625         public void AllocateBufferFreeListEmpty(string cacheName, int notGen2CountBefore) { if (IsEnabled()) WriteEvent(9, cacheName, notGen2CountBefore); }
626
627         [Event(10, Level = EventLevel.Verbose)]
628         public void FreeBuffer(string cacheName, ulong objectId, int objectHash, int freeCountBefore) { if (IsEnabled()) WriteEvent(10, cacheName, objectId, objectHash, freeCountBefore); }
629         [Event(11)]
630         public void FreeBufferStillTooYoung(string cacheName, int notGen2CountBefore) { if (IsEnabled()) WriteEvent(11, cacheName, notGen2CountBefore); }
631
632         [Event(13)]
633         public void TrimCheck(string cacheName, int totalBuffs, bool neededMoreThanFreeList, int deltaMSec) { if (IsEnabled()) WriteEvent(13, cacheName, totalBuffs, neededMoreThanFreeList, deltaMSec); }
634         [Event(14)]
635         public void TrimFree(string cacheName, int totalBuffs, int freeListCount, int toBeFreed) { if (IsEnabled()) WriteEvent(14, cacheName, totalBuffs, freeListCount, toBeFreed); }
636         [Event(15)]
637         public void TrimExperiment(string cacheName, int totalBuffs, int freeListCount, int numTrimTrial) { if (IsEnabled()) WriteEvent(15, cacheName, totalBuffs, freeListCount, numTrimTrial); }
638         [Event(16)]
639         public void TrimFreeSizeOK(string cacheName, int totalBuffs, int freeListCount) { if (IsEnabled()) WriteEvent(16, cacheName, totalBuffs, freeListCount); }
640         [Event(17)]
641         public void TrimFlush(string cacheName, int totalBuffs, int freeListCount, int notGen2CountBefore) { if (IsEnabled()) WriteEvent(17, cacheName, totalBuffs, freeListCount, notGen2CountBefore); }
642         [Event(20)]
643         public void AgePendingBuffersResults(string cacheName, int promotedToFreeListCount, int heldBackCount) { if (IsEnabled()) WriteEvent(20, cacheName, promotedToFreeListCount, heldBackCount); }
644         [Event(21)]
645         public void WalkFreeListResult(string cacheName, int freeListCount, int gen0BuffersInFreeList) { if (IsEnabled()) WriteEvent(21, cacheName, freeListCount, gen0BuffersInFreeList); }
646
647
648         static internal ulong AddressOf(object obj)
649         {
650             var asByteArray = obj as byte[];
651             if (asByteArray != null)
652                 return (ulong)AddressOfByteArray(asByteArray);
653             return 0;
654         }
655
656         [System.Security.SecuritySafeCritical]
657         static internal unsafe long AddressOfByteArray(byte[] array)
658         {
659             if (array == null)
660                 return 0;
661             fixed (byte* ptr = array)
662                 return (long)(ptr - 2 * sizeof(void*));
663         }
664     }
665 #endif
666 }