GC running dozens of times while invoking an RLP function

I’m running this code in the managed side of my music player driver. It’s a static method executed in a thread. Within the method I have a tight while loop that reads bytes from a FileStream into a static byte double-buffer, and then calls InvokeEx on an RLP method.

While this is running, the Garbage Collector is repeatedly executing, as you can see from the debug output below.


private static void playFile()
{
	try
	{
		waitPlayerStart.WaitOne();

		while (playerStatus != PlayerStatus.Stopped)
		{
			vsWriteData.InvokeEx(buffer[bufferInUse], bytesRead);

			if (bytesRead == 0)
				break;

			bufferInUse ^= 1;
			bytesRead = fileStream.Read(buffer[bufferInUse], 0, BUFFER_SIZE);
			waitBufferEmpty.WaitOne();
		}
	}
	catch (ThreadAbortException)
	{
	}
	catch (Exception ex)
	{
		onEventNotification(EventCode.SystemError, ex);
	//	vsAbort.Invoke();
	}
	finally
	{
		if (fileStream != null)
		{
			fileStream.Close();
			fileStream.Dispose();
			fileStream = null;
		}
		Debug.GC(true);
	}
}

If I comment out the InvokeEx line, and replace the waitBufferEmpty.WaitOne(); line with a Thread.Sleep(100), the code executes for about 2 minutes without executing the GC once. My conclusion is that invoking the RLP method is causing GC to run, but my understanding of InvokeEx is that it passes a pointer to the byte buffer, and therefore should not require any memory allocation. Is that correct?


Playing FLAC tracks from SD card...
	\SD\FLAC\Classical\Vivaldi\The Four Seasons - Isaac Stern\01 - Spring-1-Allegro.flac
MS Status changed: Playing
GC: 2msec 40200 bytes used, 13932 bytes available
Type 0F (STRING              ):   1572 bytes
Type 11 (CLASS               ):   2052 bytes
Type 12 (VALUETYPE           ):     96 bytes
Type 13 (SZARRAY             ):  10956 bytes
Type 15 (FREEBLOCK           ):  13932 bytes
Type 17 (ASSEMBLY            ):  14160 bytes
Type 18 (WEAKCLASS           ):     48 bytes
Type 19 (REFLECTION          ):     60 bytes
Type 1B (DELEGATE_HEAD       ):    324 bytes
Type 1C (DELEGATELIST_HEAD   ):     48 bytes
Type 1D (OBJECT_TO_EVENT     ):     96 bytes
Type 1E (BINARY_BLOB_HEAD    ):   5712 bytes
Type 1F (THREAD              ):   1536 bytes
Type 20 (SUBTHREAD           ):    192 bytes
Type 21 (STACK_FRAME         ):   1356 bytes
Type 23 (LOCK_HEAD           ):     60 bytes
Type 24 (LOCK_OWNER_HEAD     ):     24 bytes
Type 26 (WAIT_FOR_OBJECT_HEAD):     48 bytes
Type 27 (FINALIZER_HEAD      ):     96 bytes
Type 31 (IO_PORT             ):     72 bytes
Type 34 (APPDOMAIN_HEAD      ):     72 bytes
Type 36 (APPDOMAIN_ASSEMBLY  ):   1620 bytes
GC: 2msec 40404 bytes used, 13728 bytes available
Type 0F (STRING              ):   1572 bytes
Type 11 (CLASS               ):   2052 bytes
Type 12 (VALUETYPE           ):     96 bytes
Type 13 (SZARRAY             ):  10956 bytes
Type 15 (FREEBLOCK           ):  13728 bytes
Type 17 (ASSEMBLY            ):  14160 bytes
Type 18 (WEAKCLASS           ):     48 bytes
Type 19 (REFLECTION          ):     60 bytes
Type 1B (DELEGATE_HEAD       ):    324 bytes
Type 1C (DELEGATELIST_HEAD   ):     48 bytes
Type 1D (OBJECT_TO_EVENT     ):     96 bytes
Type 1E (BINARY_BLOB_HEAD    ):   5712 bytes
Type 1F (THREAD              ):   1536 bytes
Type 20 (SUBTHREAD           ):    192 bytes
Type 21 (STACK_FRAME         ):   1560 bytes
Type 23 (LOCK_HEAD           ):     60 bytes
Type 24 (LOCK_OWNER_HEAD     ):     24 bytes
Type 26 (WAIT_FOR_OBJECT_HEAD):     48 bytes
Type 27 (FINALIZER_HEAD      ):     96 bytes
Type 31 (IO_PORT             ):     72 bytes
Type 34 (APPDOMAIN_HEAD      ):     72 bytes
Type 36 (APPDOMAIN_ASSEMBLY  ):   1620 bytes

... this goes on for more than 3 minutes, and GC executes 76 times total


I’ve done lots of RLP and GC rarely kicks in for me. What if WaitOne triggers GC?

@ Hyperlisk - The waitBufferEmpty.WaitOne is cleared when an RLP event fires. This is the event handler. Notice that for the buffer empty event, I only clear the AutoResetEvent. No memory is allocated.


private static void RLP_EventHandler(uint eventCode, DateTime TimeStamp)
{
	switch (eventCode)
	{
		case EVENT_PLAYING:
			playerStatus = PlayerStatus.Playing;
			waitPlayerStart.Set();
			onEventNotification(EventCode.StatusPlaying);
			break;

		case EVENT_STOPPING:
			playerStatus = PlayerStatus.Stopping;
			onEventNotification(EventCode.StatusStopping);
			break;

		case EVENT_FLUSHING:
			playerStatus = PlayerStatus.Flushing;
			onEventNotification(EventCode.StatusFlushing);
			break;

		case EVENT_STOPPED:
			playerStatus = PlayerStatus.Stopped;
			waitBufferEmpty.Set();
			Thread.Sleep(100); // allow the player thread to stop
			if (playerThread != null)
			{
				playerThread = null;
				Debug.GC(true);
			}
			onEventNotification(EventCode.StatusStopped);
			break;

		case EVENT_BUFFER_EMPTY:
			waitBufferEmpty.Set(); // send more data
			break;

		case EVENT_SOFTWARE_RESET:
			onEventNotification(EventCode.SoftwareReset);
			break;

		case EVENT_SYSTEM_ERROR:
			onEventNotification(EventCode.SystemError);
			break;

		default:
			onEventNotification((EventCode)eventCode);
			break;
	}
}