Link to home
Start Free TrialLog in
Avatar of Alexandre Simões
Alexandre SimõesFlag for Switzerland

asked on

Queue on a Windows Service

The idea:
Several users will connect to this WS thru Remoting, requesting certain tasks.

Some interesting points:
0- You can think of this as the SQL Server service, but this one receives more requests than only DB interactions.
1- Some of the task don't return any value but some (most of them) do.
    > So I want the clients to place the request and receive the response using a single Function.
2- The WS must allways be available to process new requests.
    > The WS must never be as "not responding" because it's processing a request... (allways async calls)

Now, what is my problem...

When the client places the request, I need to find a way to make the code stop and wait for the response inside the caller function, otherwise I can't imagine other way to return the request result thru the same function that have placed the request.

What have I tried:
1- Make a sync delegate.
     > Works on the idea of blocking the client side process, but then I'll have to wait for the service-side process to wait for the request to be processed from the queue...

2- Make an async delegate.
     > Works a little better than the previous but still I need to wait somewhere on the delegate method on on the callback for the request to be processed from the queue.
     > On this scenario I use the WaitHandle to stop the process, activating the WaitOne method.

3- Regarding the waiting process...
     > As for the waiting process, I'm using a Do : Loop Until (the result value is something other than nothing) but this if a terrible CPU resources trasher...



I'm awhare that this isn't an easy thing, neither to understand or develop, but I really need this to work... need some ideas!

Thank you very much...

Alex :p
Avatar of magpie3d
magpie3d

Rather than have your client "stop", you would be better off by having it go into a "working" mode.  Disable various UI things (buttons, text boxes etc) until your server function has returned.

Using a delegate, and calling it Asynchronously (BeginXxx & EndXxx) would probably be your best bet.  Rather than blocking on the WaitHandle, pass a callback function into the BeginXxx call.  When the function completes, your callback will be called (on a background thread).  In this callback, you can call EndXxx, and retrieve the return value from the function.

Be sure to use Invoke on your form to get back onto the UI thread before you come out of "working" mode, and re-enable your controls.
Avatar of Alexandre Simões

ASKER

Hi...

I've no problem in making an async request to the service, it's really the best way, that's not my problem.
Let me try to explain it better...

1- The client makes the request.
2- The request is passed to a Windows Service (WS) using Remoting (singleton).
3- On the WS, there's a System.Collections.Queue where the requests are queued in line (in-line dequeue must be respected).
4- When processed, the request result must be sent back to the caller user application.

Now, if you think about what will happen between steps 3 and 4. Here's my real problem.

I'll need to stop the request processing, wait for the queue to pick up the request and put it to work again...

The only way I know to do this is using an endless loop based on a bool flag that will interrupt the process until the queue sets it to 'true'.

This wait the CPU will mess the CPU resources, even lowering the process priority it will be allways at 100%...


So, what I really need is a way to 'pause' the delegate processing until the Queue pick it up...


I really hoppe anyone can help me...

Thanks,
Alex
You could add a ManualResetEvent (or an AutoResetEvent) to the request class that you are placing into your Queue, then Wait on it in your service function.

Once it's been Dequeued and processed, you can set the event, which will reawaken the original function, which can then return.

Alternatively, you could implement an "Observer" pattern where the client will register itself with the server, possibly by passing in a reference when it makes the original function call.  Then the service function can add the request to the Queue, and return immediately.  Once the request has been processed, the client reference can be called to notify the client that things have been completed.
Hi...
Sorry for so much time to reply... I was finishing some other pending tasks...

Your suggestion seemed ok, but I'm finding some troubles to put it to work.

I've read this post: https://www.experts-exchange.com/questions/21110775/ThreadPool-in-Page-Load.html?query=ManualResetEvent&clearTAFilter=true
(and some other infos) and tryed to port the idea to my needs and came up to this:

                ManualResetEvent mre;

                //All the queue does when it dequeues a request is call this method, so the process can continue
            public void ContinueProcess(object stateInfo)
            {
                  this.mre.Set();
            }

            public RequestResult Request(AdvMessageQueue.Objects.AdvMessageQueueRemoteObject RemObj)
            {
                  RemObj.Enqueue(this);   //This enqueues the request

                  this.mre.WaitOne();   //This is supposed to make this thread to wait.

                        //When the queue dequeues this request, signals the ManuelResetEvent 'mre',
                        //  witch would continue this code to the ExecuteTask Method.
                  return this.ExecuteTask();
            }

