[zeromq-dev] P/Invoke-based CLR access to zeroMQ
Barak Amar
barak.amar at gmail.com
Sun Mar 8 00:33:37 CET 2009
Hi Tamara,
I found two problems - the easy one:
The 'czmq_receive' now also return a message type so we need to add it to
the PInvoke signature.
static extern void czmq_receive (IntPtr zmq, [Out] out IntPtr data, [Out]
out UInt32 size, [Out] out FreeMsgData ffn, [Out] UInt32 msgType);
The second problem, which I think that you got is the 'send' implementation.
It passes a delegate (of FreeMsgData type) to 'czmq_send' - it seems that
the lifetime of the delegate is beyond the span of the call and in your case
the garbage collector free it before it is called by libzmq c++
implementation.
I still don't know the underlaying implementation well, but I don't think we
can determine when the data will be freed. The best solution I found so far
to resolve this problem is by using a static delegate:
private static FreeMsgData freeHGlobal = Marshal.FreeHGlobal;
public void send(int eid, byte[] data)
{
if (zmq_ == IntPtr.Zero)
throw new NullReferenceException("queue must be
initialized");
IntPtr ptr = Marshal.AllocHGlobal(data.Length);
Marshal.Copy(data, 0, ptr, data.Length);
czmq_send(zmq_, eid, ptr, Convert.ToUInt32(data.Length),
freeHGlobal);
}
Check if it works for you and if anybody had this issue before and have a
better solution I'll be happy to learn.
-- Barak
On Fri, Mar 6, 2009 at 1:38 PM, Tamara Kustarova <kustarova at fastmq.com>wrote:
> Hello Barak,
>
> I've tried the C# version of zmq that you suggested. I needed to make there
> some changes in order to make the calling conventions compatible. I tested
> it with cs_remote_thr and cs_local_thr scenarios and if I set the roundtrip
> count too large (I try to send large amount of messages), the aplication
> that sends data crashes with System.AccessViolationException.
>
> Don't you have any hints? I am addressing this mail to you Barak, because
> you wrote the code, but generally anybody could have a good idea why this is
> happening.
>
> The previous implementation using dnzmq.dll written in C++ works fine with
> cs_remote_thr nd cs_local_thr, so there probably won't be problem in these
> two.
>
> The code of C# dll looks like this.
> using System;
> using System.Runtime.InteropServices;
>
> namespace zmq
> {
>
> public class Dnzmq : IDisposable
> {
> private IntPtr zmq_;
>
> public const int SCOPE_LOCAL = 0;
> public const int SCOPE_GLOBAL = 1;
>
> public Dnzmq()
> {
> zmq_ = IntPtr.Zero;
> }
>
> public Dnzmq(string host)
> {
> Open(host);
> }
> public void Open(string host)
> {
> zmq_ = czmq_create(host);
> }
>
> public bool IsOpen { get { return zmq_ == IntPtr.Zero; } }
>
> public int create_exchange (string exchange, int scope, string nic)
> {
> if (zmq_ == IntPtr.Zero)
> throw new NullReferenceException("queue must be
> initialized");
> return czmq_create_exchange(zmq_, exchange, scope, nic);
> }
>
> public int create_queue (string queue, int scope, string nic, Int64
> hwm, Int64 lwm, Int64 swapSize)
> {
> if (zmq_ == IntPtr.Zero)
> throw new NullReferenceException("queue must be
> initialized");
> return czmq_create_queue(zmq_, queue, scope, nic, hwm, lwm,
> swapSize);
> }
>
> public void bind(string exchange, string queue, string exchangeArgs,
> string queueArgs)
> {
> if (zmq_ == IntPtr.Zero)
> throw new NullReferenceException("queue must be
> initialized");
> czmq_bind(zmq_, exchange, queue, exchangeArgs, queueArgs);
> }
>
> public void send(int eid, byte[] data)
> {
> if (zmq_ == IntPtr.Zero)
> throw new NullReferenceException("queue must be
> initialized");
> IntPtr ptr = Marshal.AllocHGlobal(data.Length);
> Marshal.Copy(data, 0, ptr, data.Length);
> czmq_send(zmq_, eid, ptr, Convert.ToUInt32(data.Length),
> Marshal.FreeHGlobal);
> }
>
> public byte[] receive()
> {
> if (zmq_ == IntPtr.Zero)
> throw new NullReferenceException("queue must be
> initialized");
> IntPtr data;
> UInt32 dataSize;
> FreeMsgData freeFunc;
> czmq_receive (zmq_, out data, out dataSize, out freeFunc);
>
> if (data == IntPtr.Zero)
> return new byte[0];
>
> byte[] msg = new byte[dataSize];
> Marshal.Copy(data, msg, 0, Convert.ToInt32(dataSize));
> if (freeFunc != null)
> freeFunc(data);
> return msg;
> }
>
> [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
> private delegate void FreeMsgData(IntPtr ptr);
> public void Close()
> {
> if (zmq_ != IntPtr.Zero)
> {
> czmq_destroy(zmq_);
> zmq_ = IntPtr.Zero;
> }
> }
>
> #region IDisposable Members
>
> public void Dispose()
> {
> Close();
> }
>
> #endregion
>
> #region C API
>
> [DllImport ("..\\..\\..\\..\\Debug\\libczmq.dll", CharSet =
> CharSet.Ansi)]
> static extern IntPtr czmq_create(string host);
>
> [DllImport("..\\..\\..\\..\\Debug\\libczmq.dll")]
> static extern void czmq_destroy(IntPtr zmq);
>
> [DllImport("..\\..\\..\\..\\Debug\\libczmq.dll",
> CharSet=CharSet.Ansi)]
> static extern int czmq_create_exchange (IntPtr zmq, string exchange,
> int scope, string nic);
>
> [DllImport("..\\..\\..\\..\\Debug\\libczmq.dll",
> CharSet=CharSet.Ansi)]
> static extern int czmq_create_queue (IntPtr zmq, string queue, int
> scope, string nic,
> Int64 hwm, Int64 lwm, Int64 swapSize);
>
> [DllImport("..\\..\\..\\..\\Debug\\libczmq.dll",
> CharSet=CharSet.Ansi)]
> static extern void czmq_bind (IntPtr zmq, string exchange, string
> queue,
> string exchangeArgs, string queueArgs);
>
> [DllImport("..\\..\\..\\..\\Debug\\libczmq.dll", CallingConvention =
> CallingConvention.Cdecl)]
> static extern void czmq_send (IntPtr zmq, int eid, IntPtr data_,
> UInt32 size, FreeMsgData ffn);
>
> [DllImport("..\\..\\..\\..\\Debug\\libczmq.dll", CallingConvention =
> CallingConvention.Cdecl)]
>
> static extern void czmq_receive (IntPtr zmq, [Out] out IntPtr data,
> [Out] out UInt32 size, [Out] out FreeMsgData ffn);
>
> #endregion
> }
> }
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.zeromq.org/pipermail/zeromq-dev/attachments/20090308/ee1f8e5f/attachment.htm
More information about the zeromq-dev
mailing list