Example #1
0
		public Int32 OpenJob( Connection NewOwner, AgentGuid NewJobGuid )
		{
			// Create the bi-directional link between the job and its owner
			NewOwner.Job = this;
			Owner = NewOwner;
			
			// Determine if the owner is the Instigator, and if so, perform
			// any additional work necessary
			if( NewOwner is LocalConnection )
			{
				OwnerIsInstigator = true;

				// If the owner of the job is the Instigator, set the current working
				// directory to be where the Instigator process is executing
				LocalConnection LocalNewOwner = NewOwner as LocalConnection;
				Manager.SetCurrentDirectoryByProcessID( LocalNewOwner.ProcessID );

				// Update the visualizer
				AgentApplication.UpdateMachineState( Environment.MachineName, -1, EProgressionState.InstigatorConnected );
			}

			// Add to the Agent-wide list of active jobs
			JobGuid = NewJobGuid;
			Manager.ActiveJobs.Add( NewJobGuid, this );

			return Constants.SUCCESS;
		}
 public AgentConfiguration()
 {
     AgentProcessID = -1;
     AgentCachePath = null;
     AgentJobGuid = null;
     IsPureLocalConnection = false;
 }
Example #3
0
        public Int32 GetActiveJobGuid( Int32 ConnectionHandle, ref AgentGuid ActiveJobGuid )
        {
            StartTiming( "GetActiveJobGuid-Internal", true );

            ActiveJobGuid = null;
            Int32 ErrorCode = Constants.INVALID;

            Connection JobOwner;
            if( Connections.TryGetValue( ConnectionHandle, out JobOwner ) )
            {
                if( JobOwner.Job != null )
                {
                    ActiveJobGuid = JobOwner.Job.JobGuid;
                    ErrorCode = Constants.SUCCESS;
                }
            }
            else
            {
                Log( EVerbosityLevel.Critical, ELogColour.Red, "[Job] GetActiveJobGuid: Rejected, unrecognized connection" );
                ErrorCode = Constants.ERROR_CONNECTION_NOT_FOUND;
            }

            StopTiming();
            return ErrorCode;
        }
Example #4
0
        /**
         * Called by a remote Agent, this provides the data for a new channel being pushed
         */
        public bool SendChannel( Int32 ConnectionHandle, string ChannelName, byte[] ChannelData, AgentGuid JobGuid )
        {
            StartTiming( "SendChannel-Internal", true );

            bool bSucceeded = false;

            // Validate the calling connection
            Connection ConnectionRequestingChannel;
            if( Connections.TryGetValue( ConnectionHandle, out ConnectionRequestingChannel ) &&
                ConnectionRequestingChannel is RemoteConnection )
            {
                EChannelFlags ChannelFlags = EChannelFlags.ACCESS_WRITE;
                if( JobGuid == null )
                {
                    ChannelFlags |= EChannelFlags.TYPE_PERSISTENT;
                }
                else
                {
                    ChannelFlags |= EChannelFlags.TYPE_JOB_ONLY;
                }

                Hashtable OpenInParameters = new Hashtable();
                OpenInParameters["Version"] = ESwarmVersionValue.VER_1_0;
                OpenInParameters["ChannelName"] = ChannelName;
                OpenInParameters["ChannelFlags"] = ChannelFlags;
                Hashtable OpenOutParameters = null;

                Int32 LocalChannelHandle = OpenChannel_1_0( ConnectionHandle, OpenInParameters, ref OpenOutParameters );
                if( LocalChannelHandle >= 0 )
                {
                    // Set up the file name
                    string FullChannelName;
                    if( JobGuid == null )
                    {
                        string AgentStagingArea = Path.Combine( AgentApplication.Options.CacheFolder, "AgentStagingArea" );
                        FullChannelName = Path.Combine( AgentStagingArea, ChannelName );
                    }
                    else
                    {
                        string AllJobsFolder = Path.Combine( AgentApplication.Options.CacheFolder, "Jobs" );
                        string ThisJobFolder = Path.Combine( AllJobsFolder, "Job-" + JobGuid.ToString() );
                        FullChannelName = Path.Combine( ThisJobFolder, ChannelName );
                    }

                    // Open the FileStream and write the data directly in
                    FileStream NewChannel = null;
                    Int32 NewChannelBytes = 0;

                    try
                    {
                        NewChannel = new FileStream( FullChannelName, FileMode.Create, FileAccess.Write, FileShare.None );
                        NewChannelBytes = ChannelData.Length;
                        NewChannel.Write( ChannelData, 0, NewChannelBytes );
                        NewChannel.Close();
                    }
                    catch( Exception Ex )
                    {
                        Log( EVerbosityLevel.Informative, ELogColour.Orange, "[SendChannel] Channel \"" + ChannelName + "\" not transferred because of name collision. Channel with that name already exists" );
                        Log( EVerbosityLevel.Verbose, ELogColour.Orange, "[SendChannel] Exception: " + Ex.ToString() );
                    }

                    // Close the channel now that we're done
                    Hashtable CloseInParameters = new Hashtable();
                    CloseInParameters["Version"] = ESwarmVersionValue.VER_1_0;
                    CloseInParameters["ChannelHandle"] = LocalChannelHandle;
                    Hashtable CloseOutParameters = null;

                    if( CloseChannel_1_0( ConnectionHandle, CloseInParameters, ref CloseOutParameters ) >= 0 )
                    {
                        // Success
                        bSucceeded = true;

                        // Track the number of bytes received over the network
                        ConnectionRequestingChannel.NetworkBytesReceived += NewChannelBytes;
                        if( ConnectionRequestingChannel.Job != null )
                        {
                            ConnectionRequestingChannel.Job.NetworkBytesReceived += NewChannelBytes;
                        }
                        if( ConnectionRequestingChannel.Parent != null )
                        {
                            ConnectionRequestingChannel.Parent.NetworkBytesReceived += NewChannelBytes;
                        }
                    }
                }
            }

            StopTiming();
            return bSucceeded;
        }
