Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
StackExchange.Redis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
tsai
StackExchange.Redis
Commits
8349c04f
Commit
8349c04f
authored
Mar 30, 2020
by
mgravell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add option to use the thread-pool for scheduling
parent
6b20e13f
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
111 additions
and
24 deletions
+111
-24
PhysicalBridge.cs
src/StackExchange.Redis/PhysicalBridge.cs
+2
-1
SocketManager.cs
src/StackExchange.Redis/SocketManager.cs
+84
-23
Config.cs
tests/StackExchange.Redis.Tests/Config.cs
+25
-0
No files found.
src/StackExchange.Redis/PhysicalBridge.cs
View file @
8349c04f
...
...
@@ -2,6 +2,7 @@
using
System.Collections.Generic
;
using
System.Diagnostics
;
using
System.IO
;
using
System.IO.Pipelines
;
using
System.Runtime.CompilerServices
;
using
System.Text
;
using
System.Threading
;
...
...
@@ -768,7 +769,7 @@ private bool PushToBacklog(Message message, bool onlyIfExists)
[
MethodImpl
(
MethodImplOptions
.
AggressiveInlining
)]
private
void
StartBacklogProcessor
()
{
var
sched
=
Multiplexer
.
SocketManager
?.
Scheduler
Pool
??
DedicatedThreadPoolPipeScheduler
.
Default
;
var
sched
=
Multiplexer
.
SocketManager
?.
Scheduler
??
PipeScheduler
.
ThreadPool
;
#if DEBUG
_backlogProcessorRequestedTime
=
Environment
.
TickCount
;
#endif
...
...
src/StackExchange.Redis/SocketManager.cs
View file @
8349c04f
...
...
@@ -24,7 +24,7 @@ public sealed partial class SocketManager : IDisposable
/// </summary>
/// <param name="name">The name for this <see cref="SocketManager"/>.</param>
public
SocketManager
(
string
name
)
:
this
(
name
,
DEFAULT_WORKERS
,
fals
e
)
{
}
:
this
(
name
,
DEFAULT_WORKERS
,
SocketManagerOptions
.
Non
e
)
{
}
/// <summary>
/// Creates a new <see cref="SocketManager"/> instance
...
...
@@ -32,7 +32,7 @@ public SocketManager(string name)
/// <param name="name">The name for this <see cref="SocketManager"/>.</param>
/// <param name="useHighPrioritySocketThreads">Whether this <see cref="SocketManager"/> should use high priority sockets.</param>
public
SocketManager
(
string
name
,
bool
useHighPrioritySocketThreads
)
:
this
(
name
,
DEFAULT_WORKERS
,
useHighPrioritySocketThreads
)
{
}
:
this
(
name
,
DEFAULT_WORKERS
,
UseHighPrioritySocketThreads
(
useHighPrioritySocketThreads
)
)
{
}
/// <summary>
/// Creates a new (optionally named) <see cref="SocketManager"/> instance
...
...
@@ -40,11 +40,45 @@ public SocketManager(string name, bool useHighPrioritySocketThreads)
/// <param name="name">The name for this <see cref="SocketManager"/>.</param>
/// <param name="workerCount">the number of dedicated workers for this <see cref="SocketManager"/>.</param>
/// <param name="useHighPrioritySocketThreads">Whether this <see cref="SocketManager"/> should use high priority sockets.</param>
public
SocketManager
(
string
name
=
null
,
int
workerCount
=
0
,
bool
useHighPrioritySocketThreads
=
false
)
public
SocketManager
(
string
name
,
int
workerCount
,
bool
useHighPrioritySocketThreads
)
:
this
(
name
,
workerCount
,
UseHighPrioritySocketThreads
(
useHighPrioritySocketThreads
))
{}
private
static
SocketManagerOptions
UseHighPrioritySocketThreads
(
bool
value
)
=>
value
?
SocketManagerOptions
.
UseHighPrioritySocketThreads
:
SocketManagerOptions
.
None
;
/// <summary>
/// Additional options for configuring the socket manager
/// </summary>
[
Flags
]
public
enum
SocketManagerOptions
{
/// <summary>
/// No additional options
/// </summary>
None
=
0
,
/// <summary>
/// Whether the <see cref="SocketManager"/> should use high priority sockets.
/// </summary>
UseHighPrioritySocketThreads
=
1
<<
0
,
/// <summary>
/// Use the regular thread-pool for all scheduling
/// </summary>
UseThreadPool
=
1
<<
1
,
}
/// <summary>
/// Creates a new (optionally named) <see cref="SocketManager"/> instance
/// </summary>
/// <param name="name">The name for this <see cref="SocketManager"/>.</param>
/// <param name="workerCount">the number of dedicated workers for this <see cref="SocketManager"/>.</param>
/// <param name="options"></param>
public
SocketManager
(
string
name
=
null
,
int
workerCount
=
0
,
SocketManagerOptions
options
=
SocketManagerOptions
.
None
)
{
if
(
string
.
IsNullOrWhiteSpace
(
name
))
name
=
GetType
().
Name
;
if
(
workerCount
<=
0
)
workerCount
=
DEFAULT_WORKERS
;
Name
=
name
;
bool
useHighPrioritySocketThreads
=
(
options
&
SocketManagerOptions
.
UseHighPrioritySocketThreads
)
!=
0
,
useThreadPool
=
(
options
&
SocketManagerOptions
.
UseThreadPool
)
!=
0
;
const
long
Receive_PauseWriterThreshold
=
4L
*
1024
*
1024
*
1024
;
// receive: let's give it up to 4GiB of buffer for now
const
long
Receive_ResumeWriterThreshold
=
3L
*
1024
*
1024
*
1024
;
// (large replies get crazy big)
...
...
@@ -58,21 +92,25 @@ public SocketManager(string name = null, int workerCount = 0, bool useHighPriori
Send_PauseWriterThreshold
/
2
,
defaultPipeOptions
.
ResumeWriterThreshold
);
_schedulerPool
=
new
DedicatedThreadPoolPipeScheduler
(
name
+
":IO"
,
workerCount
:
workerCount
,
priority
:
useHighPrioritySocketThreads
?
ThreadPriority
.
AboveNormal
:
ThreadPriority
.
Normal
);
Scheduler
=
PipeScheduler
.
ThreadPool
;
if
(!
useThreadPool
)
{
Scheduler
=
new
DedicatedThreadPoolPipeScheduler
(
name
+
":IO"
,
workerCount
:
workerCount
,
priority
:
useHighPrioritySocketThreads
?
ThreadPriority
.
AboveNormal
:
ThreadPriority
.
Normal
);
}
SendPipeOptions
=
new
PipeOptions
(
pool
:
defaultPipeOptions
.
Pool
,
readerScheduler
:
_schedulerPool
,
writerScheduler
:
_schedulerPool
,
readerScheduler
:
Scheduler
,
writerScheduler
:
Scheduler
,
pauseWriterThreshold
:
Send_PauseWriterThreshold
,
resumeWriterThreshold
:
Send_ResumeWriterThreshold
,
minimumSegmentSize
:
Math
.
Max
(
defaultPipeOptions
.
MinimumSegmentSize
,
MINIMUM_SEGMENT_SIZE
),
useSynchronizationContext
:
false
);
ReceivePipeOptions
=
new
PipeOptions
(
pool
:
defaultPipeOptions
.
Pool
,
readerScheduler
:
_schedulerPool
,
writerScheduler
:
_schedulerPool
,
readerScheduler
:
Scheduler
,
writerScheduler
:
Scheduler
,
pauseWriterThreshold
:
Receive_PauseWriterThreshold
,
resumeWriterThreshold
:
Receive_ResumeWriterThreshold
,
minimumSegmentSize
:
Math
.
Max
(
defaultPipeOptions
.
MinimumSegmentSize
,
MINIMUM_SEGMENT_SIZE
),
...
...
@@ -80,23 +118,44 @@ public SocketManager(string name = null, int workerCount = 0, bool useHighPriori
}
/// <summary>
/// Default / shared socket manager
/// Default / shared socket manager
using a dedicated thread-pool
/// </summary>
public
static
SocketManager
Shared
{
get
{
var
shared
=
_shared
;
if
(
shared
!=
null
)
return
_
shared
;
var
shared
=
s
_shared
;
if
(
shared
!=
null
)
return
shared
;
try
{
// note: we'll allow a higher max thread count on the shared one
shared
=
new
SocketManager
(
"DefaultSocketManager"
,
DEFAULT_WORKERS
*
2
,
false
);
if
(
Interlocked
.
CompareExchange
(
ref
_shared
,
shared
,
null
)
==
null
)
if
(
Interlocked
.
CompareExchange
(
ref
s_shared
,
shared
,
null
)
==
null
)
shared
=
null
;
}
finally
{
shared
?.
Dispose
();
}
return
Volatile
.
Read
(
ref
s_shared
);
}
}
/// <summary>
/// Shared socket manager using the main thread-pool
/// </summary>
public
static
SocketManager
ThreadPool
{
get
{
var
shared
=
s_threadPool
;
if
(
shared
!=
null
)
return
shared
;
try
{
// note: we'll allow a higher max thread count on the shared one
shared
=
new
SocketManager
(
"ThreadPoolSocketManager"
,
options
:
SocketManagerOptions
.
UseThreadPool
);
if
(
Interlocked
.
CompareExchange
(
ref
s_threadPool
,
shared
,
null
)
==
null
)
shared
=
null
;
}
finally
{
shared
?.
Dispose
();
}
return
Volatile
.
Read
(
ref
_shared
);
return
Volatile
.
Read
(
ref
s_threadPool
);
}
}
...
...
@@ -105,18 +164,19 @@ public static SocketManager Shared
public
override
string
ToString
()
{
var
scheduler
=
SchedulerPool
;
return
$"
scheduler
- queue:
{
scheduler
?.
TotalServicedByQueue
}
, pool:
{
scheduler
?.
TotalServicedByPool
}
"
;
if
(
scheduler
==
null
)
return
Name
;
return
$"
{
Name
}
- queue:
{
scheduler
?.
TotalServicedByQueue
}
, pool:
{
scheduler
?.
TotalServicedByPool
}
"
;
}
private
static
SocketManager
_shared
;
private
static
SocketManager
s_shared
,
s_threadPool
;
private
const
int
DEFAULT_WORKERS
=
5
,
MINIMUM_SEGMENT_SIZE
=
8
*
1024
;
private
DedicatedThreadPoolPipeScheduler
_schedulerPool
;
internal
readonly
PipeOptions
SendPipeOptions
,
ReceivePipeOptions
;
internal
DedicatedThreadPoolPipeScheduler
SchedulerPool
=>
_schedulerPool
;
internal
PipeScheduler
Scheduler
{
get
;
private
set
;
}
internal
DedicatedThreadPoolPipeScheduler
SchedulerPool
=>
Scheduler
as
DedicatedThreadPoolPipeScheduler
;
private
enum
CallbackOperation
{
...
...
@@ -134,8 +194,9 @@ private void Dispose(bool disposing)
// note: the scheduler *can't* be collected by itself - there will
// be threads, and those threads will be rooting the DedicatedThreadPool;
// but: we can lend a hand! We need to do this even in the finalizer
try
{
_schedulerPool
?.
Dispose
();
}
catch
{
}
_schedulerPool
=
null
;
var
tmp
=
SchedulerPool
;
Scheduler
=
PipeScheduler
.
ThreadPool
;
try
{
tmp
?.
Dispose
();
}
catch
{
}
if
(
disposing
)
{
GC
.
SuppressFinalize
(
this
);
...
...
@@ -167,7 +228,7 @@ internal static Socket CreateSocket(EndPoint endpoint)
internal
string
GetState
()
{
var
s
=
_s
chedulerPool
;
var
s
=
S
chedulerPool
;
return
s
==
null
?
null
:
$"
{
s
.
AvailableCount
}
of
{
s
.
WorkerCount
}
available"
;
}
}
...
...
tests/StackExchange.Redis.Tests/Config.cs
View file @
8349c04f
using
System
;
using
System.IO
;
using
System.IO.Pipelines
;
using
System.Linq
;
using
System.Net
;
using
System.Net.Sockets
;
using
System.Security.Authentication
;
using
System.Threading.Tasks
;
using
Pipelines.Sockets.Unofficial
;
using
Xunit
;
using
Xunit.Abstractions
;
...
...
@@ -403,5 +405,28 @@ public void EndpointIteratorIsReliableOverChanges()
Assert
.
Equal
(
8001
,
((
IPEndPoint
)
iter
.
Current
).
Port
);
Assert
.
False
(
iter
.
MoveNext
());
}
[
Fact
]
public
void
ThreadPoolManagerIsDetected
()
{
var
config
=
new
ConfigurationOptions
{
EndPoints
=
{
{
IPAddress
.
Loopback
,
6379
}
},
SocketManager
=
SocketManager
.
ThreadPool
};
using
var
muxer
=
ConnectionMultiplexer
.
Connect
(
config
);
Assert
.
Same
(
PipeScheduler
.
ThreadPool
,
muxer
.
SocketManager
.
Scheduler
);
}
[
Fact
]
public
void
DefaultThreadPoolManagerIsDetected
()
{
var
config
=
new
ConfigurationOptions
{
EndPoints
=
{
{
IPAddress
.
Loopback
,
6379
}
},
};
using
var
muxer
=
ConnectionMultiplexer
.
Connect
(
config
);
Assert
.
Same
(
SocketManager
.
Shared
.
Scheduler
,
muxer
.
SocketManager
.
Scheduler
);
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment