Skip to content

Commit

Permalink
Move local function to member function
Browse files Browse the repository at this point in the history
This change allows the PerformanceSensitiveAttribute to be applied to
the method. See dotnet/csharplang#1888.
  • Loading branch information
sharwell committed Jun 6, 2019
1 parent 91f9de9 commit 3a6e02c
Showing 1 changed file with 43 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ internal partial class SQLitePersistentStorage
// Get's the task representing the current writes being performed by another
// thread for this queue+key, and a TaskCompletionSource we can use to let
// other threads know about our own progress writing any new writes in this queue.
var (previousWritesTask, taskCompletionSource) = await GetWriteTaskAsync(_writeQueueGate, keyToWriteActions, keyToWriteTask, key, writesToProcess, cancellationToken).ConfigureAwait(false);
var (previousWritesTask, taskCompletionSource) = await GetWriteTaskAsync(keyToWriteActions, keyToWriteTask, key, writesToProcess, cancellationToken).ConfigureAwait(false);
try
{
// Wait for all previous writes to be flushed.
Expand Down Expand Up @@ -117,61 +117,57 @@ internal partial class SQLitePersistentStorage
// to proceed.
taskCompletionSource?.TrySetResult(0);
}
}

return;

// Local functions
//[PerformanceSensitive("https://github.com/dotnet/roslyn/issues/36114", OftenCompletesSynchronously = true)]
static async ValueTask<(Task previousTask, TaskCompletionSource<int> taskCompletionSource)> GetWriteTaskAsync(
SemaphoreSlim gate,
MultiDictionary<TKey, Action<SqlConnection>> keyToWriteActions,
Dictionary<TKey, Task> keyToWriteTask,
TKey key,
ArrayBuilder<Action<SqlConnection>> writesToProcess,
CancellationToken cancellationToken)
[PerformanceSensitive("https://github.com/dotnet/roslyn/issues/36114", OftenCompletesSynchronously = true)]
private async ValueTask<(Task previousTask, TaskCompletionSource<int> taskCompletionSource)> GetWriteTaskAsync<TKey>(
MultiDictionary<TKey, Action<SqlConnection>> keyToWriteActions,
Dictionary<TKey, Task> keyToWriteTask,
TKey key,
ArrayBuilder<Action<SqlConnection>> writesToProcess,
CancellationToken cancellationToken)
{
// Have to acquire the semaphore. We're going to mutate the shared 'keyToWriteActions'
// and 'keyToWriteTask' collections.
//
// Note: by blocking on _writeQueueGate we are guaranteed to see all the writes
// performed by FlushAllPendingWritesAsync.
using (await _writeQueueGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
// Have to acquire the semaphore. We're going to mutate the shared 'keyToWriteActions'
// and 'keyToWriteTask' collections.
//
// Note: by blocking on _writeQueueGate we are guaranteed to see all the writes
// performed by FlushAllPendingWritesAsync.
using (await gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
// Get the writes we need to process.
// Note: explicitly foreach so we operate on the struct enumerator for
// MultiDictionary.ValueSet.
var actions = keyToWriteActions[key];
writesToProcess.EnsureCapacity(writesToProcess.Count + actions.Count);
foreach (var action in actions)
{
// Get the writes we need to process.
// Note: explicitly foreach so we operate on the struct enumerator for
// MultiDictionary.ValueSet.
var actions = keyToWriteActions[key];
writesToProcess.EnsureCapacity(writesToProcess.Count + actions.Count);
foreach (var action in actions)
{
writesToProcess.Add(action);
}
writesToProcess.Add(action);
}

// and clear them from the queues so we don't process things multiple times.
keyToWriteActions.Remove(key);
// and clear them from the queues so we don't process things multiple times.
keyToWriteActions.Remove(key);

// Find the existing task responsible for writing to this queue.
var existingWriteTask = keyToWriteTask.TryGetValue(key, out var task)
? task
: Task.CompletedTask;
// Find the existing task responsible for writing to this queue.
var existingWriteTask = keyToWriteTask.TryGetValue(key, out var task)
? task
: Task.CompletedTask;

if (writesToProcess.Count == 0)
{
// We have no writes of our own. But there may be an existing task that
// is writing out this queue. Return this so our caller can wait for
// all existing writes to complete.
return (previousTask: existingWriteTask, taskCompletionSource: null);
}
if (writesToProcess.Count == 0)
{
// We have no writes of our own. But there may be an existing task that
// is writing out this queue. Return this so our caller can wait for
// all existing writes to complete.
return (previousTask: existingWriteTask, taskCompletionSource: null);
}

// Create a TCS that represents our own work writing out "writesToProcess".
// Store it in keyToWriteTask so that if other threads come along, they'll
// wait for us to complete before doing their own reads/writes on this queue.
var localCompletionSource = new TaskCompletionSource<int>();
// Create a TCS that represents our own work writing out "writesToProcess".
// Store it in keyToWriteTask so that if other threads come along, they'll
// wait for us to complete before doing their own reads/writes on this queue.
var localCompletionSource = new TaskCompletionSource<int>();

keyToWriteTask[key] = localCompletionSource.Task;
keyToWriteTask[key] = localCompletionSource.Task;

return (previousTask: existingWriteTask, taskCompletionSource: localCompletionSource);
}
return (previousTask: existingWriteTask, taskCompletionSource: localCompletionSource);
}
}

Expand Down

0 comments on commit 3a6e02c

Please sign in to comment.