Search code examples
remoting

.net remoting - Better solution to wait for a service to initialize ?


Context

I have a client application (which i cannot modify, i.e. i only have the binary) that needs to run from time to time external commands that depends on a resource which is very long to initialize (about 20s).

I thus decided to initialize this resource once for all in a "CommandServer.exe" application (single instance in the system tray) and let my client application call an intermediate "ExecuteCommand.exe" program that uses .net remoting to perform the operation on the server. The "ExecuteCommand.exe" is in charge for starting the server on first call and then leave it alive to speed up further commands.

The service:

public interface IMyService
{
    void ExecuteCommand(string[] args);
}

The "CommandServer.exe" (using WindowsFormsApplicationBase for single instance management + user friendly splash screen during resource initializations):

private void onStartupFirstInstance(object sender, StartupEventArgs e)
{
    // Register communication channel
    channel = new TcpServerChannel("CommandServerChannel", 8234);
    ChannelServices.RegisterChannel(channel, false);

    // Register service
    var resource = veryLongToInitialize();
    service = new MyServiceImpl(resource);            
    RemotingServices.Marshal(service, "CommandServer");

    // Create icon in system tray
    notifyIcon = new NotifyIcon();
    ... 
}

The intermediate "ExecuteCommand.exe":

static void Main(string[] args)
{
    startCommandServerIfRequired();

    var channel = new TcpClientChannel();           
    ChannelServices.RegisterChannel(channel, false);

    var service = (IMyService)Activator.GetObject(typeof(IMyService), "tcp://localhost:8234/CommandServer");

    service.RunCommand(args);
}

Problem

As the server is very long to start (about 20s to initialize the required resources), the "ExecuteCommand.exe" fails on service.RunCommand(args) line because the server is yet not available.

Question

Is there a elegant way I can tune the delay before to receive "service not available" when calling service.RunCommand ?

NB1: Currently I'm working around the issue by adding a mutex in server to indicate for complete initiliazation and have "ExecuteCommand.exe" to wait for this mutex before to call service.RunCommand.

NB2: I have no background with .net remoting, nor WCF which is recommended replacer. (I chose .net remoting because this looked easier to set-up for this single shot issue in running external commands).


Solution

  • Found a better solution adding some ManualResetEvent instead of sharing a Mutex between applications:

    Server:

    private void onStartupFirstInstance(object sender, StartupEventArgs e)
    {
        // Event to indicate when server is ready
        serverReadyEvent = new ManualResetEvent(false);
    
          // Register communication channel
          channel = new TcpServerChannel("CommandServerChannel", 8234);
          ChannelServices.RegisterChannel(channel, false);
    
          // Register service (even if not fully initialized)
          var resources = new ServiceResources();
          service = new MyServiceImpl(resources, serverReadyEvent);            
          RemotingServices.Marshal(service, "CommandServer");
    
          // Finalize service initialization
          resources.Initialize();
    
          // Create icon in system tray
          notifyIcon = new NotifyIcon();
          ...
    
        // Indicate server is now ready to process remote commands
        serverReadyEvent.Set();         
    }
    

    Service implementation:

    class MyServiceImpl : MarshalByRefObject, IMyService
    {
        public MyServiceImpl(ServiceResources preAllocatedResources, ManualResetEvent serverReadyEvent)
        {
            this.resources = preAllocatedResources;
            this.serverReadyEvent = serverReadyEvent;    
        }
    
        public override object InitializeLifetimeService()
        {
            return null;
        }
    
        public void ExecuteCommand(string[] args)
        {
            if (!serverReadyEvent.WaitOne(20000))
            {
                throw new TimeoutException("Server did not initialized within expected delay.");
            }
    
            ...                       
        }
    
        private ManualResetEvent serverReadyEvent;
        private ServiceResources resources;
    }