Example #1
0
        public static async Task TaskDropsExecutionContextUponCompletion()
        {
            // Create a finalizable object that'll be referenced by captured ExecutionContext,
            // run a task and wait for it, and then hold on to that task while forcing GCs and finalizers.
            // We want to make sure that holding on to the resulting Task doesn't keep
            // that finalizable object alive.

            var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);

            Task t = null;
            await Task.Run(delegate // avoid any issues with the stack keeping the object alive
            {
                var state = new InvokeActionOnFinalization {
                    Action = () => tcs.SetResult(true)
                };
                var al = new AsyncLocal <object> {
                    Value = state
                };                              // ensure the object is stored in ExecutionContext
                t        = Task.Run(() => { }); // run a task that'll capture EC
                al.Value = null;
            });

            await t; // wait for the task method to complete and clear out its state

            for (int i = 0; i < 2; i++)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }

            await tcs.Task.TimeoutAfter(60_000); // finalizable object should have been collected and finalized

            GC.KeepAlive(t);                     // ensure the object is stored in the state machine
        }
Example #2
0
        public static async Task TaskCompletionSourceDoesntCaptureExecutionContext(Func <TaskCompletionSource <int> > tcsFactory)
        {
            // Create a finalizable object that'll be referenced by captured ExecutionContext,
            // create a TCS, and then hold on to that while forcing GCs and finalizers.
            // We want to make sure that holding on to the resulting TCS doesn't keep
            // that finalizable object alive.

            var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);

            TaskCompletionSource <int> t = null;
            await Task.Run(delegate // avoid any issues with the stack keeping the object alive
            {
                var state = new InvokeActionOnFinalization {
                    Action = () => tcs.SetResult(true)
                };
                var al = new AsyncLocal <object> {
                    Value = state
                };                       // ensure the object is stored in ExecutionContext
                t        = tcsFactory(); // create the TCS that shouldn't capture ExecutionContext
                al.Value = null;
            });

            for (int i = 0; i < 2; i++)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }

            await tcs.Task.TimeoutAfter(60_000); // finalizable object should have been collected and finalized

            GC.KeepAlive(t);                     // ensure the TCS is stored in the state machine
        }
Example #3
0
        public static async Task AsyncMethodsDropsStateMachineAndExecutionContextUponCompletion()
        {
            // Create a finalizable object that'll be referenced by both an async method's
            // captured ExecutionContext and its state machine, invoke the method, wait for it,
            // and then hold on to the resulting task while forcing GCs and finalizers.
            // We want to make sure that holding on to the resulting Task doesn't keep
            // that finalizable object alive.

            var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);

            Task t = null;
            await Task.Run(delegate // avoid any issues with the stack keeping the object alive, and escape xunit sync ctx
            {
                async Task YieldOnceAsync(object s)
                {
                    await Task.Yield();
                    GC.KeepAlive(s); // keep s referenced by the state machine
                }

                var state = new InvokeActionOnFinalization {
                    Action = () => tcs.SetResult(true)
                };
                var al = new AsyncLocal <object>(args => {
                    // Temporary logging to get more info when the test timeout to look who hold a reference to the finalizer object.
                    string currentValue  = args.CurrentValue == null ? "'null'" : "'Object'";
                    string previousValue = args.PreviousValue == null ? "'null'" : "'Object'";
                    Console.WriteLine($"AsyncMethodsDropsStateMachineAndExecutionContextUponCompletion: Thread Id: {Thread.CurrentThread.ManagedThreadId} Current Value: {currentValue}  Previous Value: {previousValue} ThreadContextChanged: {args.ThreadContextChanged}");
                })
                {
                    Value = state
                };                                // ensure the object is stored in ExecutionContext
                t        = YieldOnceAsync(state); // ensure the object is stored in the state machine
                al.Value = null;
            });