Example #5
0
 /**
  * Called by a remote Agent, this requests that a specified file is pushed back
  */
 public bool RequestChannel( Int32 ConnectionHandle, string ChannelName, AgentGuid JobGuid )
 {
     // Validate the calling connection
     Connection ConnectionRequestingChannel;
     if( Connections.TryGetValue( ConnectionHandle, out ConnectionRequestingChannel ) &&
         ConnectionRequestingChannel is RemoteConnection )
     {
         // Push it back across to the remote Agent
         return PushChannel( ConnectionRequestingChannel as RemoteConnection, ChannelName, JobGuid );
     }
     return false;
 }
Example #6
0
        /**
         * Pushes a local channel to the remote agent via the remote connection parameter
         * by calling SendChannel on the remote agent
         */
        public bool PushChannel(RemoteConnection Remote, string ChannelName, AgentGuid JobGuid)
        {
            StartTiming("PushChannel-Internal", true);
            bool bChannelTransferred = false;

            // The remote connection's handle can be used for all interaction because
            // it has the same meaning on both ends of the connection
            Int32 ConnectionHandle = Remote.Handle;

            EChannelFlags ChannelFlags = EChannelFlags.ACCESS_READ;
            if (JobGuid == null)
            {
                ChannelFlags |= EChannelFlags.TYPE_PERSISTENT;
            }
            else
            {
                ChannelFlags |= EChannelFlags.TYPE_JOB_ONLY;
            }

            Hashtable OpenInParameters = new Hashtable();
            OpenInParameters["Version"] = ESwarmVersionValue.VER_1_0;
            OpenInParameters["ChannelName"] = ChannelName;
            OpenInParameters["ChannelFlags"] = ChannelFlags;
            Hashtable OpenOutParameters = null;

            Int32 LocalChannelHandle = OpenChannel_1_0(ConnectionHandle, OpenInParameters, ref OpenOutParameters);
            if (LocalChannelHandle >= 0)
            {
                try
                {
                    string FullChannelName;
                    if (JobGuid == null)
                    {
                        FullChannelName = Path.Combine(AgentApplication.Options.CacheFolder, ChannelName);
                    }
                    else
                    {
                        string AllJobsFolder = Path.Combine(AgentApplication.Options.CacheFolder, "Jobs");
                        string ThisJobFolder = Path.Combine(AllJobsFolder, "Job-" + JobGuid.ToString());
                        FullChannelName = Path.Combine(ThisJobFolder, ChannelName);
                    }

                    // Read the entire file into a byte stream
                    byte[] ChannelData = File.ReadAllBytes(FullChannelName);

                    // Send the entire channel at once
                    bChannelTransferred = Remote.Interface.SendChannel(ConnectionHandle, ChannelName, ChannelData, JobGuid);

                    // If the channel was transferred, track the number of bytes that actually moved across the network
                    if (bChannelTransferred)
                    {
                        FileInfo ChannelInfo = new FileInfo(FullChannelName);
                        Remote.NetworkBytesSent += ChannelInfo.Length;
                        if (Remote.Job != null)
                        {
                            Remote.Job.NetworkBytesSent += ChannelInfo.Length;
                        }
                        if (Remote.Parent != null)
                        {
                            Remote.Parent.NetworkBytesSent += ChannelInfo.Length;
                        }
                    }
                }
                catch (Exception Ex)
                {
                    Log(EVerbosityLevel.Verbose, ELogColour.Red, "[PushChannel] Exception message: " + Ex.ToString());
                }

                Hashtable CloseInParameters = new Hashtable();
                CloseInParameters["Version"] = ESwarmVersionValue.VER_1_0;
                CloseInParameters["ChannelHandle"] = LocalChannelHandle;
                Hashtable CloseOutParameters = null;

                CloseChannel_1_0(ConnectionHandle, CloseInParameters, ref CloseOutParameters);

                if (bChannelTransferred)
                {
                    Log(EVerbosityLevel.Verbose, ELogColour.Green, "[Channel] Successful channel push of " + ChannelName);
                }
                else
                {
                    Log(EVerbosityLevel.Verbose, ELogColour.Red, string.Format("[PushChannel] Pushing the channel {0} has failed!", ChannelName));
                }
            }
            else
            {
                Log(EVerbosityLevel.Informative, ELogColour.Red, string.Format("[PushChannel] Cannot open local channel {0}.", ChannelName));
            }

            StopTiming();
            return bChannelTransferred;
        }
