Threading issue...

Threading issue...

Old forum URL: forums.lhotka.net/forums/t/2932.aspx


kbcb posted on Tuesday, May 22, 2007

I have a CSLA application (which is working GREAT, btw) which needs to have server to client messaging as well. The functionality inside the server to client messaging hasn't needed to be too extensive, so what I did to implement it, was create a "client handler" and "server handler" class where:

The client handler registers a set of objects to be remotable and sends a "Register" request to the server handler.

The server handler takes registration requests and establishes a connection to the objects and persists them for later use - How I persisted the is a different story. The server handler is also setup with some methods that initiate sending messages to the client(s) using the persisted remoting registrations.

For a little further explanation to prepare for the problem: Our security is far more complex that just a call to an IsInRole method will suffice. So we have used custom csla authentication to handle our security requirements.

What we wanted to do, was setup the system so that when a user's security has changed, the server handler would be used to send out a notification/message to the registered client (client handler) so that the client application would be aware of the security change and make an update to the locally cached security properties/objects.

Now for the problem: The way this was implemented, was that when our user object gets updated, the DataPortal_Update method makes a call to ServerHandler.NotifyClient_SecurityChanged(identity) passing in a newly updated identity object. The server handler then loops through each of the clients and finds the one that the security change applies to (if any). Once a registered client is found, a method on the remotable client handler object is called from the server, passing the updated identity information. At this point (essentially), the client handler re-performs the login process and sets ApplicationContext.User to the a new principal object based on the identity passed forward by the server.

ALL of this works fine EXCEPT that after the client handler's method is finished, the updates to the ApplicationContext.User property are dumped and revert back to what they were before the whole process started.

At first I figured the user was one of the ApplicationContext global items that gets updated by the data portal on completion of a data portal call, but didn't find that to be the case (after looking through the CSLA framework code).

Next, I figured that perhaps it was a problem with the Transaction and that the transaction was getting rolled back; But I broke into the data portal code and WATCHED it call TransactionScope.Complete(). So that (to me) says that it's not a transactional problem.

Lastly, I have come to the conclusion that it must be a threading issue and that since a new thread is created from the client handler to deal with the server's messaging request, and because the ApplicationContext.User property maps to either Thread.CurrentPrincipal, I figured it must be dumping the changes because it is in a different thread. HOWEVER, even THAT seems not too likely to me though, because:

1) I placed some of the code on the UI (to test) and invoked the "re-login" process from the same thread as what was used to initially set the ApplicationContext.User property.
2) If the principal was truely "per thread", then how could you ever spawn off a new thread and make a data-portal call without setting the principal of the thread that the data portal call is being called on.

I am running out of ideas for this problem, so if someone can help, I would really appreciate it.

CaymanIslandsCarpediem replied on Tuesday, May 22, 2007

I tend to suspect you are correct about this being related to threading.

Usually, I've found it safest when dealing with threads on something like a "listener" like this to pass a delegate to the new thread which will then raise that event to the original thread to do anything.  Here is roughly what I'm talking about from a similar project.

private void frmClient_Load(object sender, EventArgs e)
{
   try
   {
       new UDPBroadcastListener(new CommandReceived(ReceivedCommand), 5001);
   }
   catch (Exception ex)
   {
       Application.DoEvents();
   }
}

private void ReceivedCommand(AppCommand cmd)
{
    //do something
}

 

RockfordLhotka replied on Tuesday, May 22, 2007

Oh yes. Any time you are handling a remote call (via Remoting or WCF anyway) your code is running on a thread from the thread pool.

So when you think you are calling back to your client, you are only calling back to the client's AppDomain, not the primary thread for the client. Since the .NET security objects (and the CSLA ApplicationContext data) are all stored per-thread, you are setting the values on a thread other than the primary thread for the client app.

kbcb replied on Tuesday, May 22, 2007

This is some relatively new information to me, so I googled "UDPBroadcastListener" and found very little. I also googled "BroadcastListener" and "TcpBroadcastListender" and got similar results. However, I looked on MSDN Library for Listener and found that there is an HttpListener class, but the example code doesn't look very similar to what you were showing.

Can you point me in the right direction as to what "listener" to use for an IIS Remoting based application?

