Пример #1
0
        public async Task TestDeadlockOnRemove()
        {
            IEventLoop thread1    = new SingleThreadEventLoop();
            Bootstrap  bootstrap1 = new Bootstrap()
                                    .Channel <LocalChannel>().Group(thread1).LocalAddress(new LocalAddress("#1"));
            IEventLoop thread2    = new SingleThreadEventLoop();
            Bootstrap  bootstrap2 = new Bootstrap()
                                    .Channel <LocalChannel>().Group(thread2).LocalAddress(new LocalAddress("#2"));

            // pool1 runs on thread2, pool2 runs on thread1
            FixedChannelPool pool1 = new FixedChannelPool(bootstrap2, NoopHandler.Instance, 1);
            FixedChannelPool pool2 = new FixedChannelPool(bootstrap1, NoopHandler.Instance, 1);
            var channelPoolMap     = new TestChannelPoolMap1(pool1, pool2);

            Assert.Same(pool1, channelPoolMap.Get("#1"));
            Assert.Same(pool2, channelPoolMap.Get("#2"));

            // thread1 tries to remove pool1 which is running on thread2
            // thread2 tries to remove pool2 which is running on thread1

            var barrier = new Barrier(2);

            var future1 = thread1.SubmitAsync(() =>
            {
                if (!barrier.SignalAndWait(TimeSpan.FromSeconds(1)))
                {
                    throw new TimeoutException();
                }
                channelPoolMap.Remove("#1");
                return(1);
            });

            var future2 = thread2.SubmitAsync(() =>
            {
                if (!barrier.SignalAndWait(TimeSpan.FromSeconds(1)))
                {
                    throw new TimeoutException();
                }
                channelPoolMap.Remove("#2");
                return(2);
            });

            // A blocking close on remove will cause a deadlock here and the test will time out
            try
            {
                var result = await TaskUtil.WaitAsync(future1, TimeSpan.FromSeconds(1));

                if (!result || !future1.IsSuccess())
                {
                    throw new TimeoutException();
                }
                result = await TaskUtil.WaitAsync(future2, TimeSpan.FromSeconds(1));

                if (!result || !future2.IsSuccess())
                {
                    throw new TimeoutException();
                }
            }
            catch (TimeoutException)
            {
                Assert.False(true); // Fail the test on timeout to distinguish from other errors
            }
            finally
            {
                pool1.Close();
                pool2.Close();
                channelPoolMap.Close();
                Shutdown(thread1, thread2);
            }
        }
Пример #2
0
        public async Task TestDeadlockOnAcquire()
        {
            IEventLoop threadA1    = new SingleThreadEventLoop();
            Bootstrap  bootstrapA1 = new Bootstrap()
                                     .Channel <LocalChannel>().Group(threadA1).LocalAddress(new LocalAddress("A1"));
            IEventLoop threadA2    = new SingleThreadEventLoop();
            Bootstrap  bootstrapA2 = new Bootstrap()
                                     .Channel <LocalChannel>().Group(threadA2).LocalAddress(new LocalAddress("A2"));
            IEventLoop threadB1    = new SingleThreadEventLoop();
            Bootstrap  bootstrapB1 = new Bootstrap()
                                     .Channel <LocalChannel>().Group(threadB1).LocalAddress(new LocalAddress("B1"));
            IEventLoop threadB2    = new SingleThreadEventLoop();
            Bootstrap  bootstrapB2 = new Bootstrap()
                                     .Channel <LocalChannel>().Group(threadB2).LocalAddress(new LocalAddress("B2"));

            FixedChannelPool poolA1 = new FixedChannelPool(bootstrapA1, NoopHandler.Instance, 1);
            FixedChannelPool poolA2 = new FixedChannelPool(bootstrapB2, NoopHandler.Instance, 1);
            FixedChannelPool poolB1 = new FixedChannelPool(bootstrapB1, NoopHandler.Instance, 1);
            FixedChannelPool poolB2 = new FixedChannelPool(bootstrapA2, NoopHandler.Instance, 1);

            // Synchronize threads on these barriers to ensure order of execution, first wait until each thread is inside
            // the newPool callbak, then hold the two threads that should lose the match until the first two returns, then
            // release them to test if they deadlock when trying to release their pools on each other's threads.
            Barrier arrivalBarrier = new Barrier(4);
            Barrier releaseBarrier = new Barrier(3);

            var channelPoolMap = new TestChannelPoolMap0(
                threadA1, threadA2, threadB1, threadB2,
                poolA1, poolA2, poolB1, poolB2, arrivalBarrier, releaseBarrier);

            // Thread A1 calls ChannelPoolMap.get(A)
            // Thread A2 calls ChannelPoolMap.get(A)
            // Thread B1 calls ChannelPoolMap.get(B)
            // Thread B2 calls ChannelPoolMap.get(B)

            var futureA1 = threadA1.SubmitAsync(() =>
            {
                return(channelPoolMap.Get("A"));
            });

            var futureA2 = threadA2.SubmitAsync(() =>
            {
                return(channelPoolMap.Get("A"));
            });

            var futureB1 = threadB1.SubmitAsync(() =>
            {
                return(channelPoolMap.Get("B"));
            });

            var futureB2 = threadB2.SubmitAsync(() =>
            {
                return(channelPoolMap.Get("B"));
            });

            // Thread A1 succeeds on updating the map and moves on
            // Thread B1 succeeds on updating the map and moves on
            // These should always succeed and return with new pools
            try
            {
                var result = await TaskUtil.WaitAsync(futureA1, TimeSpan.FromSeconds(1));

                if (!result || !futureA1.IsSuccess())
                {
                    throw new TimeoutException();
                }
                Assert.Same(poolA1, futureA1.Result);

                result = await TaskUtil.WaitAsync(futureB1, TimeSpan.FromSeconds(1));

                if (!result || !futureB1.IsSuccess())
                {
                    throw new TimeoutException();
                }
                Assert.Same(poolB1, futureB1.Result);
            }
            catch (Exception e)
            {
                Shutdown(threadA1, threadA2, threadB1, threadB2);
                throw e;
            }

            // Now release the other two threads which at this point lost the race and will try to clean up the acquired
            // pools. The expected scenario is that both pools close, in case of a deadlock they will hang.
            if (!releaseBarrier.SignalAndWait(TimeSpan.FromSeconds(1)))
            {
                Assert.False(true);
            }

            // Thread A2 fails to update the map and submits close to thread B2
            // Thread B2 fails to update the map and submits close to thread A2
            // If the close is blocking, then these calls will time out as the threads are waiting for each other
            // If the close is not blocking, then the previously created pools will be returned
            try
            {
                var result = await TaskUtil.WaitAsync(futureA2, TimeSpan.FromSeconds(1));

                if (!result || !futureA2.IsSuccess())
                {
                    throw new TimeoutException();
                }
                Assert.Same(poolA1, futureA2.Result);

                result = await TaskUtil.WaitAsync(futureB2, TimeSpan.FromSeconds(1));

                if (!result || !futureB2.IsSuccess())
                {
                    throw new TimeoutException();
                }
                Assert.Same(poolB1, futureB2.Result);
            }
            catch (TimeoutException)
            {
                Assert.False(true); // Fail the test on timeout to distinguish from other errors
                throw;
            }
            finally
            {
                poolA1.Close();
                poolA2.Close();
                poolB1.Close();
                poolB2.Close();
                channelPoolMap.Close();
                Shutdown(threadA1, threadA2, threadB1, threadB2);
            }
        }