Example #7
0
        /**
         * Pulls a remote channel from the remote agent via the remote connection parameter.
         *
         * @param Remote Remote connection.
         * @param ChannelName The name of the file to pull.
         * @param JobGuid A guid of the job.
         * @param RetriesOnFailure How many times should the pull fail until return a failure.
         *
         * @returns True on success. False otherwise.
         */
        public bool PullChannel(RemoteConnection Remote, string ChannelName, AgentGuid JobGuid, int RetriesOnFailure = int.MaxValue)
        {
            StartTiming("PullChannel-Internal", true);

            // The remote connection's handle can be used for all interaction because
            // it has the same meaning on both ends of the connection
            Int32 ConnectionHandle = Remote.Handle;

            // Request the file, which will push it back, and keep doing so until we get it
            // or until the connection is dead, which ever comes first
            bool bChannelTransferred = false;
            int TryId = 0;
            while ((Remote.Interface.IsAlive()) &&
                   (bChannelTransferred == false) &&
                   (TryId < RetriesOnFailure))
            {
                try
                {
                    bChannelTransferred = Remote.Interface.RequestChannel(ConnectionHandle, ChannelName, JobGuid);
                }
                catch (Exception Ex)
                {
                    Log(EVerbosityLevel.Verbose, ELogColour.Red, "[PullChannel] Exception message: " + Ex.ToString());
                }

                if(!bChannelTransferred)
                {
                    Log(EVerbosityLevel.Verbose, ELogColour.Red, string.Format("[PullChannel] Pulling the channel {0} has failed! Retry {1} of {2}.", ChannelName, TryId + 1, RetriesOnFailure));
                }

                ++TryId;
            }

            StopTiming();
            return bChannelTransferred;
        }