Everything works fine except that when the queue call the ContinueProcess Method, the this.mre.Set() line doesn't conithue the process...
Asolutelly nothing happens, and the client form continues blocked.

I supose that when I call the
this.mre.WaitOne();
I'm giving the current thread a WaitHandle... but where is it? I don't think it's mre...


Thaks for the help!

Alex :p
Yes, mre is the WaitHandle itself.

Does ExecuteTask actually perform the long-running task on the server, or does the thread that Dequeues the object do that?  I'm a little confused that you are Queing the Server class into the AdvMessageQueueRemoteObject that the client is handing to you.  Does the client try to Dequeue the object?

I'll put together a little code snippet for how the Server could look, but if I had a better feeling for what happens in the background thread.  Is AdvMessageQueue a library of your own, or is it a third-party thing?
Here's a possible filling for the server class:

class ServerClass
   {
      private Queue _WorkQueue = Queue.Synchronized(new Queue());

      private void SomeThreadThatWillDequeueTheRequests()
      {
         // do stuff here

         WorkItem workItem = _WorkQueue.Dequeue() as WorkItem;
         if (workItem != null)
         {
            // do the work with it
            // workItem.ClientObject.BlahBlah...

            workItem.WaitHandle.Set();
         }

         // more stuff goes here
      }

      // called by the remote clients
      public RequestResult Request(AdvMessageQueueRemoteObject remObj)
      {
         WorkItem wi = new WorkItem(remObj);

         _WorkQueue.Enqueue(wi);

         wi.WaitHandle.WaitOne();

         return ExecuteTask();
      }

      private class WorkItem
      {
         public AdvMessageQueueRemoteObject ClientObject;
         public ManualResetEvent WaitHandle;

         public WorkItem(AdvMessageQueueRemoteObject remObj)
         {
            ClientObject = remObj;
            WaitHandle = new ManualResetEvent(false);
         }
      }

      private RequestResult ExecuteTask()
      {
         // stuff happens in here
      }
   }

The WorkItem class is just a little helper that allows you to associate the wait handle with the request object.  As your remoting class is set to be a singleton, you shouldn't have your wait handle as a class-level object.  The server can be called by multiple clients, so each request will need to have it's own handle.

This will still block the client call.  It might help to have a better understanding of what you want the flow between the two sides to be, to get the best solution.
I'll explain my need, then my idea, then what I have ok?

THE NEED:
I have a working software platform, from where I build all my applications.
This work just gread, but now I need more control over the processes.
I want to take over users concurrent requests, take over a new need of software licencing, give users a priority to perform some requests to the server over others (users).

THE IDEA:
Take all the current logic on the DB (SQL Server) to a Windows Service (WS).
All users will connect to this WS and all their need will come from there... never directly from the DB or from wherever...
Focusing on the DB access, I need a Queue, on the WS, to the command that will modify the DB (Updates, Inserts & Deletes), Selects are given as requested.

WHAT I HAVE:
Right now, for testing I have:
-> A ServerForm that acts like if it was a Windows Service (no need to register and stuff... just for testing).
When this intiates, registers the singleton remote object, that is the actual object that will handle the queues.
-> A ClientForm that acts like the client application.
When this starts, creates a sync with the singleton object.

Now the Request object it self:
As I have plenty of types of requests, I created an abstract Request type (RequestBase), from where all the request types inherit.
Each Request type "knows" how to perform its taks, it just needs to wait for its time.
So, RequestBase have a non overridable function named 'Request' that returns the request result. This function goes like this:
            public RequestResult Request(AdvMessageQueue.Objects.AdvMessageQueueRemoteObject RemObj)
            {
                  RemObj.Enqueue(this);

                  //Wait for the MQ to pick up this request
                  MREColl = new ManualResetEvent[]{mre};
                  WaitHandle.WaitAny(MREColl);      

                  return this.ExecuteTask();      
            }
