/// <summary> /// Wait for completion of the request and return the result (or throw an exception if there was a failure). /// </summary> /// <typeparam name="T">Data type of the expected response result for the call (Table[], Table, /// SingleRowTable[], SingleRowTable, T[][], T[] or T, with T one of the supported value types).</typeparam> /// <param name="asyncResponse">The execution handle for the request</param> /// <returns>The request's response</returns> internal static AsyncResponse <T> EndExecute <T>(AsyncResponse <T> asyncResponse) { // Wait until completion (Note: timeout control is already managed by the background thread, so we do not // specify a(nother!) timeout on the WaitHandle). if (!asyncResponse.IsCompleted) { asyncResponse.AsyncWaitHandle.WaitOne(); } // If there was an error, throw it right out. if (asyncResponse.Status != ResponseStatus.Success) { throw asyncResponse.Exception; } // Return the response. return(asyncResponse); }
/// <summary> /// Cancels the request associated to this execution, causing triggerring of the Asynchronous callback with a /// VoltClientAbortException result. Understand that this process merely lets you decide to "forget" about /// the request, however, the execution has been posted to the server and will be completed all the same. /// </summary> /// <param name="asyncResponse">The execution handle for the request</param> private void ExecuteCancelAsync(AsyncResponse <TResult> asyncResponse) { asyncResponse.Cancel(); }
/// <summary> /// Asynchronously execute a procedure against a VoltDB database, returning immediately to the calling thread. /// The provided callback will be fired upon completion. /// </summary> /// <typeparam name="T">Data type of the expected response result for the call (Table[], Table, /// SingleRowTable[], SingleRowTable, T[][], T[] or T, with T one of the supported value types).</typeparam> /// <param name="callback">The callback method to call upon completion.</param> /// <param name="state">A user-defined state object to be passed to your callback through the Response's /// .AsyncState property</param> /// <param name="timeout">Timeout value (overrides connection settings DefaultCommandTimeout). Use /// Timeout.Infinite or -1 for infinite timeout.</param> /// <param name="procedure">The name of the procedure to call.</param> /// <param name="procedureUtf8">The UTF-8 bytes of the procedure name.</param> /// <param name="parameters">List of parameters to pass to the procedure.</param> /// <returns>The execution handle for the request.</returns> public override AsyncResponse <T> BeginExecute <T>( ExecuteAsyncCallback <T> callback , object state , int timeout , string procedure , byte[] procedureUtf8 , params object[] parameters ) { // Correct default timeout usage. if (timeout == 0) { timeout = this.Settings_DefaultCommandTimeout; } else if (timeout < -1) { throw new ArgumentOutOfRangeException(string.Format(Resources.InvalidTimeoutValue, timeout)); } // Validate connection status. if (this.Status != ConnectionStatus.Connected) { if (this.TerminalException != null) { throw this.TerminalException; } else { throw new InvalidOperationException(string.Format(Resources.InvalidConnectionStatus, this.Status)); } } // Assign execution id. long executionId = Interlocked.Increment(ref this.ExecutionId); // Prepare the response object. AsyncResponse <T> response = new AsyncResponse <T>(this, executionId, timeout, callback, state, procedure, parameters); // Attempt asynchronouse execution - any failure will trigger a synchronous failure. try { // This might fail (invalid parameters / exceeding max string length, for instance) - the equivalent of an // ArgumentException, so we leave it outside of the try/catch block related to protecting ourselves against // connectivity issues. var message = GetProcedureCallMessage(executionId, procedureUtf8, parameters); // Block if we reached queue capacity. while (this.ExecutionCache.Size >= this.Settings_MaxOutstandingTxns) { Thread.Sleep(1); } // The request appears to be properly formatted and ready to ship - push it in the queue. this.ExecutionCache.AddItem(response); // Write out execution request to protocol stream - lock out access for thread safety around the underlying // stream. lock (this.SyncRoot) { try { this.BaseStream.WriteMessage(message.Array, message.Offset, message.Count); } catch (Exception x) { // We will only get here in case of a network/connection failure. // Swallow any exception: the background processing thread will pick up a network failure as well // and we will let it initiate termination and kick out the abort on the request just posted. // We still set the Terminal exception so we report the *first* exception encountered (failing to // write on the stream here, in case the background thread picks things up first, failing to read). this.TerminalException = new VoltExecutionException(Resources.ConnectionClosedDuringAWrite, x); // But, as stated, we do not re-throw: connection termination will ensure that all pending requests // are rejected with an "abort" exception. The next execution call will fail directly on a // "connection closed" error, or be blocked until recovery is complete. } } // Track statistics as needed. TrackStatisticsOpenRequest(procedure, message.Count); // Trace as needed. if (this.Settings_TraceEnabled) { VoltTrace.TraceEvent( TraceEventType.Information , VoltTraceEventType.ExecutionStarted , Resources.TraceExecutionStarted , this.ServerHostId , this.ConnectionId , executionId , procedure ); } } catch (Exception x) { response.OnExecutionRequestFailed(new VoltExecutionException(Resources.RequestExecutionFailure, x)); } // Return execution handle to caller. return(response); }