Example #8
0
 ///////////////////////////////////////////////////////////////////////////
 /**
  * Standard constructor
  */
 public JobChannel( AgentGuid NewJobGuid, string ChannelName, string FullChannelName, EChannelFlags ChannelFlags )
     : base(ChannelName, FullChannelName, ChannelFlags)
 {
     JobGuid = NewJobGuid;
 }
		///////////////////////////////////////////////////////////////////////////
		
		public Int32 OpenJob(Int32 ConnectionHandle, AgentGuid JobGuid )
		{
			OpenJobDelegate DOpenJob = Connection.OpenJob;
			
			// Set up the versioned hashtable input parameters
			Hashtable InParameters = new Hashtable();
			InParameters["Version"] = ESwarmVersionValue.VER_1_0;
			InParameters["JobGuid"] = JobGuid;
			
			// Invoke the method, then wait for it to finish or to be notified that the connection dropped
			Hashtable OutParameters = null;
			IAsyncResult Result = DOpenJob.BeginInvoke(ConnectionHandle, InParameters, ref OutParameters, null, null);
			WaitHandle.WaitAny(new WaitHandle[]{ Result.AsyncWaitHandle, ConnectionDroppedEvent });
			
			// If the method completed normally, return the result
			if (Result.IsCompleted)
			{
				// If the invocation completed, success
				return DOpenJob.EndInvoke(ref OutParameters, Result);
			}
			// Otherwise, error
			return Constants.ERROR_CONNECTION_DISCONNECTED;
		}
		/*
		 * Generates the full channel name, including Job path and staging area path if necessary
		 */
		String GenerateFullChannelName(String ManagedChannelName, EChannelFlags ChannelFlags)
		{
			if ((ChannelFlags & EChannelFlags.TYPE_PERSISTENT) != 0)
			{
				if ((ChannelFlags & EChannelFlags.ACCESS_WRITE) != 0)
				{
					// A persistent cache channel open for writing opens in the staging area
					String StagingAreaName = Path.Combine(AgentCacheFolder, "AgentStagingArea");
					return Path.Combine(StagingAreaName, ManagedChannelName);
				}
				else if ((ChannelFlags & EChannelFlags.ACCESS_READ) != 0)
				{
					// A persistent cache channel open for reading opens directly in the cache
					return Path.Combine(AgentCacheFolder, ManagedChannelName);
				}
			}
			else if ((ChannelFlags & EChannelFlags.TYPE_JOB_ONLY) != 0)
			{
				AgentGuid JobGuid = ConnectionConfiguration.AgentJobGuid;
				if (JobGuid == null)
				{
					// If there's no Job associated with this connection at this point, provide
					// a default GUID for one for debugging access to the agent cache
					JobGuid = new AgentGuid(0x00000123, 0x00004567, 0x000089ab, 0x0000cdef);
				}

				// A Job Channel opens directly in the Job-specific directory
				String JobsFolder = Path.Combine(AgentCacheFolder, "Jobs");
				String JobFolderName = Path.Combine(JobsFolder, "Job-" + JobGuid.ToString());
				return Path.Combine(JobFolderName, ManagedChannelName);
			}

			return "";
		}
		/**
		 * Adds a Task to the current Job
		 *
		 * @param Specification A structure describing the new Task
		 *
		 * @return Int32 Error code (< 0 is an error)
		 */
		public virtual Int32 AddTask(FTaskSpecification Specification)
		{
			StartTiming("AddTask-Managed", true);

			Int32 ReturnValue = Constants.INVALID;
			if (Connection != null)
			{
				if (ConnectionConfiguration.AgentJobGuid != null)
				{
					// Convert the parameters from native to managed
					AgentGuid TaskGuid = new AgentGuid(Specification.TaskGuid.A,
													   Specification.TaskGuid.B,
													   Specification.TaskGuid.C,
													   Specification.TaskGuid.D);

					String Parameters = Specification.Parameters;

					List<String> Dependencies = null;
					if (Specification.DependencyCount > 0)
					{
						Dependencies = new List<String>();
						for (UInt32 i = 0; i < Specification.DependencyCount; i++)
						{
							Dependencies.Add(Specification.Dependencies[i]);
						}
					}

					AgentTaskSpecification NewSpecification =
						new AgentTaskSpecification(ConnectionConfiguration.AgentJobGuid, TaskGuid, (Int32)Specification.Flags, Parameters, (Int32)Specification.Cost, Dependencies);

					// Ensure all the files are in the cache with the right cache compatible name
					ReturnValue = CacheAllFiles(NewSpecification);
					if (ReturnValue >= 0)
					{
						// Queue up all tasks until the specification is complete and submit them all at once
						PendingTasks.Add(NewSpecification);
					}
				}
				else
				{
					ReturnValue = Constants.ERROR_JOB_NOT_FOUND;
				}
			}
			else
			{
				ReturnValue = Constants.ERROR_CONNECTION_NOT_FOUND;
			}

			StopTiming();
			return ReturnValue;
		}
		/**
		 * Opens a Job session, which allows a Job to be specified, Tasks added, Job
		 * channels opened and used, etc. When the Job is complete and no more Job
		 * related data is needed from the Swarm, call CloseJob.
		 *
		 * @param JobGuid A GUID that uniquely identifies this Job, generated by the caller
		 *
		 * @return Int32 Error code (< 0 is an error)
		 */
		public virtual Int32 OpenJob(FGuid JobGuid)
		{
			StartTiming("OpenJob-Managed", true);

			Int32 ReturnValue = Constants.INVALID;
			if (Connection != null)
			{
				StartTiming("OpenJob-Remote", false);
				try
				{
					AgentGuid ManagedJobGuid = new AgentGuid(JobGuid.A, JobGuid.B, JobGuid.C, JobGuid.D);
					ReturnValue = Connection.OpenJob(ConnectionHandle, ManagedJobGuid);
					if (ReturnValue >= 0)
					{
						// If the call was successful, assign the Job Guid as the active one
						ConnectionConfiguration.AgentJobGuid = ManagedJobGuid;

						// Allocate a new list to collect tasks until the specification is complete
						PendingTasks = new List<AgentTaskSpecification>();
					}
				}
				catch (Exception Ex)
				{
					Log(EVerbosityLevel.Critical, ELogColour.Red, "[Interface:OpenJob] Error: " + Ex.Message);
					ReturnValue = Constants.ERROR_CONNECTION_DISCONNECTED;
					CleanupClosedConnection();
				}
				StopTiming();
			}
			else
			{
				ReturnValue = Constants.ERROR_CONNECTION_NOT_FOUND;
			}

			StopTiming();
			return ReturnValue;
		}
		/**
		 * Sends a message to an Agent (return messages are sent via the FConnectionCallback)
		 *
		 * @param Message The message being sent
		 *
		 * @return Int32 error code (< 0 is error)
		 */
		public virtual Int32 SendMessage(IntPtr NativeMessagePtr)
		{
			StartTiming("SendMessage-Managed", true);

			FMessage NativeMessage = (FMessage)Marshal.PtrToStructure(NativeMessagePtr, typeof(FMessage));

			Int32 ReturnValue = Constants.INVALID;
			if (Connection != null)
			{
				AgentMessage ManagedMessage = null;

				// TODO: As we add additional versions, convert to a switch rather than if-else.
				// For now, just use a simple if since we only have one version and a switch is
				// overkill.
				if (NativeMessage.Version == ESwarmVersionValue.VER_1_0)
				{
					switch (NativeMessage.Type)
					{
						case EMessageType.TASK_REQUEST_RESPONSE:
							// Swallow this message, since it should not be sent along to a local connection
							// since all Job and Task information is contained within the Agent itself
						break;

						case EMessageType.TASK_STATE:
						{
							FTaskState NativeTaskStateMessage = (FTaskState)Marshal.PtrToStructure(NativeMessagePtr, typeof(FTaskState));
							AgentGuid ManagedTaskGuid = new AgentGuid(NativeTaskStateMessage.TaskGuid.A,
																	  NativeTaskStateMessage.TaskGuid.B,
																	  NativeTaskStateMessage.TaskGuid.C,
																	  NativeTaskStateMessage.TaskGuid.D);
							EJobTaskState TaskState = (EJobTaskState)NativeTaskStateMessage.TaskState;
							AgentTaskState ManagedTaskStateMessage = new AgentTaskState(null, ManagedTaskGuid, TaskState);

							ManagedTaskStateMessage.TaskExitCode = NativeTaskStateMessage.TaskExitCode;
							ManagedTaskStateMessage.TaskRunningTime = NativeTaskStateMessage.TaskRunningTime;

							// If there is a message, be sure copy and pass it on
							if (NativeTaskStateMessage.TaskMessage != IntPtr.Zero)
							{
								ManagedTaskStateMessage.TaskMessage = FStringMarshaler.MarshalNativeToManaged(NativeTaskStateMessage.TaskMessage);
							}

							ManagedMessage = ManagedTaskStateMessage;
						}
						break;

						case EMessageType.INFO:
						{
							// Create the managed version of the info message
							FInfoMessage NativeInfoMessage = (FInfoMessage)Marshal.PtrToStructure(NativeMessagePtr, typeof(FInfoMessage));
							AgentInfoMessage ManagedInfoMessage = new AgentInfoMessage();
							if (NativeInfoMessage.TextMessage != IntPtr.Zero)
							{
								ManagedInfoMessage.TextMessage = FStringMarshaler.MarshalNativeToManaged(NativeInfoMessage.TextMessage);
							}
							ManagedMessage = ManagedInfoMessage;
						}
						break;

						case EMessageType.ALERT:
						{
							// Create the managed version of the alert message
							FAlertMessage NativeAlertMessage = (FAlertMessage)Marshal.PtrToStructure(NativeMessagePtr, typeof(FAlertMessage));
							AgentGuid JobGuid = new AgentGuid(NativeAlertMessage.JobGuid.A,
															  NativeAlertMessage.JobGuid.B,
															  NativeAlertMessage.JobGuid.C,
															  NativeAlertMessage.JobGuid.D);
							AgentAlertMessage ManagedAlertMessage = new AgentAlertMessage(JobGuid);
							ManagedAlertMessage.AlertLevel = (EAlertLevel)(NativeAlertMessage.AlertLevel);
							AgentGuid ObjectGuid = new AgentGuid(NativeAlertMessage.ObjectGuid.A,
																 NativeAlertMessage.ObjectGuid.B,
																 NativeAlertMessage.ObjectGuid.C,
																 NativeAlertMessage.ObjectGuid.D);
							ManagedAlertMessage.ObjectGuid = ObjectGuid;
							ManagedAlertMessage.TypeId = NativeAlertMessage.TypeId;
							if (NativeAlertMessage.TextMessage != IntPtr.Zero)
							{
								ManagedAlertMessage.TextMessage = FStringMarshaler.MarshalNativeToManaged(NativeAlertMessage.TextMessage);
							}
							ManagedMessage = ManagedAlertMessage;
						}
						break;

						case EMessageType.TIMING:
						{
							// Create the managed version of the info message
							FTimingMessage NativeTimingMessage = (FTimingMessage)Marshal.PtrToStructure(NativeMessagePtr, typeof(FTimingMessage));
							AgentTimingMessage ManagedTimingMessage = new AgentTimingMessage((EProgressionState)NativeTimingMessage.State, NativeTimingMessage.ThreadNum);
							ManagedMessage = ManagedTimingMessage;
						}
						break;

						default:
							// By default, just pass the message version and type through, but
							// any additional payload of a specialized type will be lost
							ManagedMessage = new AgentMessage((EMessageType)NativeMessage.Type);
						break;
					}
				}

				if (ManagedMessage != null)
				{
					try
					{
						// Finally, send the message to the Agent
						StartTiming("SendMessage-Remote", false);
						Connection.SendMessage(ConnectionHandle, ManagedMessage);
						StopTiming();
						ReturnValue = Constants.SUCCESS;
					}
					catch (Exception Ex)
					{
						Log(EVerbosityLevel.Critical, ELogColour.Red, "[Interface:SendMessage] Error: " + Ex.Message);
						ReturnValue = Constants.ERROR_CONNECTION_DISCONNECTED;
						CleanupClosedConnection();
					}
				}
			}
			else
			{
				ReturnValue = Constants.ERROR_CONNECTION_NOT_FOUND;
			}

			StopTiming();
			return( ReturnValue );
		}
