-
Notifications
You must be signed in to change notification settings - Fork 0
async multithreading support #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v4
Are you sure you want to change the base?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| #if NET45 | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Runtime.CompilerServices; | ||
| using System.Runtime.Remoting.Messaging; | ||
| using System.Runtime.Serialization; | ||
|
|
||
| namespace StackExchange.Profiling.Helpers.Net45 | ||
| { | ||
| /// <summary> | ||
| /// Implements interface similar to AsyncLocal which is not available until .Net 4.6. | ||
| /// | ||
| /// The implementation is inspired by Ambient Context Logic from here: | ||
| /// https://github.com/mehdime/DbContextScope/blob/master/Mehdime.Entity/Implementations/DbContextScope.cs | ||
| /// Unlike DbContextScope's implementation this implementation doesn't support serialization/deserialization | ||
| /// ths not allowing to cross app domain barrier. | ||
| /// </summary> | ||
| internal class AsyncLocal<T> | ||
| where T : class | ||
| { | ||
| public AsyncLocal() | ||
| { | ||
| } | ||
|
|
||
| public AsyncLocal( | ||
| T obj | ||
| ) | ||
| { | ||
| Value = obj; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets or Sets the value. | ||
| /// </summary> | ||
| public T Value | ||
| { | ||
| get | ||
| { | ||
| return (CallContext.LogicalGetData(_id) as ObjectRef)?.Ref; | ||
| } | ||
|
|
||
| set | ||
| { | ||
| CallContext.LogicalSetData(_id, new ObjectRef { Ref = value }); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Identifies this instance of <see cref="AsyncLocal"/> in CallContext. | ||
| /// </summary> | ||
| private readonly string _id = Guid.NewGuid().ToString(); | ||
|
|
||
| [Serializable] | ||
| private class ObjectRef : MarshalByRefObject, ISerializable | ||
| { | ||
| // The special constructor is used to deserialize values. | ||
| public ObjectRef() | ||
| { | ||
| } | ||
|
|
||
| // The special constructor is used to deserialize values. | ||
| public ObjectRef( | ||
| SerializationInfo info, | ||
| StreamingContext context | ||
| ) | ||
| { | ||
| } | ||
|
|
||
| public void GetObjectData(SerializationInfo info, StreamingContext context) | ||
| { | ||
| } | ||
|
|
||
| public T Ref { get; set; } | ||
| } | ||
| } | ||
| } | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -135,10 +135,9 @@ public Timing Root | |
| { | ||
| var timing = timings.Pop(); | ||
|
|
||
| if (timing.HasChildren) | ||
| var children = timing.Children; | ||
| if (children?.Count > 0) | ||
| { | ||
| var children = timing.Children; | ||
|
|
||
| for (int i = children.Count - 1; i >= 0; i--) | ||
| { | ||
| children[i].ParentTiming = timing; | ||
|
|
@@ -189,7 +188,19 @@ public Timing Root | |
| /// <summary> | ||
| /// Gets or sets points to the currently executing Timing. | ||
| /// </summary> | ||
| public Timing Head { get; set; } | ||
| public Timing Head | ||
| { | ||
| get | ||
| { | ||
| Settings.EnsureProfilerProvider(); | ||
| return Settings.ProfilerProvider.CurrentHead; | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is possible that the current thread spawns more than one task on separate threads and wait for them. As a result technically it is possible that there would be more than one separate head for a miniprofiler instance (one for each spawned thread). This code offloads managing heads to the profiler provider, so if the provider handles spawning threads (i.e. it relies on AsyncLocal or CallContext) then it should be able to manage head properly. |
||
| } | ||
| set | ||
| { | ||
| Settings.EnsureProfilerProvider(); | ||
| Settings.ProfilerProvider.CurrentHead = value; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the ticks since this MiniProfiler was started. | ||
|
|
@@ -329,9 +340,9 @@ public IEnumerable<Timing> GetTimingHierarchy() | |
|
|
||
| yield return timing; | ||
|
|
||
| if (timing.HasChildren) | ||
| var children = timing.Children; | ||
| if (children?.Count > 0) | ||
| { | ||
| var children = timing.Children; | ||
| for (int i = children.Count - 1; i >= 0; i--) timings.Push(children[i]); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,8 @@ | ||
| #if NET45 | ||
| using System; | ||
| using System.Runtime.Remoting.Messaging; | ||
| using System.Web; | ||
| using System.Web; | ||
| using StackExchange.Profiling.Helpers.Net45; | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since there is AsyncLocal polyfill we can keep the implementation of the provider the same - it will be either using the polyfill or .net version of AsyncLocal |
||
| #else | ||
| using System.Threading; | ||
| #endif | ||
|
|
@@ -13,43 +14,23 @@ namespace StackExchange.Profiling | |
| /// </summary> | ||
| public class DefaultProfilerProvider : BaseProfilerProvider | ||
| { | ||
| #if NET45 | ||
| const string ContextKey = ":miniprofiler:"; | ||
| private readonly AsyncLocal<MiniProfiler> _profiler = new AsyncLocal<MiniProfiler>(); | ||
| private readonly AsyncLocal<Timing> _currentTiming = new AsyncLocal<Timing>(); | ||
|
|
||
| private MiniProfiler Profiler | ||
| { | ||
| get | ||
| { | ||
| if (HttpContext.Current != null) | ||
| { | ||
| return HttpContext.Current?.Items[ContextKey] as MiniProfiler; | ||
| } | ||
| else | ||
| { | ||
| return CallContext.LogicalGetData(ContextKey) as MiniProfiler; | ||
| } | ||
| } | ||
| set | ||
| { | ||
| if (HttpContext.Current != null) | ||
| { | ||
| HttpContext.Current.Items[ContextKey] = value; | ||
| } | ||
| else | ||
| { | ||
| CallContext.LogicalSetData(ContextKey, value); | ||
| } | ||
| } | ||
| get { return _profiler.Value; } | ||
| set { _profiler.Value = value; } | ||
| } | ||
| #else | ||
| private AsyncLocal<MiniProfiler> _profiler = new AsyncLocal<MiniProfiler>(); | ||
|
|
||
| private MiniProfiler Profiler | ||
| /// <summary> | ||
| /// Current head timing. | ||
| /// </summary> | ||
| public override Timing CurrentHead | ||
| { | ||
| get { return _profiler.Value; } | ||
| set { _profiler.Value = value; } | ||
| get { return _currentTiming.Value; } | ||
| set { _currentTiming.Value = value; } | ||
| } | ||
| #endif | ||
|
|
||
| /// <summary> | ||
| /// The name says it all. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a polyfill for AsyncLocal which is not available before .Net 4.6