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