CaymanIslandsCarpediem replied on Wednesday, May 23, 2007

Yeah, that code I posted was from an "in-house" custom app.  UDPBroadcastListener is a custom class so I don't expect you'd find much on it ;-)  However, after a quick search here are a couple simple samples of how to use threads with call-back functions to the primary thread.

http://www.codeproject.com/csharp/workerthread.asp

http://www.codeproject.com/Purgatory/ThreadsinC_.asp

Hopefully, these will point you in the right direction.

Cheers!

Tony

RockfordLhotka replied on Wednesday, May 23, 2007

You are looking at way to low a level.

 

Remoting is built on top of all that low-level plumbing, so you don’t need to worry about those details. Remoting sets up the listener, handles the incoming message and executes your code on a thread from the thread pool – all by itself.

 

All you need to worry about is the fact that your code is running on a background thread, and that you need to transfer the call to the UI thread. This is a standard .NET practice – at least in Windows Forms – where you call Invoke() on a form or other Windows control, and that transfers (marshals) the call to the UI thread. Just pass all the data to the Invoke() call as an argument and do the real work on the UI thread.

 

If you actually call back to your originating client you could run into some issues. Remember that your UI thread on the calling client is blocked, because it is waiting for the server call to complete. But if the server call goes back to the client it would end up waiting for the UI thread to become free – which won’t happen. Deadlock.

 

To avoid this, you can use form.BeginInvoke(), which queues the argument data in a call to the UI thread, allowing the callback to complete immediately (even though it really hasn’t been processed yet). That would allow the server code to complete, freeing the UI thread,  which would then execute the BeginInvoke() call. Deadlock averted.

 

Rocky

 

 

From: kbcb [mailto:cslanet@lhotka.net]
Sent: Tuesday, May 22, 2007 9:05 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Threading issue...

 

This is some relatively new information to me, so I googled "UDPBroadcastListener" and found very little. I also googled "BroadcastListener" and "TcpBroadcastListender" and got similar results. However, I looked on MSDN Library for Listener and found that there is an HttpListener class, but the example code doesn't look very similar to what you were showing.

Can you point me in the right direction as to what "listener" to use for an IIS Remoting based application?



kbcb replied on Wednesday, May 23, 2007

OK... I realize now that what you guys are talking about is NOT too new to me.

I, in fact, have already implemented an invokable method that gets run from the UI and still had no luck. As I said in my first post, one of my assumptions/tests was that it was a threading issue. So what I did, was named my UI thread (from the MainForm constructor) and watched that my login code was running on the UI thread (just to make sure that what I thought was happening was happening). Then, I did as both of you suggest, and created a delegate that I accepted the parameters that I needed to update the security/identity of the ApplicationContext.User property. I then invoked the delegate from the MainForm thread (named "UI Thread") while passing the parameters needed to the invoke method. At this point, I can clearly break into the "re-login" code and see that it is running off of my "UI Thread".

Even with this being done, the data in ApplicationContext.User is still being rolled back to what it was prior to the security change as SOON as it leaves the method.

kbcb replied on Thursday, May 24, 2007

Hey guys... Any more ideas?

RockfordLhotka replied on Thursday, May 24, 2007

I don't have any ideas, sorry.

Wouldn't it be far simpler to just use the callback to trigger the client app to re-login?

kbcb replied on Thursday, May 24, 2007

It would be, except how would you do that when one user changes another user's permissions. How does the "other" user's permissions get updated?

JoeFallon1 replied on Thursday, May 24, 2007

Every app I have ever used does not behave that way. Your permissions are what they are when you log in. Until you logout and back in - they do not change. Most users expect this behavior so why not go with the flow?

Joe

 

kbcb replied on Thursday, May 24, 2007

Yah... I can agree with that. Sure would still like to know why this is happening though. Even if this particular sittuation doesn't fit, the fact that information is just getting lost without explanation is an issue for me. Yah know what I mean?

RockfordLhotka replied on Thursday, May 24, 2007

The only think I can think of that is perhaps (and this seems like a long shot) the Invoke() behavior of Windows forms brings the thread context over temporarily, and then resets it once the invoke is complete.

Copyright (c) Marimer LLC