Example #14
0
 public bool SendChannel( Int32 ConnectionHandle, string ChannelName, byte[] ChannelData, AgentGuid JobGuid )
 {
     bool ReturnCode = false;
     if( RemoteInterfaceAlive )
     {
         try
         {
             SendChannelDelegate DSendChannel = new SendChannelDelegate( RemoteInterface.SendChannel );
             IAsyncResult Result = DSendChannel.BeginInvoke( ConnectionHandle, ChannelName, ChannelData, JobGuid, null, null );
             WaitHandle.WaitAny( new WaitHandle[2] { Result.AsyncWaitHandle, RemoteInterfaceDropped } );
             if( Result.IsCompleted )
             {
                 ReturnCode = DSendChannel.EndInvoke( Result );
             }
         }
         catch( Exception )
         {
             SignalConnectionDropped();
         }
     }
     return ReturnCode;
 }
Example #15
0
		public bool RequestChannel( Int32 ConnectionHandle, string ChannelName, AgentGuid JobGuid )
		{
			bool ReturnCode = false;
			if( RemoteInterfaceAlive )
			{
				RetryCatch(
					// Try {
					() =>
					{
						RequestChannelDelegate DRequestChannel = new RequestChannelDelegate(RemoteInterface.RequestChannel);
						IAsyncResult Result = DRequestChannel.BeginInvoke(ConnectionHandle, ChannelName, JobGuid, null, null);
						WaitHandle.WaitAny(new WaitHandle[2] { Result.AsyncWaitHandle, RemoteInterfaceDropped });
						if (Result.IsCompleted)
						{
							ReturnCode = DRequestChannel.EndInvoke(Result);
						}
					},
					// } Catch (Exception) {
					() =>
					{
						SignalConnectionDropped();
					},
					// }
					5
				);
			}
			return ReturnCode;
		}