Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
da9aaf8e
R
roslyn
项目概览
lwm1986
/
roslyn
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
roslyn
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
da9aaf8e
编写于
5月 02, 2017
作者:
C
CyrusNajmabadi
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Simplify how we batch write to our persistence service.
上级
13a3bb52
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
47 addition
and
82 deletion
+47
-82
src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage.Accessor.cs
...ktop/Workspace/SQLite/SQLitePersistentStorage.Accessor.cs
+6
-5
src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs
...Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs
+41
-77
未找到文件。
src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage.Accessor.cs
浏览文件 @
da9aaf8e
...
...
@@ -30,11 +30,12 @@ private abstract class Accessor<TKey, TWriteQueueKey, TDatabaseId>
new
MultiDictionary
<
TWriteQueueKey
,
Action
<
SqlConnection
>>();
/// <summary>
/// Keep track of how many threads are trying to write out this particular queue. All threads
/// trying to write out the queue will wait until all the writes are done.
/// The task responsible for writing out all the batched actions we have for a particular
/// queue. When new reads come in for that queue they can 'await' this write-task completing
/// so that all reads for the queue observe any previously completed writes.
/// </summary>
private
readonly
Dictionary
<
TWriteQueueKey
,
CountdownEvent
>
_writeQueueKeyToCountdown
=
new
Dictionary
<
TWriteQueueKey
,
CountdownEvent
>();
private
readonly
Dictionary
<
TWriteQueueKey
,
Task
>
_writeQueueKeyToWriteTask
=
new
Dictionary
<
TWriteQueueKey
,
Task
>();
public
Accessor
(
SQLitePersistentStorage
storage
)
{
...
...
@@ -119,7 +120,7 @@ public async Task<Stream> ReadStreamAsync(TKey key, CancellationToken cancellati
private
Task
FlushPendingWritesAsync
(
SqlConnection
connection
,
TKey
key
,
CancellationToken
cancellationToken
)
=>
Storage
.
FlushSpecificWritesAsync
(
connection
,
_writeQueueKeyToWrites
,
_writeQueueKeyTo
Countdown
,
GetWriteQueueKey
(
key
),
cancellationToken
);
connection
,
_writeQueueKeyToWrites
,
_writeQueueKeyTo
WriteTask
,
GetWriteQueueKey
(
key
),
cancellationToken
);
private
Task
AddWriteTaskAsync
(
TKey
key
,
Action
<
SqlConnection
>
action
,
CancellationToken
cancellationToken
)
=>
Storage
.
AddWriteTaskAsync
(
_writeQueueKeyToWrites
,
GetWriteQueueKey
(
key
),
action
,
cancellationToken
);
...
...
src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs
浏览文件 @
da9aaf8e
...
...
@@ -2,7 +2,6 @@
using
System
;
using
System.Collections.Generic
;
using
System.Diagnostics
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Microsoft.CodeAnalysis.SQLite.Interop
;
...
...
@@ -52,14 +51,14 @@ internal partial class SQLitePersistentStorage
private
async
Task
FlushSpecificWritesAsync
<
TKey
>(
SqlConnection
connection
,
MultiDictionary
<
TKey
,
Action
<
SqlConnection
>>
keyToWriteActions
,
Dictionary
<
TKey
,
CountdownEvent
>
keyToCountdown
,
Dictionary
<
TKey
,
Task
>
keyToWriteTask
,
TKey
key
,
CancellationToken
cancellationToken
)
{
var
writesToProcess
=
ArrayBuilder
<
Action
<
SqlConnection
>>.
GetInstance
();
try
{
await
FlushSpecificWritesAsync
(
connection
,
keyToWriteActions
,
keyTo
Countdown
,
key
,
writesToProcess
,
cancellationToken
).
ConfigureAwait
(
false
);
connection
,
keyToWriteActions
,
keyTo
WriteTask
,
key
,
writesToProcess
,
cancellationToken
).
ConfigureAwait
(
false
);
}
finally
{
...
...
@@ -69,19 +68,28 @@ internal partial class SQLitePersistentStorage
private
async
Task
FlushSpecificWritesAsync
<
TKey
>(
SqlConnection
connection
,
MultiDictionary
<
TKey
,
Action
<
SqlConnection
>>
keyToWriteActions
,
Dictionary
<
TKey
,
CountdownEvent
>
keyToCountdown
,
TKey
key
,
Dictionary
<
TKey
,
Task
>
keyToWriteTask
,
TKey
key
,
ArrayBuilder
<
Action
<
SqlConnection
>>
writesToProcess
,
CancellationToken
cancellationToken
)
{
// Many threads many be trying to flush a specific queue. If some other thread
// beats us to writing this queue, we want to still wait until it is down. To
// accomplish that, we use a countdown that effectively states how many current
// writers there are, and which only lets us past once all the concurrent writers
// say they are done.
CountdownEvent
countdown
;
// Note: by blocking on _writeQueueGate we are guaranteed to see all the writes
// performed by FlushAllPendingWrites.
// Get the task that is responsible for doing the writes for this queue.
// This task will complete when all previously enqueued writes for this queue
// complete, and all the currently enqueued writes for this queue complete as well.
var
writeTask
=
await
GetWriteTask
(
connection
,
keyToWriteActions
,
keyToWriteTask
,
key
,
writesToProcess
,
cancellationToken
).
ConfigureAwait
(
false
);
await
writeTask
.
ConfigureAwait
(
false
);
}
private
async
Task
<
Task
>
GetWriteTask
<
TKey
>(
SqlConnection
connection
,
MultiDictionary
<
TKey
,
Action
<
SqlConnection
>>
keyToWriteActions
,
Dictionary
<
TKey
,
Task
>
keyToWriteTask
,
TKey
key
,
ArrayBuilder
<
Action
<
SqlConnection
>>
writesToProcess
,
CancellationToken
cancellationToken
)
{
// Have to acqure the semaphore. We're going to mutate the shared 'keyToWriteActions' and
// 'keyToWriteTask' collections.
using
(
await
_writeQueueGate
.
DisposableWaitAsync
(
cancellationToken
).
ConfigureAwait
(
false
))
{
// Get the writes we need to process.
...
...
@@ -90,76 +98,32 @@ internal partial class SQLitePersistentStorage
// and clear them from the queues so we don't process things multiple times.
keyToWriteActions
.
Remove
(
key
);
//
We may have acquired _writeQueueGate between the time that an existing thread
// completes the "Wait" below and grabs this lock. If that's the case, let go
// of the countdown associated with this key as it is no longer usable.
RemoveCountdownIfComplete
(
keyToCountdown
,
key
)
;
//
Find the existing task responsible for writing to this queue.
var
existingWriteTask
=
keyToWriteTask
.
TryGetValue
(
key
,
out
var
task
)
?
task
:
SpecializedTasks
.
EmptyTask
;
// See if there's an existing countdown keeping track of the number of writers
// writing this queue.
if
(!
keyToCountdown
.
TryGetValue
(
key
,
out
countdown
))
if
(
writesToProcess
.
Count
==
0
)
{
// We
're the first writer for this queue. Set the count to one, and keep
// i
t around so future concurrent writers will see it.
countdown
=
new
CountdownEvent
(
initialCount
:
1
);
keyToCountdown
.
Add
(
key
,
countdown
)
;
// We
have no writes of our own. But there may be an existing task that
// i
s writing out this queue. Return this so our caller can wait for
// all existing writes to complete.
return
existingWriteTask
;
}
else
{
// If there is, increment the count to indicate that we're writing as well.
countdown
.
AddCount
();
}
Debug
.
Assert
(
countdown
.
CurrentCount
>=
1
);
}
// Now actually process any writes we found for this queue.
ProcessWriteQueue
(
connection
,
writesToProcess
);
// Mark that we're done writing out this queue, and wait until all other writers
// for this queue are done. Note: this needs to happen in the lock so that
// changes to the countdown value are observed consistently across all threads.
bool
lastSignal
;
using
(
await
_writeQueueGate
.
DisposableWaitAsync
(
cancellationToken
).
ConfigureAwait
(
false
))
{
lastSignal
=
countdown
.
Signal
();
}
// Don't proceed until all concurrent writers of this queue complete.
countdown
.
Wait
();
// If we're the thread that finally got the countdown to zero, then dispose of this
// count down and remove it from the dictionary (if it hasn't already been replaced
// by the next request).
if
(
lastSignal
)
{
Debug
.
Assert
(
countdown
.
CurrentCount
==
0
);
// We have our own writes to process. Enqueue the task to write
// these out after the existing write-task for this queue completes.
var
nextTask
=
existingWriteTask
.
ContinueWith
(
_
=>
ProcessWriteQueue
(
connection
,
writesToProcess
),
cancellationToken
,
TaskContinuationOptions
.
RunContinuationsAsynchronously
,
TaskScheduler
.
Default
);
// Safe to call outside of lock. Countdown is only given out to a set of threads
// that have incremented it. And we can only get here once all the threads have
// been allowed to get past the 'Wait' point. Only one of those threads will
// have lastSignal set to true, so we'll only dispose this once.
countdown
.
Dispose
();
// Store this for the next flush call to see.
keyToWriteTask
[
key
]
=
nextTask
;
using
(
await
_writeQueueGate
.
DisposableWaitAsync
(
cancellationToken
).
ConfigureAwait
(
false
))
{
// Remove the countdown if it's still in the dictionary. It may not be if
// another thread came in after this batch of threads completed, and it
// removed the completed countdown already.
RemoveCountdownIfComplete
(
keyToCountdown
,
key
);
}
}
}
private
void
RemoveCountdownIfComplete
<
TKey
>(
Dictionary
<
TKey
,
CountdownEvent
>
keyToCountdown
,
TKey
key
)
{
Debug
.
Assert
(
_writeQueueGate
.
CurrentCount
==
0
);
if
(
keyToCountdown
.
TryGetValue
(
key
,
out
var
tempCountDown
)
&&
tempCountDown
.
CurrentCount
==
0
)
{
keyToCountdown
.
Remove
(
key
);
// And return this to our caller so it can 'await' all these writes completing.
return
nextTask
;
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录