Example #4
0
        public static async Task AsyncMethodsDropsStateMachineAndExecutionContextUponCompletion()
        {
            // Create a finalizable object that'll be referenced by both an async method's
            // captured ExecutionContext and its state machine, invoke the method, wait for it,
            // and then hold on to the resulting task while forcing GCs and finalizers.
            // We want to make sure that holding on to the resulting Task doesn't keep
            // that finalizable object alive.

            var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);

            Task t = null;
            await Task.Run(delegate // avoid any issues with the stack keeping the object alive, and escape xunit sync ctx
            {
                async Task YieldOnceAsync(object s)
                {
                    await Task.Yield();
                    GC.KeepAlive(s); // keep s referenced by the state machine
                }

                var state = new InvokeActionOnFinalization {
                    Action = () => tcs.SetResult(true)
                };
                var al = new AsyncLocal <object> {
                    Value = state
                };                                // ensure the object is stored in ExecutionContext
                t        = YieldOnceAsync(state); // ensure the object is stored in the state machine
                al.Value = null;
            });
        public static async Task AsyncMethodsDropsStateMachineAndExecutionContextUponCompletion()
        {
            // Create a finalizable object that'll be referenced by both an async method's
            // captured ExecutionContext and its state machine, invoke the method, wait for it,
            // and then hold on to the resulting task while forcing GCs and finalizers.
            // We want to make sure that holding on to the resulting Task doesn't keep
            // that finalizable object alive.

            var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

            Task t = null;

            Thread runner = new Thread(() =>
            {
                async Task YieldOnceAsync(object s)
                {
                    await Task.Yield();
                    GC.KeepAlive(s); // keep s referenced by the state machine
                }

                var state = new InvokeActionOnFinalization {
                    Action = () => tcs.SetResult()
                };
                var al = new AsyncLocal <object>()
                {
                    Value = state
                };                                // ensure the object is stored in ExecutionContext
                t        = YieldOnceAsync(state); // ensure the object is stored in the state machine
                al.Value = null;
            })
            {
                IsBackground = true
            };

            runner.Start();
            runner.Join();

            await t;            // wait for the async method to complete and clear out its state
            await Task.Yield(); // ensure associated state is not still on the stack as part of the antecedent's execution

            for (int i = 0; i < 2; i++)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }

            try
            {
                await tcs.Task.WaitAsync(TimeSpan.FromSeconds(60));
            }
            catch (Exception e)
            {
                Environment.FailFast("Look at the created dump", e);
            }

            GC.KeepAlive(t); // ensure the object is stored in the state machine
        }
Example #6
0
        public static async Task AsyncMethodsDropsStateMachineAndExecutionContextUponCompletion()
        {
            // Create a finalizable object that'll be referenced by both an async method's
            // captured ExecutionContext and its state machine, invoke the method, wait for it,
            // and then hold on to the resulting task while forcing GCs and finalizers.
            // We want to make sure that holding on to the resulting Task doesn't keep
            // that finalizable object alive.

            bool finalized = false;

            Task t = null;

            Thread runner = new Thread(() =>
            {
                async Task YieldOnceAsync(object s)
                {
                    await Task.Yield();
                    GC.KeepAlive(s); // keep s referenced by the state machine
                }

                var state = new InvokeActionOnFinalization {
                    Action = () => Volatile.Write(ref finalized, true)
                };
                var al = new AsyncLocal <object>()
                {
                    Value = state
                };                                // ensure the object is stored in ExecutionContext
                t        = YieldOnceAsync(state); // ensure the object is stored in the state machine
                al.Value = null;
            })
            {
                IsBackground = true
            };

            runner.Start();
            runner.Join();

            await t;            // wait for the async method to complete and clear out its state
            await Task.Yield(); // ensure associated state is not still on the stack as part of the antecedent's execution

            for (int i = 0; i < 10 && !Volatile.Read(ref finalized); i++)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
                await Task.Yield();
            }

            if (!Volatile.Read(ref finalized))
            {
                Environment.FailFast("Look at the created dump");
            }

            GC.KeepAlive(t); // ensure the object is stored in the state machine
        }
Example #7
0
        public static async Task TaskDropsExecutionContextUponCompletion()
        {
            // Create a finalizable object that'll be referenced by captured ExecutionContext,
            // run a task and wait for it, and then hold on to that task while forcing GCs and finalizers.
            // We want to make sure that holding on to the resulting Task doesn't keep
            // that finalizable object alive.

            var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);

            Task t = null;

            Thread runner = new Thread(() =>
            {
                var state = new InvokeActionOnFinalization {
                    Action = () => tcs.SetResult(true)
                };
                var al = new AsyncLocal <object>()
                {
                    Value = state
                };                              // ensure the object is stored in ExecutionContext
                t        = Task.Run(() => { }); // run a task that'll capture EC
                al.Value = null;
            })
            {
                IsBackground = true
            };

            runner.Start();
            runner.Join();

            await t; // wait for the task method to complete and clear out its state

            for (int i = 0; i < 2; i++)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }

            try
            {
                await tcs.Task.TimeoutAfter(60_000); // finalizable object should have been collected and finalized
            }
            catch (Exception e)
            {
                Environment.FailFast("Look at the created dump", e);
            }

            GC.KeepAlive(t); // ensure the object is stored in the state machine
        }
Example #8
0
        public static async Task TaskDropsExecutionContextUponCompletion()
        {
            // Create a finalizable object that'll be referenced by captured ExecutionContext,
            // run a task and wait for it, and then hold on to that task while forcing GCs and finalizers.
            // We want to make sure that holding on to the resulting Task doesn't keep
            // that finalizable object alive.

            var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);

            Task t = null;
            await Task.Run(delegate // avoid any issues with the stack keeping the object alive
            {
                var state = new InvokeActionOnFinalization {
                    Action = () => tcs.SetResult(true)
                };
                var al = new AsyncLocal <object>(args => {
                    // Temporary logging to get more info when the test timeout to look who hold a reference to the finalizer object.
                    string currentValue  = args.CurrentValue == null ? "'null'" : "'Object'";
                    string previousValue = args.PreviousValue == null ? "'null'" : "'Object'";
                    Console.WriteLine($"TaskDropsExecutionContextUponCompletion: Thread Id: {Thread.CurrentThread.ManagedThreadId} Current Value: {currentValue}  Previous Value: {previousValue} ThreadContextChanged: {args.ThreadContextChanged}");
                })
                {
                    Value = state
                };                              // ensure the object is stored in ExecutionContext
                t        = Task.Run(() => { }); // run a task that'll capture EC
                al.Value = null;
            });

            await t; // wait for the task method to complete and clear out its state

            for (int i = 0; i < 2; i++)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }

            try
            {
                await tcs.Task.TimeoutAfter(60_000); // finalizable object should have been collected and finalized
            }
            catch (Exception e)
            {
                Environment.FailFast("Look at the created dump", e);
            }

            GC.KeepAlive(t); // ensure the object is stored in the state machine
        }
        public static async Task TaskDropsExecutionContextUponCompletion()
        {
            // Create a finalizable object that'll be referenced by captured ExecutionContext,
            // run a task and wait for it, and then hold on to that task while forcing GCs and finalizers.
            // We want to make sure that holding on to the resulting Task doesn't keep
            // that finalizable object alive.

            bool finalized = false;

            Task t = null;

            Thread runner = new Thread(() =>
            {
                var state = new InvokeActionOnFinalization {
                    Action = () => Volatile.Write(ref finalized, true)
                };
                var al = new AsyncLocal <object>()
                {
                    Value = state
                };                              // ensure the object is stored in ExecutionContext
                t        = Task.Run(() => { }); // run a task that'll capture EC
                al.Value = null;
            })
            {
                IsBackground = true
            };

            runner.Start();
            runner.Join();

            await t; // wait for the task method to complete and clear out its state

            for (int i = 0; i < 10 && !Volatile.Read(ref finalized); i++)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }

            if (!Volatile.Read(ref finalized))
            {
                Environment.FailFast("Look at the created dump");
            }

            GC.KeepAlive(t); // ensure the object is stored in the state machine
        }