void Send(PopulateMethod populateMethod) { while (true) { var directBuf = new DirectBuffer(); var completed = populateMethod(ref directBuf); _connector.SendBuffer(); if (completed) break; // Sent all messages // The following is an optimization hack for writing large byte arrays without passing // through our buffer if (directBuf.Buffer != null) { _connector.WriteBuffer.DirectWrite(directBuf.Buffer, directBuf.Offset, directBuf.Size == 0 ? directBuf.Buffer.Length : directBuf.Size); directBuf.Buffer = null; directBuf.Size = 0; } if (_writeStatementIndex > 0) { // We've send all the messages for the first statement in a multistatement command. // If we continue blocking writes for the rest of the messages, we risk a deadlock where // PostgreSQL sends large results for the first statement, while we're sending large // parameter data for the second. To avoid this, switch to async sends. // See #641 RemainingSendTask = SendRemaining(populateMethod, CancellationToken.None); return; } } }
/// <summary> /// This method is used to asynchronously sends all remaining protocol messages for statements /// beyond the first one, and *without* waiting for the send to complete. This technique is /// used to avoid the deadlock described in #641 by allowing the user to read query results /// while at the same time sending messages for later statements. /// </summary> async Task SendRemaining(PopulateMethod populateMethod, CancellationToken cancellationToken) { Contract.Requires(_writeStatementIndex > 0); try { while (true) { var directBuf = new DirectBuffer(); var completed = populateMethod(ref directBuf); await _connector.SendBufferAsync(cancellationToken).ConfigureAwait(false); if (completed) return; // Sent all messages // The following is an optimization hack for writing large byte arrays without passing // through our buffer if (directBuf.Buffer != null) { await _connector.WriteBuffer.DirectWriteAsync(directBuf.Buffer, directBuf.Offset, directBuf.Size == 0 ? directBuf.Buffer.Length : directBuf.Size, cancellationToken ).ConfigureAwait(false); directBuf.Buffer = null; directBuf.Size = 0; } } } catch (Exception e) { Log.Error("Exception while asynchronously sending remaining messages", e, _connector.Id); } }
/// <summary> /// Populates the specified pool with the specified number of objects, so that they do not need instantiating later. /// </summary> /// <param name="poolName">The name of the pool to populate.</param> /// <param name="quantity">The number of objects to populate it with.</param> /// <param name="method">The population mode.</param> public void Populate(string poolName, int quantity, PopulateMethod method = PopulateMethod.Set) { GetPool(poolName).Populate(quantity, method); }