As you can see, the Request function enqueues itself on the singleton remote object and stops, waiting for the MQ to pick it up.

When the MQ pick it up, signals 'mre' witch would make the code to jump to the next line: return this.ExecuteTask();
But it doesn't...

Tonight I'll perform some more testings...
As I never used this ManualResetEvent I just don't trust it that much... yet! :P


This is my idea...
If you have anithing else to add, or even to change this all feel free to say ok? :D Please! :p

Thank you very much

Alex :p
Hummmmm...
I think this may be more a Remoting problem than drom the ManualResetEvent...

I'm digging into the damn thing...

Alex :p
Ok, here's a variation of what I posted earlier.  This should keep the general flow of what you want to do.

   class ServerClass
   {
      private Queue _WorkQueue = Queue.Synchronized(new Queue());

      private void SomeThreadThatWillDequeueTheRequests()
      {
         // do stuff here

         WorkItem workItem = _WorkQueue.Dequeue() as WorkItem;
         if (workItem != null)
         {
            // do the work with it
            workItem.ExecuteTask();
            workItem.WaitHandle.Set();
         }

         // more stuff goes here
      }

      // called by the remote clients
      public RequestResult Request(AdvMessageQueueRemoteObject remObj)
      {
         WorkItem wi = new WorkItem(remObj);

         _WorkQueue.Enqueue(wi);

         wi.WaitHandle.WaitOne();

         return wi.Result;
      }

      private class WorkItem
      {
         public RequestResult Result;
         public AdvMessageQueueRemoteObject ClientObject;
         public ManualResetEvent WaitHandle;

         public void ExecuteTask()
         {
            // do what you need to do with the ClientObject
            // Set the value of Result
         }

         public WorkItem(AdvMessageQueueRemoteObject remObj)
         {
            ClientObject = remObj;
            WaitHandle = new ManualResetEvent(false);
         }
      }

   }


The thing to be clear about is which thread things are happening on, and which side of the remoting connection they are happening.  
Hi...
Thanks for your time!

My problem lies on the remothing traffic...
When the remote object tries to signal the ManualResetEvent the following exception is raised:

This remoting proxy has no channel sink which means either the server has no registered server channels that are listening, or this application has no suitable client channel to talk to the server

The remoting channel is working fine (as far as I know) because if I don't use the ManualResetEvent, just ask for something on the RemoteObject, I can return a result to the client.
It just seems I can't signal the ManualResetEvent on the remote object!
It still throws the error if I have a method, on the client, that signals the ManualResetEvent and call it from the remote object...

I just need to be shure that ManualResetEvent can be signaled from a RemoteObject...

Thanks!

Alex :p
It sounds to me like things are getting mixed up as to where you expect them to be running.  Something from the server is trying to do a remote call back to an object on the client.
Damn I'm stressing out with this...

I need to pass the ManualResetEvent to the RemoteObject so it can segnal it right?
If not, I could call a method on the caller client that would do it... but both ways raise errors... :s

Another way I tried was to pass the request itselft to the remoteobject... still a damn error pops up...
Allways on the ManualResetEvent.Set()...

