LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Mutex Handle leak with DAQmx

Hi,

 

I'm using CVI 2015 and DAQmx 15.1.1.

 

When I use DAQmx functions from user DLL that created by CVI,

 

there is Mutex handle leak even if after unload that.

 

For example, WinDbg reports handle leaks on name start with

 

"National Instruments LabWindows/CVI Data Acquisition MX Library".

 

Do I need to call some function to clean up this resource?

 

This is some of debugger trace:

 

Handle = 0x00000594 - OPEN
Thread ID = 0x00000704, Process ID = 0x0000251c

0xc30cd83a: +0xc30cd83a
0xc2d5b193: +0xc2d5b193
0x43fa6374: +0x43fa6374
0x6a46bf87: +0x6a46bf87
0x6a466ea5: +0x6a466ea5
0x6a451cf7: +0x6a451cf7
0x6a47bfa1: +0x6a47bfa1
0x6a46cbb0: +0x6a46cbb0
0x43f91791: +0x43f91791
0x43fc7b28: +0x43fc7b28
0x43f78d6e: +0x43f78d6e
0x77ebe7ac: ntdll!NtCreateMutant+0x0000000c
0x74a8b7bb: KERNELBASE!CreateMutexExW+0x000000cb
0x74ab1c95: KERNELBASE!CreateMutexA+0x00000075
0x02af2188: <Unloaded_DllProject.dll>+0x00002188
--------------------------------------
Handle = 0x000005d8 - OPEN
Thread ID = 0x00000704, Process ID = 0x0000251c

0xc30cd83a: +0xc30cd83a
0xc2d5b193: +0xc2d5b193
0x43fa6374: +0x43fa6374
0x6a46bf87: +0x6a46bf87
0x6a466ea5: +0x6a466ea5
0x6a451cf7: +0x6a451cf7
0x6a47bfa1: +0x6a47bfa1
0x6a46cbb0: +0x6a46cbb0
0x43f91791: +0x43f91791
0x43fc7b28: +0x43fc7b28
0x43f78d6e: +0x43f78d6e
0x77ebe7ac: ntdll!NtCreateMutant+0x0000000c
0x74a8b7bb: KERNELBASE!CreateMutexExW+0x000000cb
0x74ab1c95: KERNELBASE!CreateMutexA+0x00000075
0x02af2188: <Unloaded_DllProject.dll>+0x00002188
--------------------------------------
Displayed 0x66 stack traces for outstanding handles opened since the previous snapshot.
0:011> !handle 5d8 f
Handle 5d8
  Type         	Mutant
  Attributes   	0
  GrantedAccess	0x1f0001:
         Delete,ReadControl,WriteDac,WriteOwner,Synch
         QueryState
  HandleCount  	102
  PointerCount 	3342236
  Name         	\Sessions\1\BaseNamedObjects\National Instruments LabWindows/CVI Data Acquisition MX Library 9500
  Object Specific Information
    Mutex is Free
0 Kudos
Message 1 of 4
(3,378 Views)

This is some hack for DLL generated by CVI 2015, call this inside your DLL before unloaded(32bit).

 

typedef void (__cdecl *CleanFunc)(int);

static void cleanUp(void)
{
	unsigned int *p;
	CleanFunc func;
	
	p = (unsigned int *)DAQmxCreateTask;
	func = (CleanFunc)(p - (0x74e0 / 4));
	
	func(4);
}
Message 2 of 4
(3,317 Views)

Hello,

 

Wonderful hack, maybe place it in the DLL entry point for Process/Thread Detach?

 

If you run one of the DAQmx ANSI C examples, do you find this to still be the case? If so, I would contact your local NI support to inform them of the mutex leak and your findings.

 

Best regards,

 

Ed

0 Kudos
Message 3 of 4
(3,219 Views)

I'm using it in DllMain.

 

I don't think DAQmx ANSI C examples show handle problem because they are all single executable file example. I'll attach sample source files for this case.

 

Build first source as DLL file name as SampleDAQLibrary.dll and check them with second source. If you did not call cleanFunc in DllMain detach case, you can see increased handle count in task manager.

 

#include <stdio.h>
#include <NIDAQmx.h>
#include <Windows.h>

#include <cvirte.h>

/*****************************************************************************/

typedef void (__cdecl *CleanFunc)(int);

static void cleanUp(void)
{
	unsigned int *p;
	CleanFunc func;

	p = (unsigned int *)DAQmxCreateTask;
	func = (CleanFunc)(p - (0x74e0 / 4));
	
	func(4);
}

__declspec(dllexport) void TestAnalogIn(void)
{
	TaskHandle handle;
	double buff[256];
	int arraySize, readSize;

	if (DAQmxCreateTask("", &handle) != 0) {

		printf("Error on DAQmxCreateTask\n");

		return;
	}
	
	if (DAQmxCreateAIVoltageChan(handle,
		"Dev1/ai0",
		"",
		DAQmx_Val_RSE,
		-5,
		5,
		DAQmx_Val_Volts,
		NULL) != 0) {

		printf("Error on DAQmxCreateAIVoltageChan\n");

		goto exit;
	}
	
	arraySize = 4;
	if (DAQmxReadAnalogF64(handle,
		DAQmx_Val_Auto,
		1.0,
		DAQmx_Val_GroupByChannel,
		buff,
		arraySize,
		&readSize,
		NULL) != 0) {

		printf("Error on DAQmxReadAnalogF64\n");
	}

	printf("Read %d samples\n", readSize);

 exit:
	if (DAQmxStopTask(handle) != 0) {
	
		printf("Error on DAQmxStopTask\n");
	}
	
	if (DAQmxClearTask(handle) != 0) {
	
		printf("Error on DAQmxClearTask\n");
	}
}

/*****************************************************************************/

int __stdcall DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	switch (fdwReason)
	{
		case DLL_PROCESS_ATTACH:
			if (InitCVIRTE (hinstDLL, 0, 0) == 0) {
				return 0;
			}
			
			break;
		case DLL_PROCESS_DETACH:
			CloseCVIRTE();

			/* call cleanUp before unloaded */
			cleanUp();
			break;
	}
	
	return 1;
}

int __stdcall DllEntryPoint (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	/* Included for compatibility with Borland */

	return DllMain (hinstDLL, fdwReason, lpvReserved);
}
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

/*****************************************************************************/

typedef void (*testFunc)(void);

static void test(void)
{
	HMODULE handle;
	testFunc func;

	handle = LoadLibrary("SampleDAQLibrary.dll");
	if (!handle) {

		printf("Error on load library\n");

		return;
	}
	
	func = (testFunc)GetProcAddress(handle, "TestAnalogIn");
	if (!func) {

		printf("Error on get function address\n");

		goto exit;
	}
	
	func();

 exit:
	FreeLibrary(handle);
}

int main(void)
{
	int i;

	printf("first prerun\n");
	test();

	printf("Enter to continue\n");
	getchar();
	
	for (i = 0; i < 100; ++i) {

		printf("run %d\n", i);

		test();
	}

	printf("End\n");
	getchar();
	
	return (0);
}

 

0 Kudos
Message 4 of 4
(3,207 Views)