[rabbitmq-discuss] .NET client SimpleRpcServer question

Ryan Davis ryan at acceleration.net
Tue Jun 9 17:02:22 BST 2009


Matthias Radestock wrote:
> Looking at the code, I am pretty sure that Close() is meant to be
> called from inside the HandleCall/Cast functions. 
Really?  The code comment on Close() says "Shut down the server, causing
MainLoop() to return to its caller.", which is exactly what I want.  I
dug deeper into SimpleRpcServer, and what I really need is to break from
MainLoop's foreach after the body of the loop has executed.  The
m_subscription is used after HandleCall / HandleCast / ProcessRequest,
and I don't see how I could close it in HandleCall without creating
problems later on down the line. 

If I call Close() at any point in HandleCall/Cast, then the byte[]
intended for the RPC caller will be lost, and I expect it would throw a
nullref when it tries to BasicPublish those bytes.
If I call Close() at the end of ProcessRequest, then the message will be
processed and the ack will be lost.  This isn't so bad as I have other
logic checking for duplicates, but I expect it would throw a nullref
when it tries to ack in MainLoop.
If I call Close() at the beginning of ProcessRequest, then the message
will not be processed and the ack will be lost.  This isn't so bad as we
should have a redelivery, but I expect it would throw a nullref when it
tries to ack in MainLoop.

> It would be useful to get a stack trace with line numbers to work out
> exactly where the NPE occurs.
I'm afraid I might be a minor version behind at this point.
System.NullReferenceException: Object reference not set to an instance of an object.
   at RabbitMQ.Client.Impl.ModelBase.BasicCancel(String consumerTag) in d:\Projects\rabbitmq-dotnet-client-1.5.3\src\client\impl\ModelBase.cs:line 668
   at RabbitMQ.Client.MessagePatterns.Subscription.Close() in d:\Projects\rabbitmq-dotnet-client-1.5.3\src\client\messagepatterns\Subscription.cs:line 223
   at RabbitMQ.Client.MessagePatterns.Subscription.System.IDisposable.Dispose() in d:\Projects\rabbitmq-dotnet-client-1.5.3\src\client\messagepatterns\Subscription.cs:line 454
   at RabbitMQ.Client.MessagePatterns.SimpleRpcServer.MainLoop() in d:\Projects\rabbitmq-dotnet-client-1.5.3\src\client\messagepatterns\SimpleRpcServer.cs:line 205

Line 668 is:
ModelShutdown -= new
ModelShutdownEventHandler(k.m_consumer.HandleModelShutdown);

I think I'm calling subscription.Close() multiple times, since my code
is essentially:

using(var subscription = new Subscription(model, "queue", false))
using (RpcServer = new MyRpcServer(subscription, this)) {
 RpcServer.MainLoop();
}

So I think I'm calling subscription.Close() 3 times in 2 threads:
# controller thread: calls RpcServer.Close()
# worker thread: when the using() invokes subscription.Dispose()
# worker thread: when the using() invokes RpcServer.Dispose()

Subscription.Close() is checking if m_consumer is null before calling
BasicCancel, so it looks like a race condition between the controlling
thread calling RpcServer.Close() and the worker thread cleaning up with
Dispose(), but I don't follow how the foreach in MainLoop would
terminate and start Disposing of things before the controller thread had
gotten through subscription.Close() to set m_consumer to null.

I've got a unit test that reproduces this behavior, so I'll  be playing
with this today, stepping through to better understand what is happening.

As a side note, the docs on
http://www.rabbitmq.com/build-dotnet-client.html could use mention of
the local.build file.

Thanks,

Ryan Davis
Acceleration.net
Director of Programming Services
2831 NW 41st street, suite B
Gainesville, FL 32606

Office: 352-335-6500 x 124
Fax: 352-335-6506






More information about the rabbitmq-discuss mailing list