Alex :(
As I understand it, the client submits a request to the server, then waits until the whole request is completed.

The server, when it receives a request, puts it onto a queue then waits for it to be taken off the queue and processed, before returning the result to the client (which has been waiting the whole time).

There needs to be a background thread on the server that will be dequeing things and processing them.

In the function on the server that receives the request, you should associate the ManualResetEvent with the request object, as in my second example.

Nothing should be calling back to the client until the whole process is completed, and the result is returned.

Is that a fair description of the process?
That's exactly what I have...

The problem is that I can't call the mre.Set(), from the remoteObject, without that error I mentioned earlier pops up...

Do I have to create a Channel Sync for this?

I deal with Remoting some quite a while, made a lot of things with it, but this never happened... :s

Despite this, I got the whole general idea, but I think this "new" Framework 1.1 remoting security is messing this up...

Alex :p
I think the problem is that you are running code back on the client, namely "from the remoteObject".  In 1.1 they changed the default behaviour for client-supplied objects to prevent inadvertant callbacks to the client, the error that it produces looks just the same as the one that you are describing.

The flow that I think you are implementing over there is as follows:

Client places request to server.
Server adds request to queue, then waits for it to complete.
Background thread dequeues request, then calls request process function.
Request process function runs ~on the client~ to connect to the DB and do it's thing.
Client tries to set the reset event in the request object.
If things were working, the function on the server would wake up, and return the result to the client.

Try removing all the code that does anything from the request object.  Consider it as just a bag of data for the server.  Make sure that it is [Serializable], and don't inherit from MarshalByRefObject.  Have your dequeing function do any processing, treating the passed object purely as data.


Assuming that you are using a Binary formatter on a TCP channel, you could register your server with remoting like this (using your existing code):

                  BinaryServerFormatterSinkProvider prov = new BinaryServerFormatterSinkProvider();
                  prov.TypeFilterLevel = TypeFilterLevel.Full;
                  Hashtable props = new Hashtable();
                  props["port"] = 3223;
                  TcpChannel channel = new TcpChannel(props,null,prov);

The TypeFilterLevel.Full will allow calling back to the client.  When you register your client with remoting, give it a port number on it's channel rather than using the default constructor.

This will probably make the error go away, but it may not be functioning in the way that you think.  It WON'T be functioning in the way that I described in my previous post, where all the DB work was happening in the server process.  It's more likely that the DB access will actually be happening in the client process.  I suspect that's not what you are aiming for.
I've put together a little sample that I think follows the general flow that you are looking for.  It's at the following link:

http://stuff.crowsnest-design.com/RequestRemotingTest.zip
Hi...

Thank you very much, I'll see it later but as I'll be disconnected the whole weekend I'l get back to you Monday...

Again, thak you very much for your time and effort to help me on this.

Have a nice weekend.

Alex :p
The example I posted before will do what you want (I think).  However, it does have one drawback.  As the client requests come in to the server, a thread from the application's thread pool is being used to service the request.  When we add the request to the queue we go to sleep on that thread, waiting for the request to be serviced.  We only hand the thread back to the pool once the request has been fully processed.  If you have a few clients making connections, you will find that you use up your pool threads quite quickly.

Threadpool threads are intended to perform ~quick~ tasks in the background.  It's not a good idea to be doing lots of processing on them, or sending them to sleep.

This second example works a little differently.  The client submits a request to the server, passing in a reference to a class which will contain a callback function.  The server puts the request into the queue, and returns immediately.  This releases the pool thread back into the pool, and keeps things responsive.

When the server has dequeued the request and processed it, it will call the callback that the client passed in originally to notify the client that things have finished.

http://stuff.crowsnest-design.com/RequestRemotingTestCallback.zip

This will work better than the first example for multiple clients.  It does require that the server will be able to make a connection back to the client, so firewalls may need to be configured appropriately.

Also, if you have multiple clients running on a single machine, you will need to vary the port number that they each use to register with remoting.

Hi!
Thanks for your huge help.

I've been messing around with your code but remain with a problem.
The request result will allways come thru a callback, never thru the SubmitRequest (that is a void funtion in your code).

I'm trying to overcome this, based on your code...

Thanks again...

Alex :p
ASKER CERTIFIED SOLUTION
Avatar of magpie3d
magpie3d

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Man... you're a genious!! eheheheh
I was really stuck on this one...

Now that I got it I don't know how to thank you... :)

As Async call are all retrieved to the same callback, if the client makes several requests at the same time I was't able to destiguish between them to get the result of a specific request...
This way, all requests are retrieved to their caller function, no problems, and still I can make async requests to perform tasks that I don't need to get any values from.

Many many many thanks.

Alex :p
Glad you like it.  =)

With the new client, you can create several instances of it to run more than one request at the same time.  Reusing the same client instance will work as far as the server is concerned, but the results may confuse it, as the client only stores one callback reference.  So any results from the server will be calling that callback.  Using more than one instance of the client class will solve that one.

All the best,
Will