P
per9000
An interesting/annoying problem.
I created a small example to provoke an exception I keep getting.
Basically I have a C-struct (Container) with a function-pointer in it.
I perform repeated calls to the function in the container. I allocate
(and free in c) arrays to get garbage collection in both C and C#.
After a few seconds I get an exception.
I assume GarbageCollector moves the delegate (or collects is) and when
I don't find it in C (since it has moved) I get an exception. In my
real life problem this behaviour is not stopped by adding
GC.KeepAlive(X), but here it seems to be. Can the callback/delegate/
functionpointer still be moved by the garbage collector even if I add
GC.KeepAlive(X)?
I have somewhere seen that I can map a C-struct to a C#-struct but I
have had problems with it so that is not an option for me at this
point.
Can my problems be solved with the keyword fixed? If so how? I tried
fixed (DateCallBack* pdcb = &dcb) {...} but I am told that I cannot
take the address of, get the size of, or declare a pointer to a
managed type.
Any help is appreciated,
Per
//
// Contents of files below
//
// --------------------------------------------
/*
* File: CallBackExample.cs
*
* Compiled with:
* csc CallBackExample.cs
* Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
*
* Purpose:
* Illustrates problems with callback function
* when using it in combination with
* CallBackCaller.c
*
* Created 20070212
* by Per Erik Strandberg, www.pererikstrandberg.se
* for use in newsgroups
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace PER9000.CallBackExample
{
class CallBackExample
{
// delegate "class" to match my TimeStamper
delegate void DateCallBack(ref int y, ref int m, ref int d, ref
int h,
ref int min, ref int s, ref int ms);
// sets year, month, day, hour, minute, second and millisecond
public static void TimeStamper(ref int y, ref int m, ref int d,
ref int h, ref int min, ref int s, ref int ms)
{
DateTime dt = DateTime.Now;
y = dt.Year;
m = dt.Month;
d = dt.Day;
h = dt.Hour;
min = dt.Minute;
s = dt.Second;
ms = dt.Millisecond;
double[] tempArr = new double[y * (m + d)];
for (int i = 0; i < tempArr.Length; i++)
tempArr = Math.Sin(((double)y) / (d + m)) +
Math.Sqrt((double)(ms + s + min));
}
[DllImport("CallBackCaller.dll")]
extern static void InitStruct(DateCallBack func, ref IntPtr Ctr);
[DllImport("CallBackCaller.dll")]
extern static void KillStruct(IntPtr Ctr);
[DllImport("CallBackCaller.dll")]
extern static void RunFuncs(IntPtr Ctr, int i, int j);
// testprogram does the tests
unsafe static void Main(string[] args)
{
// say: Hell o World
Console.WriteLine("C# >> start");
// Create IntPtr to manage C-struct
IntPtr Ctr = new IntPtr();
// Create callback to put in Container
DateCallBack dcb = new DateCallBack(
PER9000.CallBackExample.CallBackExample.TimeStamper);
fixed (DateCallBack* pdcb = &dcb)
{
// ...
}
// create container
InitStruct(dcb, ref Ctr);
// Call RunFuncs
int i = 5;
int j = 5;
try
{
i = int.Parse(args[0]);
j = int.Parse(args[1]);
}
catch(Exception)
{
i = 5;
j = 5;
}
Console.WriteLine("C# >> calling with (i,j) == ({0},{1})", i,
j);
RunFuncs(Ctr, i, j);
// Kill Container
KillStruct(Ctr);
// keep alive and see the same (?) behaviour
// GC.KeepAlive(dcb);
// say that's it folks
Console.WriteLine("C# >> done");
}
}
}
/* CallBackExample.cs EOF */
// --------------------------------------------
/*
* File: CallBackCaller.c
*
* Compiled with:
* cl CallBackCaller.c /LD /Zi
* Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804
for 80x86
*
* Purpose:
* Illustrates movement (?) of callback function
* when using it in combination with
* CallBackExample.cs
*
* Created 20070212
* by Per Erik Strandberg, www.pererikstrandberg.se
* for use in newsgroups
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* should match C# delegate DateCallBack */
typedef void (__stdcall * DateCallBack)(int*, int*, int*, int*, int*,
int*, int*);
/* struct with function pointer */
typedef struct ContainerStruct
{
DateCallBack F;
} Container;
/* Creates a struct with a callback */
void __declspec(dllexport) InitStruct(DateCallBack Function,
Container ** Ctr)
{
*Ctr = (Container *) malloc(1 * sizeof(Container));
(*Ctr)->F = Function;
}
/* Frees the struct */
void __declspec(dllexport) KillStruct(Container * Ctr)
{
free(Ctr);
}
/* Evil tester - runs the callback very many times */
void __declspec(dllexport) RunFuncs(Container * Ctr, int i, int j)
{
/* p,q are loop counters */
int p, q;
/* year, month and so on */
int y, m, d, h, min, s, ms;
/* for creation of temporary array */
int l, len;
int * tempArr;
len = i+j+500;
/*
* 1 - call the callback and print result on console
* 2 - allocate memory (int-array)
* 3 - fill array with annoying stuff that
* a - takes time
* b - provoces memory management
* 4 - free memory
*/
for (p = 1; p < i; p++)
{
/* 1 */
Ctr->F(&y, &m, &d, &h, &min, &s, &ms);
printf("C >> (p,q) = (%d,%d) of (%d,%d): %d-%d-%d, %d:%d:%d,%d
\n",
p, q, i, j, y, m, d, h, min, s, ms);
fflush(stdout);
/* 2 */
tempArr = (int * ) malloc((len)*sizeof(int));
/* 3 */
for (l = 1; l < len; l++)
for (q = 1; q < j; q++)
tempArr[l] = (q+(l*l/l)/l+123)-(321+(l*l/l)/l)*l/l;
/* 4 */
free(tempArr);
}
}
/* CallBackCaller.c EOF */
I created a small example to provoke an exception I keep getting.
Basically I have a C-struct (Container) with a function-pointer in it.
I perform repeated calls to the function in the container. I allocate
(and free in c) arrays to get garbage collection in both C and C#.
After a few seconds I get an exception.
I assume GarbageCollector moves the delegate (or collects is) and when
I don't find it in C (since it has moved) I get an exception. In my
real life problem this behaviour is not stopped by adding
GC.KeepAlive(X), but here it seems to be. Can the callback/delegate/
functionpointer still be moved by the garbage collector even if I add
GC.KeepAlive(X)?
I have somewhere seen that I can map a C-struct to a C#-struct but I
have had problems with it so that is not an option for me at this
point.
Can my problems be solved with the keyword fixed? If so how? I tried
fixed (DateCallBack* pdcb = &dcb) {...} but I am told that I cannot
take the address of, get the size of, or declare a pointer to a
managed type.
Any help is appreciated,
Per
//
// Contents of files below
//
// --------------------------------------------
/*
* File: CallBackExample.cs
*
* Compiled with:
* csc CallBackExample.cs
* Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
*
* Purpose:
* Illustrates problems with callback function
* when using it in combination with
* CallBackCaller.c
*
* Created 20070212
* by Per Erik Strandberg, www.pererikstrandberg.se
* for use in newsgroups
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace PER9000.CallBackExample
{
class CallBackExample
{
// delegate "class" to match my TimeStamper
delegate void DateCallBack(ref int y, ref int m, ref int d, ref
int h,
ref int min, ref int s, ref int ms);
// sets year, month, day, hour, minute, second and millisecond
public static void TimeStamper(ref int y, ref int m, ref int d,
ref int h, ref int min, ref int s, ref int ms)
{
DateTime dt = DateTime.Now;
y = dt.Year;
m = dt.Month;
d = dt.Day;
h = dt.Hour;
min = dt.Minute;
s = dt.Second;
ms = dt.Millisecond;
double[] tempArr = new double[y * (m + d)];
for (int i = 0; i < tempArr.Length; i++)
tempArr = Math.Sin(((double)y) / (d + m)) +
Math.Sqrt((double)(ms + s + min));
}
[DllImport("CallBackCaller.dll")]
extern static void InitStruct(DateCallBack func, ref IntPtr Ctr);
[DllImport("CallBackCaller.dll")]
extern static void KillStruct(IntPtr Ctr);
[DllImport("CallBackCaller.dll")]
extern static void RunFuncs(IntPtr Ctr, int i, int j);
// testprogram does the tests
unsafe static void Main(string[] args)
{
// say: Hell o World
Console.WriteLine("C# >> start");
// Create IntPtr to manage C-struct
IntPtr Ctr = new IntPtr();
// Create callback to put in Container
DateCallBack dcb = new DateCallBack(
PER9000.CallBackExample.CallBackExample.TimeStamper);
fixed (DateCallBack* pdcb = &dcb)
{
// ...
}
// create container
InitStruct(dcb, ref Ctr);
// Call RunFuncs
int i = 5;
int j = 5;
try
{
i = int.Parse(args[0]);
j = int.Parse(args[1]);
}
catch(Exception)
{
i = 5;
j = 5;
}
Console.WriteLine("C# >> calling with (i,j) == ({0},{1})", i,
j);
RunFuncs(Ctr, i, j);
// Kill Container
KillStruct(Ctr);
// keep alive and see the same (?) behaviour
// GC.KeepAlive(dcb);
// say that's it folks
Console.WriteLine("C# >> done");
}
}
}
/* CallBackExample.cs EOF */
// --------------------------------------------
/*
* File: CallBackCaller.c
*
* Compiled with:
* cl CallBackCaller.c /LD /Zi
* Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804
for 80x86
*
* Purpose:
* Illustrates movement (?) of callback function
* when using it in combination with
* CallBackExample.cs
*
* Created 20070212
* by Per Erik Strandberg, www.pererikstrandberg.se
* for use in newsgroups
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* should match C# delegate DateCallBack */
typedef void (__stdcall * DateCallBack)(int*, int*, int*, int*, int*,
int*, int*);
/* struct with function pointer */
typedef struct ContainerStruct
{
DateCallBack F;
} Container;
/* Creates a struct with a callback */
void __declspec(dllexport) InitStruct(DateCallBack Function,
Container ** Ctr)
{
*Ctr = (Container *) malloc(1 * sizeof(Container));
(*Ctr)->F = Function;
}
/* Frees the struct */
void __declspec(dllexport) KillStruct(Container * Ctr)
{
free(Ctr);
}
/* Evil tester - runs the callback very many times */
void __declspec(dllexport) RunFuncs(Container * Ctr, int i, int j)
{
/* p,q are loop counters */
int p, q;
/* year, month and so on */
int y, m, d, h, min, s, ms;
/* for creation of temporary array */
int l, len;
int * tempArr;
len = i+j+500;
/*
* 1 - call the callback and print result on console
* 2 - allocate memory (int-array)
* 3 - fill array with annoying stuff that
* a - takes time
* b - provoces memory management
* 4 - free memory
*/
for (p = 1; p < i; p++)
{
/* 1 */
Ctr->F(&y, &m, &d, &h, &min, &s, &ms);
printf("C >> (p,q) = (%d,%d) of (%d,%d): %d-%d-%d, %d:%d:%d,%d
\n",
p, q, i, j, y, m, d, h, min, s, ms);
fflush(stdout);
/* 2 */
tempArr = (int * ) malloc((len)*sizeof(int));
/* 3 */
for (l = 1; l < len; l++)
for (q = 1; q < j; q++)
tempArr[l] = (q+(l*l/l)/l+123)-(321+(l*l/l)/l)*l/l;
/* 4 */
free(tempArr);
}
}
/* CallBackCaller.c EOF */