Sharing objects between application instances using WCF - Page 2
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 2 of 2 FirstFirst 12
Results 16 to 24 of 24

Thread: Sharing objects between application instances using WCF

  1. #16
    Arjay's Avatar
    Arjay is offline Moderator / MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    11,478

    Re: Sharing objects between application instances using WCF

    Your startup code is still incorrect.

    Compare the timings of the following code snippets

    Good
    Code:
    int main(array<System::String ^> ^args)
    {
    	bool mutexWasCreated;
    
    	Console::WriteLine( System::String::Format( L"Starting client: {0}", DateTime::Now ) );
    
    	Mutex^ m = gcnew Mutex( false, "WcfShareTestServerLaunchedEvent", mutexWasCreated );
    
    	// Block other processes until startup has completed
    	m->WaitOne( );
    
    	// Start up the server
    	if( mutexWasCreated )
    	{
    		Console::WriteLine( System::String::Format( L"Starting server - pid: {0}", Process::GetCurrentProcess( )->Id ) );
    
    		// Simulate startup up of WCF server (pause 10 seconds)
    		Thread::Sleep( 10000 );
    	}
    
    	// Allow access to other processes after startup has finished
    	m->ReleaseMutex( );
    
    	Console::WriteLine( System::String::Format( L"Using server - pid: {0}", Process::GetCurrentProcess( )->Id ) );
    	Console::WriteLine( System::String::Format( L"Client has access: {0}", DateTime::Now ) );
    
    
    	Console::ReadLine( );
    
        return 0;
    }
    Bad
    Code:
    int main(array<System::String ^> ^args)
    {
    	bool mutexWasCreated;
    
    	Console::WriteLine( System::String::Format( L"Starting client: {0}", DateTime::Now ) );
    
    	Mutex^ m = gcnew Mutex( true, "WcfShareTestServerLaunchedEvent", mutexWasCreated );
    
    	// Block other processes until startup has completed
    	// m->WaitOne( );
    
    	// Start up the server
    	if( mutexWasCreated )
    	{
    		Console::WriteLine( System::String::Format( L"Starting server - pid: {0}", Process::GetCurrentProcess( )->Id ) );
    
    		// Simulate startup up of WCF server (pause 10 seconds)
    		Thread::Sleep( 10000 );
    	}
    
    	// Allow access to other processes after startup has finished
    	// m->ReleaseMutex( );
    
    	Console::WriteLine( System::String::Format( L"Using server - pid: {0}", Process::GetCurrentProcess( )->Id ) );
    	Console::WriteLine( System::String::Format( L"Client has access: {0}", DateTime::Now ) );
    
    
    	Console::ReadLine( );
    
        return 0;
    }
    Bottom line, is that setting TakeOwnership value to True, isn't sufficient to block the opening of the mutex.

  2. #17
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,594

    Re: Sharing objects between application instances using WCF

    Right, your Bad sample doesn't behave according to the specs, but my code rather behaves like this (or at least is supposed to):

    Code:
    int ServerSimulator()
    {
      bool mutexWasCreated;
    
      Console::WriteLine( System::String::Format( L"Starting server app: {0}", DateTime::Now ) );
    
      Mutex^ m = gcnew Mutex( true, "WcfShareTestServerStartupMutex", mutexWasCreated );
    
      // The mutex is used to protect the server start-up phase only
    
      if( !mutexWasCreated )
      {
        Console::WriteLine( L"Server start-up failed: another server instance is already starting up\n"
          L"right now" );
        m->Close();
        return 1;
      }
    
      EventWaitHandle ^e = gcnew EventWaitHandle(false, EventResetMode::ManualReset, "WcfShareTestServerLaunchedEvent");
    
      // The event is used to indicate a running (i.e. completely started-up) server instance to both clients and
      // other server instances
      // The event is the only server-owned synchronization object that is ever accessed by clients
    
      if (e->WaitOne(0)) {
        Console::WriteLine( L"Server start-up failed: there's already another server instance running that\n"
          L"has finished start-up" );
        e->Close();
        m->ReleaseMutex();
        m->Close();
        return 1;
      }
    
      Console::WriteLine( System::String::Format( L"Starting server - pid: {0}", Process::GetCurrentProcess( )->Id ) );
    
      // Simulate startup up of WCF server (pause 10 seconds)
      Thread::Sleep( 10000 );
    
      // Set the event to indicate a valid running server instance
      e->Set();
    
      // Start-up is finished, so release the start-up mutex
      m->ReleaseMutex( );
    
      m->Close();
    
      // The actual Server::Startup() ends here
    
      Console::WriteLine( System::String::Format( L"Using server - pid: {0}", Process::GetCurrentProcess( )->Id ) );
      Console::WriteLine( System::String::Format( L"Client has access: {0}", DateTime::Now ) );
    
      // Simulate server up time (wait for key press)
      Console::WriteLine( L"Hit <Enter> to shut down server..." );
      Console::ReadLine();
    
      // The following happens in Server::Shutdown()
    
      // Reset the event indicating a running server instance
      e->Reset();
      e->Close();
    
      // Actual WCF service shutdown goes here
    
      Console::WriteLine( L"Server (pid: {0}) was shut down at: {1}", Process::GetCurrentProcess()->Id, DateTime::Now);
    
      return 0;
    }
    
    int main(array<System::String ^> ^args)
    {
      int nRet = ServerSimulator();
      Console::WriteLine ( L"Hit <Enter> to exit..." );
      Console::ReadLine();
      return nRet;
    }
    And here's a screen shot of it, analogous to yours from post #14:
    Attached Images Attached Images  
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  3. #18
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,594

    Re: Sharing objects between application instances using WCF

    Ok, in the meantime I have migrated the real-life app (as described in the OP of the Remoting thread) to the client/server architecture.

    However, I considerably revamped the server design. The reason was that I didn't find anything resembling the functionality of the Server class from the Remoting scenario whose instances can simply be returned to the client and then be "remote controlled" because the class is derived from MarshalByRefObject. One alternative I considered was returning an integral handle to the client that identifies the related server-side object, but that somehow felt a bit too old-school... The next idea was to use that handle, but wrap it with a client-side proxy object. Looked better, but again that would require implementing part of the server functionality on the client side, like I did in the RemotingShareTest solution.

    What I'm currently at is a design that does use a per-client proxy object, but that is located at the server side and remote-instantiated from the client. These objects of type SmtpIdentityProxy refer to reference-counted instances of a class SmtpIdentity (n-to-1 relation) that get created per SMTP server/user name pair. These in turn have some sort of n-to-1 relation to the Server class that's still there, but merely is a private static-members-only class now. It just exposes four methods to the server GUI app, two of which are Startup() and Shutdown() known from earlier in this thread and the Remoting thread, and one of which is rather private to the server class, has only been exposed for diagnostic purposes and is currently unused from outside the server class anyway. I think this design much better fits the WCF architecture.

    Works mostly fine so far. The reusable SMTP identity objects allow a back-to-back mail delivery distance of as little as observed 3 ms, as opposed to the logout/login distance of 300 ms in the former solution. And of course they reduce the stress imposed on the SMTP server by using fewer SMTP sessions which was the primary intention behind this approach.

    However, there still are some multithreading issues remaining, or rather non-multithreading issues. MSDN docs originally made me think that setting SessionMode to Required in the service contract as well as setting InstanceContextMode to PerSession and ConcurrencyMode to Multiple in the ServiceBehaviorAttribute, along with taking care of synchronization in my classes allow instant parallel processing of requests issued by the clients. However, this is not the case: Everything goes on in the context of the server app's GUI thread (managed thread ID 1) except things like decrementing timeout counters and timeout-based destruction of SMTP identity objects (no surprise since this happens in the context of a System::Timers::Timer tick event handler) and (!) destruction of identity proxy instances initiated by calls to ClientBase<ISmtpIdentityProxy ^>::Close() on the client side.

    Is there really no way to achieve parallel handling of requests regarding independent SMTP server connections except explicitly implementing the asynchronous pattern (see http://msdn.microsoft.com/en-us/libr...ncpattern.aspx), IOW may I have overlooked anything obvious? I'm not afraid of actually implementing that, still of course I'd prefer a simpler solution if one is available. For the case I actually need to implement concurrency on my own, I'm currently considering two fundamental approaches:
    • Queue a thread pool execution request for each mail to be submitted and have them compete by synchronization or
    • set up a queue of mails to be submitted myself for each SMTP server connection (IOW SMTP identity object) with a dedicated thread servicing it.

    The first approach may seem simpler at first sight, but it may make it more difficult to make sure all requests on an SMTP identity instance have been serviced and the instance can be shut down cleanly. Any comments on which approach is to be preferred and why is welcome.

    On the side note: This again made me experience that multithreading issues may lead to weird results and be tricky to diagnose. At some point it even made me suspect that VC++ 2010 might not correctly render short-circuit evaluation of boolean expressions in C++/CLI. Of course it actually does that perfectly, but it took me careful examination of some disassembly to prove that. Eventually it turned out that the observed behaviour was due to several timer tick event handler invocations on the thread pool got blocked requesting a lock on a long-locked object, thus piling up and then all got executed at once when the lock was eventually released...
    Last edited by Eri523; October 27th, 2011 at 12:10 PM.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  4. #19
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,594

    Re: Sharing objects between application instances using WCF

    In the past days I have changed the server of the real-life app to implement the asynchronous protocol for the exposed mail sending method (SmtpIdentityProxy::Send()) and the method of SmtpIdentity of the same name and signature to which the calls are simply relayed by the proxy by calling it on an SmtpIdentity member it contains. (I would have been done with that quicker if I hadn't made a tiny but fundamental one-line mistake in the implementation of the asynchronous pattern that took me three or four days to find. I'll certainly never make that mistake again... )

    As the way to fundamentally implement asynchronous functionality on the server side I chose option #4 out of the two options mentioned in my last post: using SmtpClient::SendAsync() instead of merely Send(). This is a rather natural option that I simply failed to consider for some time and which makes the whole thing surprisingly simple. (Option #3 would have been tasks.)

    Another thing I really like about WCF is that it is able to handle interfacing between the synchronous and asynchronous protocols on the client and the server side transparently, so I didn't even need to modify the client to accomodate that design change. (I actually did a few small changes to the client, but not in this respect.)

    Asynchronous sending apparently happens in a separate thread private to the SmtpClient object. When my server gets passed a mail while the SmtpClient instance it is meant for is busy anynchronously sending a previous mail (of which I need to keep track myself, since there's no way to query that state), the new mail is enqueued and later fed to the SmtpClient in its SendCompleted event handler.

    While that strategy pretty much mitigates it by reducing the load on the server's GUI thread, it still doesn't solve the problem of practically any server activity taking place in that thread, as described in my last post. The server runs much smoother now, still the problem of clustered heartbeat handler invocations remains to some extent and causes timeout counters to be off occasionally. However, the worst result of that effect I observed on the new version of the server was a timout meant to be five seconds actually being three seconds, but already that is much better than before, is an exception as I usually observe the timeouts to be reasonably exact, and the code is written so that there's no risk of accidentally destroying an object due to timeout when it's actually still in use. Currently I'm thinking about a mechanism to detect and report congestions on the heartbeat ticks without the need to log each invocation of the tick handler which pretty much clutters the log file. (I had that kind of logging for some time but the single line responsible for that is currently commented out.)

    Another thing, unrelated to the above: The behavior I observed in the SimulStart.log file of mysteriously starting just a single server instance when simultaneously starting two client instances is a curious exceptional behavior I still have no idea of why it happens. The common case actually is, as initially expected, that two server instances get started, only one of which will win. Ok, so I changed the server to die silently if it's the loser in such a launch collision. I implemented that by simply calling Close() in the main form's constructor in case of a collision (i.e. if Server::Startup() returned false). It took some time to realize that this actually causes a problem: An ObjectDisposedException gets thrown when, as I interpret the stack trace, something (not in my code) attempts to set the main form's visibility though the form's window handle has never been created. That was rather sublime because in the absence of an existing main form, the exception doesn't pop up on the screen, but instead silently gets posted to the Windows event log.

    Any suggestion on how to resolve that the best way? I may postpone the call to Close() until the window handle actually has been created, probably in the form's Load event handler. OTOH I'd like to have the superfluous server instance terminate as early as possible. Is there a way to do that in the form's constructor without raising an exception and in a not so hackish way?

    Finally, I'm currently uncertain about the responsibility for closing the synchronization object returned by IAsyncResult::AsyncWaitHandle. The code sample on http://msdn.microsoft.com/en-us/libr...yncresult.aspx seems to suggest that closing it is the responsibility of the code calling my asynchronous interface (here: the WCF infrastructure). But what if the caller doesn't make use of that object at all, which seems to be rather common from the code I've seen all around? An idea might be to not even create the object before it gets requested by a call to that property's get accessor. However, that may become complicated if I also use the object internally, which I do in my code.
    Last edited by Eri523; November 2nd, 2011 at 10:49 PM.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  5. #20
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,594

    Re: Sharing objects between application instances using WCF

    Some debugging, showing information that, due to implementation details of the logger, couldn't be seen in the log file, reveiled that calling Form::Close() from within the form's c'tor really was a big-time silly idea. That leads to an implicit and of course synchronous call to the d'tor, and shortly after that, the c'tor returns as if nothing exciting would have happened. Not hard to imagine that this leads to desastrous consequences... I fixed that by moving the call to Server::Startup() and the call to Close() to the form's Load event handler, as outlined in my previous post. That increases the lifetime of a server that fails start-up from around 50 ms to around 100 ms, but, honestly, no big deal either, so what?

    I also was able to remedy the problem of lacking multithreading. That problem was due to the fact that I instantiated the ServiceHost on the server's GUI thread, which (of course) is attributed [STAThread], which AFAIK is mandatory for the Windows Forms infrastructure to work. I fixed it by creating a dedicated thread with ApartmentState::MTA. That thread's worker function now contains the core parts of both the start-up and shut-down code (though, in particular regarding the start-up, that is not the majority of code lines), with a WaitOne() on a newly introduced shut-down event in between. As an appreciated side effect this also allowed me to improve encapsulation a bit by making some important objects local to the worker function that previously were static class members to allow access from both the start-up and shut-down code.

    Now everything is beautifully multithreaded, and the modification wasn't even really that much effort, since the server code had already been designed with thread-safety in mind before anyway. Now having the server-side handling of mail sending requests implemented using the asynchronous protocol and SmtpClient::SendAsync() may be overkill, but, OTOH, now that I already have it that way, there's no relevant reason to change that back to the synchronous protocol either, is there?

    Instantiating the ServiceHost on an MTA thread again was something I simply failed to consider for some time. That was due to the fact that MSDN claims it wouldn't make a difference anyway as long as I'm not using COM, in conjunction with my belief that I am not actually using COM. Now the facts seem to prove that either MSDN's claim of irrellevance is wrong or I actually am using COM which would mean that under the hood WCF uses a COM-based implementation (at least in conjunction with the netNamedPipeBinding).

    Eventual it looks like I figured it all out. So unless someone comes along and posts something along the lines of "OMG, what are you doing!?", I'll probably mark the thread resolved in a week or so.
    Last edited by Eri523; November 3rd, 2011 at 07:11 PM.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  6. #21
    Arjay's Avatar
    Arjay is offline Moderator / MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    11,478

    Re: Sharing objects between application instances using WCF

    Quote Originally Posted by Eri523 View Post
    I'll probably mark the thread resolved in a week or so.
    I wouldn't be so hasty. Give it some time.

  7. #22
    Join Date
    Jul 2002
    Posts
    2,526

    Re: Sharing objects between application instances using WCF

    Quote Originally Posted by Eri523 View Post
    I'll probably mark the thread resolved in a week or so.
    No!!! Don't do this! Plzzzzzzzzzzzzzz!!!

  8. #23
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,594

    Re: Sharing objects between application instances using WCF

    Oooh... You guys are kidding, aren't you? My "OMG, what are you doing!?" was (of course) meant along the lines of "Oh, what a crappy code you're writing/using there!?". Perhaps I should choose my words more carefully - especially around midnight...

    At any rate, your wish is my command. Here's some more to ruminate about...

    As my last post indicated, I felt I was almost done with this, merely facing a few minor issues I could sort out quickly. One of those was that, on the client side, I just get thrown a naked ServiceModel::FaultException (as opposed to a FaultException<TDetail>) in response to an exception getting thrown on the server side while processing a request issued by the client. These exceptions always carry the same sterotypical message along the lines of "The server couldn't process the request due to an internal error. Please activate includeExceptionDetailInFaults in the servive behavior if you want to get more information". However, I'm quite sure I do have activated that, just like in the test project attached to post #11, where that works like a charm. In fact, the real-life app's server app.config mostly is a modified copy of the one from the test project.

    I didn't mind that too much yet because the stereotypic message is enough to indicate that something went wrong on the server side at all, and the server catches exeptions resulting from the actual mail sending attempt, writes them to the log file and finally rethrows them, so I can look them up later. However, now that I was going to fix it, this seemingly harmless issue turned out to actually be big-time stubborn. I have examined the four app.config files (for the client and the server from both the real-life app and the test project) with an XML editor, Notepad++ and the Service Configuration Editor for hours but still wasn't able to spot what's wrong.

    I have some gut feeling that I actually overlooked something really basic. Perhaps a fresh pair of eyes can see more than I can at the moment, maybe even pretty quickly. So here's the real-life app's server app.config file:

    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <appSettings>
        <!-- ... -->
      </appSettings>
      <system.net>
        <mailSettings>
          <!-- ... -->
        </mailSettings>
      </system.net>
      <system.serviceModel>
        <services>
          <service name="esendmailsvr.ISmtpIdentityProxy" behaviorConfiguration="debug">
            <endpoint address="net.pipe://localhost/esendmailsvr"
                      binding="netNamedPipeBinding"
                      contract="ISmtpIdentityProxy"
                      name="esendmailsvr.ISmtpIdentityProxy" />
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="debug">
              <serviceDebug includeExceptionDetailInFaults="true" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    </configuration>
    ... and the one of the client:

    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <appSettings>
            <!-- ... -->
        </appSettings>
        <system.net>
            <mailSettings>
                <!-- ... -->
            </mailSettings>
        </system.net>
        <system.serviceModel>
            <client>
                <endpoint binding="netNamedPipeBinding"
                    contract="ISmtpIdentityProxy" name="esendmailsvr.ISmtpIdentityProxy"
                    address="net.pipe://localhost/esendmailsvr" />
            </client>
        </system.serviceModel>
    </configuration>
    And, for reference, the config files from the test project where the marshalling of exceptions from the server to the client works perfectly. Compared to the versions of these files from the last version of the test project I attached, the server config has just one additional line in the appSettings section that is certainly unrelated to WCF, and anyway, it has worked with that line as well as without it. The client config is absolutely identical to the attached one.

    For the server:

    Code:
    <configuration>
      <appSettings>
        <add key="logfile" value="WcfShareTest.log" />
        <add key="dontFailSilentlyOnLaunchCollision" value="true" />
      </appSettings>
      <system.serviceModel>
        <services>
          <service name="TestServer.Server" behaviorConfiguration="debug">
            <endpoint address="net.pipe://localhost/TestServer"
                      binding="netNamedPipeBinding"
                      contract="TestServer.ITestServer" />
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="debug">
              <serviceDebug includeExceptionDetailInFaults="true" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    </configuration>
    ... and the client:

    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <appSettings>
            <add key="logfile" value="WcfShareTest.log" />
        </appSettings>
        <system.serviceModel>
            <client>
                <endpoint binding="netNamedPipeBinding"
                    contract="TestServer.ITestServer" name="DefaultBinding_ITestServer_ITestServer" address="net.pipe://localhost/TestServer" />
            </client>
        </system.serviceModel>
    </configuration>
    I'm pretty much out of ideas regarding this at the moment. Do you have some? I'm pretty sure the problem wasn't caused by the recent changes regarding multithreading I described in my last post or the change to the asynchronous protocol on the server side, since it already has been present long before. The only remarkable and perhaps relevant difference I can think of at the moment is that the test client is a Windows Forms app while the real-life client is a console app. However, I don't see how that may influence the behavior of the WCF infrastructure in the way observed.

    Aside from that, I think the program could probably benefit by some fine-tuning of the WCF timeout and buffer size settings. It has worked fine with the defaults so far, but none of the test mails I sent was larger than 20 kB and it never were more than four (or perhaps six) of them at a time. However, before I do that, I need to thoroughly read up on these settings again and envision not only how they interact with each other, but also with the time outs I'm maintaining myself.
    Last edited by Eri523; November 25th, 2011 at 08:08 AM.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  9. #24
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,594

    Re: Sharing objects between application instances using WCF

    In the meantime, while looking for a way to determine at runtime which behavior configuration the service actually is using, I discovered that the ServiceBehaviorAttribute has an IncludeExceptionDetailInFaults property as well. Setting that to true on my service activated marshaling of exceptions to the client as originally intended, proving BTW that this problem was in no way related to the client and the fact that it's a console app.

    However, on mid-term, I'll most probaly implement a fault contract anyway, since the ExceptionDetail class doesn't convey some specifics of the SMTP faults like the status code or the list of failed recipients, which of course are of interest to the client, and I learned that implementing that probably won't be that difficult either. Other exceptions than SmtpException or derived classes are of no interest to the client since they either indicate an internal failure of the server or a server failure that has been caused by the client improperly interacting with the server, which of course shouldn't happen in the first place.

    I still would love to know why the server keeps ignoring the behavior configuration set up in the app.config file, though...
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Page 2 of 2 FirstFirst 12

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center