@@ -479,25 +479,43 @@ void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_I
479
479
// 9. Process again (go to 3).
480
480
// 10. Continue until there are no more buffers to process.
481
481
482
- // Take the lock before walking the buffer list.
483
- SpinLockHolder _slh (&m_lock);
484
-
485
482
// Naively walk the circular buffer, writing the event stream in timestamp order.
486
483
m_numEventsWritten = 0 ;
487
484
while (true )
488
485
{
489
486
EventPipeEventInstance *pOldestInstance = NULL ;
490
487
EventPipeBuffer *pOldestContainingBuffer = NULL ;
491
488
EventPipeBufferList *pOldestContainingList = NULL ;
492
- SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead ();
493
- while (pElem != NULL )
489
+
490
+ CQuickArrayList<EventPipeBuffer*> bufferList;
491
+ CQuickArrayList<EventPipeBufferList*> bufferListList;
494
492
{
495
- EventPipeBufferList *pBufferList = pElem->GetValue ();
493
+ // Take the lock before walking the buffer list.
494
+ SpinLockHolder _slh (&m_lock);
495
+ SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead ();
496
+ while (pElem != NULL )
497
+ {
498
+ EventPipeBufferList* pBufferList = pElem->GetValue ();
499
+ EventPipeBuffer* pBuffer = pBufferList->TryGetBuffer (stopTimeStamp);
500
+ if (pBuffer != nullptr )
501
+ {
502
+ bufferListList.Push (pBufferList);
503
+ bufferList.Push (pBuffer);
504
+ }
505
+ pElem = m_pPerThreadBufferList->GetNext (pElem);
506
+ }
507
+ }
496
508
497
- // Peek the next event out of the list.
498
- EventPipeBuffer *pContainingBuffer = NULL ;
499
- EventPipeEventInstance *pNext = pBufferList->PeekNextEvent (stopTimeStamp, &pContainingBuffer);
500
- if (pNext != NULL )
509
+ for (size_t i = 0 ; i < bufferList.Size (); i++)
510
+ {
511
+ EventPipeBufferList* pBufferList = bufferListList[i];
512
+ EventPipeBuffer* pBuffer = bufferList[i];
513
+ pBufferList->ConvertBufferToReadOnly (pBuffer);
514
+
515
+ // Peek the next event out of the buffer.
516
+ EventPipeBuffer *pContainingBuffer = pBuffer;
517
+ EventPipeEventInstance *pNext = pBuffer->PeekNext (stopTimeStamp);
518
+ if (pNext != NULL )
501
519
{
502
520
// If it's the oldest event we've seen, then save it.
503
521
if ((pOldestInstance == NULL ) ||
@@ -507,9 +525,7 @@ void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_I
507
525
pOldestContainingBuffer = pContainingBuffer;
508
526
pOldestContainingList = pBufferList;
509
527
}
510
- }
511
-
512
- pElem = m_pPerThreadBufferList->GetNext (pElem);
528
+ }
513
529
}
514
530
515
531
if (pOldestInstance == NULL )
@@ -522,8 +538,12 @@ void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_I
522
538
pFile->WriteEvent (*pOldestInstance);
523
539
524
540
m_numEventsWritten++;
525
- // Pop the event from the buffer.
526
- pOldestContainingList->PopNextEvent (stopTimeStamp);
541
+
542
+ {
543
+ SpinLockHolder _slh (&m_lock);
544
+ // Pop the event from the buffer.
545
+ pOldestContainingList->PopNextEvent (stopTimeStamp);
546
+ }
527
547
}
528
548
529
549
if (m_numEventsWritten > 0 )
@@ -541,62 +561,78 @@ EventPipeEventInstance* EventPipeBufferManager::GetNextEvent()
541
561
}
542
562
CONTRACTL_END;
543
563
544
- // Take the lock before walking the buffer list.
545
- SpinLockHolder _slh (&m_lock);
546
-
547
- // Naively walk the circular buffer, getting the event stream in timestamp order.
548
564
LARGE_INTEGER stopTimeStamp;
549
565
QueryPerformanceCounter (&stopTimeStamp);
550
- while (true )
566
+
567
+ EventPipeEventInstance *pOldestInstance = NULL ;
568
+ EventPipeBuffer *pOldestContainingBuffer = NULL ;
569
+ EventPipeBufferList *pOldestContainingList = NULL ;
570
+
571
+ CQuickArrayList<EventPipeBuffer*> bufferList;
572
+ CQuickArrayList<EventPipeBufferList*> bufferListList;
551
573
{
552
- EventPipeEventInstance *pOldestInstance = NULL ;
553
- EventPipeBuffer *pOldestContainingBuffer = NULL ;
554
- EventPipeBufferList *pOldestContainingList = NULL ;
574
+ // Take the lock before walking the buffer list.
575
+ SpinLockHolder _slh (&m_lock);
555
576
SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead ();
556
- while (pElem != NULL )
577
+ while (pElem != NULL )
557
578
{
558
- EventPipeBufferList *pBufferList = pElem->GetValue ();
559
-
560
- // Peek the next event out of the list.
561
- EventPipeBuffer *pContainingBuffer = NULL ;
562
-
563
- // PERF: This may be too aggressive? If this method is being called frequently enough to keep pace with the
564
- // writing threads we could be in a state of high lock contention and lots of churning buffers. Each writer
565
- // would take several locks, allocate a new buffer, write one event into it, then the reader would take the
566
- // lock, convert the buffer to read-only and read the single event out of it. Allowing more events to accumulate
567
- // in the buffers before converting between writable and read-only amortizes a lot of the overhead. One way
568
- // to achieve that would be picking a stopTimeStamp that was Xms in the past. This would let Xms of events
569
- // to accumulate in the write buffer before we converted it and forced the writer to allocate another. Other more
570
- // sophisticated approaches would probably build a low overhead synchronization mechanism to read and write the
571
- // buffer at the same time.
572
- EventPipeEventInstance *pNext = pBufferList->PeekNextEvent (stopTimeStamp, &pContainingBuffer);
573
- if (pNext != NULL )
579
+ EventPipeBufferList* pBufferList = pElem->GetValue ();
580
+ EventPipeBuffer* pBuffer = pBufferList->TryGetBuffer (stopTimeStamp);
581
+ if (pBuffer != nullptr )
574
582
{
575
- // If it's the oldest event we've seen, then save it.
576
- if ((pOldestInstance == NULL ) ||
577
- (pOldestInstance->GetTimeStamp ()->QuadPart > pNext->GetTimeStamp ()->QuadPart ))
578
- {
579
- pOldestInstance = pNext;
580
- pOldestContainingBuffer = pContainingBuffer;
581
- pOldestContainingList = pBufferList;
582
- }
583
+ bufferListList.Push (pBufferList);
584
+ bufferList.Push (pBuffer);
583
585
}
584
-
585
586
pElem = m_pPerThreadBufferList->GetNext (pElem);
586
587
}
588
+ }
587
589
588
- if (pOldestInstance == NULL )
590
+ for (size_t i = 0 ; i < bufferList.Size (); i++)
591
+ {
592
+ EventPipeBufferList* pBufferList = bufferListList[i];
593
+ EventPipeBuffer* pBuffer = bufferList[i];
594
+ pBufferList->ConvertBufferToReadOnly (pBuffer);
595
+
596
+ // Peek the next event out of the buffer.
597
+ EventPipeBuffer *pContainingBuffer = pBuffer;
598
+
599
+ // PERF: This may be too aggressive? If this method is being called frequently enough to keep pace with the
600
+ // writing threads we could be in a state of high lock contention and lots of churning buffers. Each writer
601
+ // would take several locks, allocate a new buffer, write one event into it, then the reader would take the
602
+ // lock, convert the buffer to read-only and read the single event out of it. Allowing more events to accumulate
603
+ // in the buffers before converting between writable and read-only amortizes a lot of the overhead. One way
604
+ // to achieve that would be picking a stopTimeStamp that was Xms in the past. This would let Xms of events
605
+ // to accumulate in the write buffer before we converted it and forced the writer to allocate another. Other more
606
+ // sophisticated approaches would probably build a low overhead synchronization mechanism to read and write the
607
+ // buffer at the same time.
608
+ EventPipeEventInstance *pNext = pBuffer->PeekNext (stopTimeStamp);
609
+ if (pNext != NULL )
589
610
{
590
- // We're done. There are no more events.
591
- return NULL ;
592
- }
611
+ // If it's the oldest event we've seen, then save it.
612
+ if ((pOldestInstance == NULL ) ||
613
+ (pOldestInstance->GetTimeStamp ()->QuadPart > pNext->GetTimeStamp ()->QuadPart ))
614
+ {
615
+ pOldestInstance = pNext;
616
+ pOldestContainingBuffer = pContainingBuffer;
617
+ pOldestContainingList = pBufferList;
618
+ }
619
+ }
620
+ }
621
+
622
+ if (pOldestInstance == NULL )
623
+ {
624
+ // We're done. There are no more events.
625
+ return nullptr ;
626
+ }
593
627
628
+ {
629
+ SpinLockHolder _slh (&m_lock);
594
630
// Pop the event from the buffer.
595
631
pOldestContainingList->PopNextEvent (stopTimeStamp);
596
-
597
- // Return the oldest event that hasn't yet been processed.
598
- return pOldestInstance;
599
632
}
633
+
634
+ // Return the oldest event that hasn't yet been processed.
635
+ return pOldestInstance;
600
636
}
601
637
602
638
void EventPipeBufferManager::SuspendWriteEvent ()
@@ -875,6 +911,69 @@ unsigned int EventPipeBufferList::GetCount() const
875
911
return m_bufferCount;
876
912
}
877
913
914
+ EventPipeBuffer* EventPipeBufferList::TryGetBuffer (LARGE_INTEGER beforeTimeStamp)
915
+ {
916
+ LIMITED_METHOD_CONTRACT;
917
+ _ASSERTE (EventPipe::IsBufferManagerLockOwnedByCurrentThread ());
918
+ /* *
919
+ * There are 4 cases we need to handle in this function:
920
+ * 1) There is no buffer in the list, in this case, return nullptr
921
+ * 2) The head buffer is written to but not read yet, in this case, return that buffer
922
+ * 2.1) It is possible that the head buffer is the only buffer that is created and is empty, or
923
+ * 2.2) The head buffer is written to but not read
924
+ * We cannot differentiate the two cases without reading it - but it is okay, in both cases, the buffer represents the head of the buffer list.
925
+ * Note that writing to the buffer can happen after we return from this function, and it is also okay.
926
+ * 3.) The head buffer is read but not completely reading, and
927
+ * 4.) The head buffer is read completely.
928
+ * This case requires special attention because it is possible that the next buffer in the list contain the oldest event. Fortunately, it is
929
+ * already read so it is safe to read it to determine this case.
930
+ */
931
+
932
+ if (this ->m_pHeadBuffer == nullptr )
933
+ {
934
+ // Case 1
935
+ return nullptr ;
936
+ }
937
+ if (this ->m_pHeadBuffer ->GetCreationTimeStamp ().QuadPart >= beforeTimeStamp.QuadPart )
938
+ {
939
+ // If the oldest buffer is still newer than the beforeTimeStamp, we can stop.
940
+ return nullptr ;
941
+ }
942
+ EventPipeBufferState bufferState = this ->m_pHeadBuffer ->GetVolatileState ();
943
+ if (bufferState != EventPipeBufferState::READ_ONLY)
944
+ {
945
+ // Case 2 (2.1 or 2.2)
946
+ return this ->m_pHeadBuffer ;
947
+ }
948
+ else
949
+ {
950
+ if (this ->m_pHeadBuffer ->PeekNext (beforeTimeStamp))
951
+ {
952
+ // Case 3
953
+ return this ->m_pHeadBuffer ;
954
+ }
955
+ else
956
+ {
957
+ // Case 4
958
+ return this ->m_pHeadBuffer ->GetNext ();
959
+ }
960
+ }
961
+ }
962
+
963
+ void EventPipeBufferList::ConvertBufferToReadOnly (EventPipeBuffer* pNewReadBuffer)
964
+ {
965
+ LIMITED_METHOD_CONTRACT;
966
+ _ASSERTE (pNewReadBuffer != nullptr );
967
+ _ASSERTE (!EventPipe::IsBufferManagerLockOwnedByCurrentThread ());
968
+ {
969
+ SpinLockHolder _slh (m_pThread->GetLock ());
970
+ if (m_pThread->GetWriteBuffer () == pNewReadBuffer)
971
+ {
972
+ m_pThread->SetWriteBuffer (nullptr );
973
+ }
974
+ }
975
+ }
976
+
878
977
EventPipeBuffer* EventPipeBufferList::TryGetReadBuffer (LARGE_INTEGER beforeTimeStamp, EventPipeBuffer* pNewReadBuffer)
879
978
{
880
979
LIMITED_METHOD_CONTRACT;
@@ -961,13 +1060,13 @@ EventPipeEventInstance* EventPipeBufferList::PopNextEvent(LARGE_INTEGER beforeTi
961
1060
// Check to see if we need to clean-up the buffer that contained the previously popped event.
962
1061
if (pContainingBuffer->GetPrevious () != NULL )
963
1062
{
964
- // Remove the previous node. The previous node should always be the head node.
965
- EventPipeBuffer *pRemoved = GetAndRemoveHead ();
966
- _ASSERTE (pRemoved != pContainingBuffer);
967
- _ASSERTE (pContainingBuffer == GetHead ());
1063
+ // Remove the previous node. The previous node should always be the head node.
1064
+ EventPipeBuffer *pRemoved = GetAndRemoveHead ();
1065
+ _ASSERTE (pRemoved != pContainingBuffer);
1066
+ _ASSERTE (pContainingBuffer == GetHead ());
968
1067
969
- // De-allocate the buffer.
970
- m_pManager->DeAllocateBuffer (pRemoved);
1068
+ // De-allocate the buffer.
1069
+ m_pManager->DeAllocateBuffer (pRemoved);
971
1070
}
972
1071
973
1072
// If the event is non-NULL, pop it.
0 commit comments