{"action":"created","comment":{"url":"https://api.github.com/repos/netty/netty/pulls/comments/232098915","pull_request_review_id":173219588,"id":232098915,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzMjA5ODkxNQ==","diff_hunk":"@@ -0,0 +1,133 @@\n+/*\n+ * Copyright 2018 The Netty Project\n+ *\n+ * The Netty Project licenses this file to you under the Apache License,\n+ * version 2.0 (the \"License\"); you may not use this file except in compliance\n+ * with the License. You may obtain a copy of the License at:\n+ *\n+ *   http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n+ * License for the specific language governing permissions and limitations\n+ * under the License.\n+ */\n+\n+package io.netty.buffer;\n+\n+import io.netty.util.ResourceLeakDetector;\n+import io.netty.util.ResourceLeakTracker;\n+\n+import java.io.Closeable;\n+import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.List;\n+\n+import static io.netty.util.internal.StringUtil.NEWLINE;\n+\n+/**\n+ * A {@link ByteBufAllocator} for unit testing.  This allocator performs leak tracking\n+ * on all allocated buffers.  At the end the test, call {@link #close} to assert\n+ * that all buffers have been freed.\n+ */\n+public class TestingPooledByteBufAllocator extends PooledByteBufAllocator implements Closeable {\n+\n+    private final TestingResourceLeakDetector leakDetector = new TestingResourceLeakDetector();\n+\n+    public TestingPooledByteBufAllocator() {\n+    }\n+\n+    public TestingPooledByteBufAllocator(boolean preferDirect) {\n+        super(preferDirect);\n+    }\n+\n+    @Override\n+    ByteBuf toLeakAwareBufferInternal(ByteBuf buf) {\n+        ResourceLeakTracker<ByteBuf> leak = leakDetector.track(buf);\n+        if (leak != null) {\n+            buf = new AdvancedLeakAwareByteBuf(buf, leak);\n+        }\n+        return buf;\n+    }\n+\n+    @Override\n+    CompositeByteBuf toLeakAwareBufferInternal(CompositeByteBuf buf) {\n+        ResourceLeakTracker<ByteBuf> leak = leakDetector.track(buf);\n+        if (leak != null) {\n+            buf = new AdvancedLeakAwareCompositeByteBuf(buf, leak);\n+        }\n+        return buf;\n+    }\n+\n+    /**\n+     * Throws an exception if all allocated buffers have been not freed.\n+     * @throws AssertionError if buffers have been leaked.\n+     */\n+    @Override\n+    public void close() {\n+        List<String> allLeaks = leakDetector.getAllLeaks();\n+        if (!allLeaks.isEmpty()) {\n+            throwLeakDetected(allLeaks);\n+        }\n+    }\n+\n+    private static void throwLeakDetected(List<String> allLeaks) {\n+        int expectedBufferSize = 1024;\n+        for (String leak : allLeaks) {\n+            expectedBufferSize += leak.length();\n+        }\n+\n+        StringBuilder buf = new StringBuilder(expectedBufferSize)\n+                .append(\"LEAK: \")\n+                .append(allLeaks.size())\n+                .append(\" leak(s) detected. \")\n+                .append(\"ByteBuf.release() was not called before it's garbage-collected. \")\n+                .append(\"See http://netty.io/wiki/reference-counted-objects.html for more information.\");\n+        for (int i = 0; i < allLeaks.size(); i++) {\n+            String leak = allLeaks.get(i);\n+            buf.append(NEWLINE)\n+                    .append(\"== ByteBuf \")\n+                    .append(i + 1)\n+                    .append(\": \")\n+                    .append(leak);\n+        }\n+        buf.append(NEWLINE)\n+                .append(\"== Leak detection:\");\n+        throw new AssertionError(buf.toString());\n+    }\n+\n+    private static class TestingResourceLeakDetector extends ResourceLeakDetector<ByteBuf> {\n+\n+        private final List<String> leaks = new ArrayList<String>(0);\n+\n+        public TestingResourceLeakDetector() {\n+            super(ByteBuf.class);\n+        }\n+\n+        @Override\n+        protected boolean shouldTrack(ByteBuf obj) {\n+            return true;\n+        }\n+\n+        @Override\n+        protected boolean isLeakReportingEnabled() {\n+            return true;\n+        }\n+\n+        @Override\n+        protected synchronized void reportTracedLeak(String resourceType, String records) {","path":"buffer/src/main/java/io/netty/buffer/TestingPooledByteBufAllocator.java","position":119,"original_position":119,"commit_id":"720f223ba8b30bf7ddf20254660628615fcb605e","original_commit_id":"720f223ba8b30bf7ddf20254660628615fcb605e","user":{"login":"carl-mastrangelo","id":8943572,"node_id":"MDQ6VXNlcjg5NDM1NzI=","avatar_url":"https://avatars0.githubusercontent.com/u/8943572?v=4","gravatar_id":"","url":"https://api.github.com/users/carl-mastrangelo","html_url":"https://github.com/carl-mastrangelo","followers_url":"https://api.github.com/users/carl-mastrangelo/followers","following_url":"https://api.github.com/users/carl-mastrangelo/following{/other_user}","gists_url":"https://api.github.com/users/carl-mastrangelo/gists{/gist_id}","starred_url":"https://api.github.com/users/carl-mastrangelo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/carl-mastrangelo/subscriptions","organizations_url":"https://api.github.com/users/carl-mastrangelo/orgs","repos_url":"https://api.github.com/users/carl-mastrangelo/repos","events_url":"https://api.github.com/users/carl-mastrangelo/events{/privacy}","received_events_url":"https://api.github.com/users/carl-mastrangelo/received_events","type":"User","site_admin":false},"body":"Just use ConcurrentLinkedQueue?","created_at":"2018-11-08T23:33:47Z","updated_at":"2018-11-08T23:44:49Z","html_url":"https://github.com/netty/netty/pull/8423#discussion_r232098915","pull_request_url":"https://api.github.com/repos/netty/netty/pulls/8423","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/netty/netty/pulls/comments/232098915"},"html":{"href":"https://github.com/netty/netty/pull/8423#discussion_r232098915"},"pull_request":{"href":"https://api.github.com/repos/netty/netty/pulls/8423"}}},"pull_request":{"url":"https://api.github.com/repos/netty/netty/pulls/8423","id":224857818,"node_id":"MDExOlB1bGxSZXF1ZXN0MjI0ODU3ODE4","html_url":"https://github.com/netty/netty/pull/8423","diff_url":"https://github.com/netty/netty/pull/8423.diff","patch_url":"https://github.com/netty/netty/pull/8423.patch","issue_url":"https://api.github.com/repos/netty/netty/issues/8423","number":8423,"state":"open","locked":false,"title":"Add testing ByteBufAllocator for leak unit testing","user":{"login":"dain","id":58298,"node_id":"MDQ6VXNlcjU4Mjk4","avatar_url":"https://avatars0.githubusercontent.com/u/58298?v=4","gravatar_id":"","url":"https://api.github.com/users/dain","html_url":"https://github.com/dain","followers_url":"https://api.github.com/users/dain/followers","following_url":"https://api.github.com/users/dain/following{/other_user}","gists_url":"https://api.github.com/users/dain/gists{/gist_id}","starred_url":"https://api.github.com/users/dain/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dain/subscriptions","organizations_url":"https://api.github.com/users/dain/orgs","repos_url":"https://api.github.com/users/dain/repos","events_url":"https://api.github.com/users/dain/events{/privacy}","received_events_url":"https://api.github.com/users/dain/received_events","type":"User","site_admin":false},"body":"Motivation:\r\n\r\nCurrently the only way to detect leaks during testing is log scanning which is\r\ndifficult to automate.  As an alternative it would be nice to fail unit tests\r\nthat leak buffers.\r\n\r\nModifications:\r\n\r\nAdded try with resources friendly TestingPooledByteBufAllocator which throws\r\nan AssertionError in close if all buffers have not been freed.  To implement\r\nthis, I modified modified ResourceLeakDetector to allow sub clases to force\r\ntracking and error reporting regardless of the global static tracking level,\r\nor the configuration of the logging system.  I also added a method to allow\r\nsub classes to dispose of all tracked references, which triggers reporting\r\nif the referant was not freed. Then I modified, AbstractByteBufAllocator and\r\nPooledByteBufAllocator to allow package local sub classes to hook the call to\r\nthe global static toLeakAwareBuffer methods.  Finally, I added\r\nTestingPooledByteBufAllocator which is a subclass of PooledByteBufAllocator\r\ncontaining a private ResourceLeakDetector that always tracks and warns on\r\nleaks.\r\n\r\nResult:\r\n\r\nDevelopers using Netty can now easily verify that all buffers have been\r\nreleased in unit tests.\r\n","created_at":"2018-10-23T00:48:00Z","updated_at":"2018-11-08T23:44:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f851e0ee99c58ac7c52f6f8066de1212db2a5f1a","assignee":null,"assignees":[],"requested_reviewers":[{"login":"trustin","id":173918,"node_id":"MDQ6VXNlcjE3MzkxOA==","avatar_url":"https://avatars0.githubusercontent.com/u/173918?v=4","gravatar_id":"","url":"https://api.github.com/users/trustin","html_url":"https://github.com/trustin","followers_url":"https://api.github.com/users/trustin/followers","following_url":"https://api.github.com/users/trustin/following{/other_user}","gists_url":"https://api.github.com/users/trustin/gists{/gist_id}","starred_url":"https://api.github.com/users/trustin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trustin/subscriptions","organizations_url":"https://api.github.com/users/trustin/orgs","repos_url":"https://api.github.com/users/trustin/repos","events_url":"https://api.github.com/users/trustin/events{/privacy}","received_events_url":"https://api.github.com/users/trustin/received_events","type":"User","site_admin":false},{"login":"ejona86","id":2811396,"node_id":"MDQ6VXNlcjI4MTEzOTY=","avatar_url":"https://avatars2.githubusercontent.com/u/2811396?v=4","gravatar_id":"","url":"https://api.github.com/users/ejona86","html_url":"https://github.com/ejona86","followers_url":"https://api.github.com/users/ejona86/followers","following_url":"https://api.github.com/users/ejona86/following{/other_user}","gists_url":"https://api.github.com/users/ejona86/gists{/gist_id}","starred_url":"https://api.github.com/users/ejona86/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ejona86/subscriptions","organizations_url":"https://api.github.com/users/ejona86/orgs","repos_url":"https://api.github.com/users/ejona86/repos","events_url":"https://api.github.com/users/ejona86/events{/privacy}","received_events_url":"https://api.github.com/users/ejona86/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/netty/netty/pulls/8423/commits","review_comments_url":"https://api.github.com/repos/netty/netty/pulls/8423/comments","review_comment_url":"https://api.github.com/repos/netty/netty/pulls/comments{/number}","comments_url":"https://api.github.com/repos/netty/netty/issues/8423/comments","statuses_url":"https://api.github.com/repos/netty/netty/statuses/720f223ba8b30bf7ddf20254660628615fcb605e","head":{"label":"dain:testing-byte-buf-allocator","ref":"testing-byte-buf-allocator","sha":"720f223ba8b30bf7ddf20254660628615fcb605e","user":{"login":"dain","id":58298,"node_id":"MDQ6VXNlcjU4Mjk4","avatar_url":"https://avatars0.githubusercontent.com/u/58298?v=4","gravatar_id":"","url":"https://api.github.com/users/dain","html_url":"https://github.com/dain","followers_url":"https://api.github.com/users/dain/followers","following_url":"https://api.github.com/users/dain/following{/other_user}","gists_url":"https://api.github.com/users/dain/gists{/gist_id}","starred_url":"https://api.github.com/users/dain/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dain/subscriptions","organizations_url":"https://api.github.com/users/dain/orgs","repos_url":"https://api.github.com/users/dain/repos","events_url":"https://api.github.com/users/dain/events{/privacy}","received_events_url":"https://api.github.com/users/dain/received_events","type":"User","site_admin":false},"repo":{"id":154229190,"node_id":"MDEwOlJlcG9zaXRvcnkxNTQyMjkxOTA=","name":"netty","full_name":"dain/netty","private":false,"owner":{"login":"dain","id":58298,"node_id":"MDQ6VXNlcjU4Mjk4","avatar_url":"https://avatars0.githubusercontent.com/u/58298?v=4","gravatar_id":"","url":"https://api.github.com/users/dain","html_url":"https://github.com/dain","followers_url":"https://api.github.com/users/dain/followers","following_url":"https://api.github.com/users/dain/following{/other_user}","gists_url":"https://api.github.com/users/dain/gists{/gist_id}","starred_url":"https://api.github.com/users/dain/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dain/subscriptions","organizations_url":"https://api.github.com/users/dain/orgs","repos_url":"https://api.github.com/users/dain/repos","events_url":"https://api.github.com/users/dain/events{/privacy}","received_events_url":"https://api.github.com/users/dain/received_events","type":"User","site_admin":false},"html_url":"https://github.com/dain/netty","description":"Netty project - an event-driven asynchronous network application framework","fork":true,"url":"https://api.github.com/repos/dain/netty","forks_url":"https://api.github.com/repos/dain/netty/forks","keys_url":"https://api.github.com/repos/dain/netty/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dain/netty/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dain/netty/teams","hooks_url":"https://api.github.com/repos/dain/netty/hooks","issue_events_url":"https://api.github.com/repos/dain/netty/issues/events{/number}","events_url":"https://api.github.com/repos/dain/netty/events","assignees_url":"https://api.github.com/repos/dain/netty/assignees{/user}","branches_url":"https://api.github.com/repos/dain/netty/branches{/branch}","tags_url":"https://api.github.com/repos/dain/netty/tags","blobs_url":"https://api.github.com/repos/dain/netty/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dain/netty/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dain/netty/git/refs{/sha}","trees_url":"https://api.github.com/repos/dain/netty/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dain/netty/statuses/{sha}","languages_url":"https://api.github.com/repos/dain/netty/languages","stargazers_url":"https://api.github.com/repos/dain/netty/stargazers","contributors_url":"https://api.github.com/repos/dain/netty/contributors","subscribers_url":"https://api.github.com/repos/dain/netty/subscribers","subscription_url":"https://api.github.com/repos/dain/netty/subscription","commits_url":"https://api.github.com/repos/dain/netty/commits{/sha}","git_commits_url":"https://api.github.com/repos/dain/netty/git/commits{/sha}","comments_url":"https://api.github.com/repos/dain/netty/comments{/number}","issue_comment_url":"https://api.github.com/repos/dain/netty/issues/comments{/number}","contents_url":"https://api.github.com/repos/dain/netty/contents/{+path}","compare_url":"https://api.github.com/repos/dain/netty/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dain/netty/merges","archive_url":"https://api.github.com/repos/dain/netty/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dain/netty/downloads","issues_url":"https://api.github.com/repos/dain/netty/issues{/number}","pulls_url":"https://api.github.com/repos/dain/netty/pulls{/number}","milestones_url":"https://api.github.com/repos/dain/netty/milestones{/number}","notifications_url":"https://api.github.com/repos/dain/netty/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dain/netty/labels{/name}","releases_url":"https://api.github.com/repos/dain/netty/releases{/id}","deployments_url":"https://api.github.com/repos/dain/netty/deployments","created_at":"2018-10-22T23:09:09Z","updated_at":"2018-10-22T23:09:22Z","pushed_at":"2018-10-26T19:00:59Z","git_url":"git://github.com/dain/netty.git","ssh_url":"git@github.com:dain/netty.git","clone_url":"https://github.com/dain/netty.git","svn_url":"https://github.com/dain/netty","homepage":"http://netty.io/","size":52082,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"4.1"}},"base":{"label":"netty:4.1","ref":"4.1","sha":"48c45cf4aceb1e6e9dce8a0670af79570a11f369","user":{"login":"netty","id":473791,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ3Mzc5MQ==","avatar_url":"https://avatars1.githubusercontent.com/u/473791?v=4","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"repo":{"id":1064563,"node_id":"MDEwOlJlcG9zaXRvcnkxMDY0NTYz","name":"netty","full_name":"netty/netty","private":false,"owner":{"login":"netty","id":473791,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQ3Mzc5MQ==","avatar_url":"https://avatars1.githubusercontent.com/u/473791?v=4","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/netty/netty","description":"Netty project - an event-driven asynchronous network application framework","fork":false,"url":"https://api.github.com/repos/netty/netty","forks_url":"https://api.github.com/repos/netty/netty/forks","keys_url":"https://api.github.com/repos/netty/netty/keys{/key_id}","collaborators_url":"https://api.github.com/repos/netty/netty/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/netty/netty/teams","hooks_url":"https://api.github.com/repos/netty/netty/hooks","issue_events_url":"https://api.github.com/repos/netty/netty/issues/events{/number}","events_url":"https://api.github.com/repos/netty/netty/events","assignees_url":"https://api.github.com/repos/netty/netty/assignees{/user}","branches_url":"https://api.github.com/repos/netty/netty/branches{/branch}","tags_url":"https://api.github.com/repos/netty/netty/tags","blobs_url":"https://api.github.com/repos/netty/netty/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/netty/netty/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/netty/netty/git/refs{/sha}","trees_url":"https://api.github.com/repos/netty/netty/git/trees{/sha}","statuses_url":"https://api.github.com/repos/netty/netty/statuses/{sha}","languages_url":"https://api.github.com/repos/netty/netty/languages","stargazers_url":"https://api.github.com/repos/netty/netty/stargazers","contributors_url":"https://api.github.com/repos/netty/netty/contributors","subscribers_url":"https://api.github.com/repos/netty/netty/subscribers","subscription_url":"https://api.github.com/repos/netty/netty/subscription","commits_url":"https://api.github.com/repos/netty/netty/commits{/sha}","git_commits_url":"https://api.github.com/repos/netty/netty/git/commits{/sha}","comments_url":"https://api.github.com/repos/netty/netty/comments{/number}","issue_comment_url":"https://api.github.com/repos/netty/netty/issues/comments{/number}","contents_url":"https://api.github.com/repos/netty/netty/contents/{+path}","compare_url":"https://api.github.com/repos/netty/netty/compare/{base}...{head}","merges_url":"https://api.github.com/repos/netty/netty/merges","archive_url":"https://api.github.com/repos/netty/netty/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/netty/netty/downloads","issues_url":"https://api.github.com/repos/netty/netty/issues{/number}","pulls_url":"https://api.github.com/repos/netty/netty/pulls{/number}","milestones_url":"https://api.github.com/repos/netty/netty/milestones{/number}","notifications_url":"https://api.github.com/repos/netty/netty/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/netty/netty/labels{/name}","releases_url":"https://api.github.com/repos/netty/netty/releases{/id}","deployments_url":"https://api.github.com/repos/netty/netty/deployments","created_at":"2010-11-09T09:22:21Z","updated_at":"2018-11-08T15:33:41Z","pushed_at":"2018-11-08T22:32:55Z","git_url":"git://github.com/netty/netty.git","ssh_url":"git@github.com:netty/netty.git","clone_url":"https://github.com/netty/netty.git","svn_url":"https://github.com/netty/netty","homepage":"http://netty.io/","size":52764,"stargazers_count":16438,"watchers_count":16438,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":7229,"mirror_url":null,"archived":false,"open_issues_count":405,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":7229,"open_issues":405,"watchers":16438,"default_branch":"4.1"}},"_links":{"self":{"href":"https://api.github.com/repos/netty/netty/pulls/8423"},"html":{"href":"https://github.com/netty/netty/pull/8423"},"issue":{"href":"https://api.github.com/repos/netty/netty/issues/8423"},"comments":{"href":"https://api.github.com/repos/netty/netty/issues/8423/comments"},"review_comments":{"href":"https://api.github.com/repos/netty/netty/pulls/8423/comments"},"review_comment":{"href":"https://api.github.com/repos/netty/netty/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/netty/netty/pulls/8423/commits"},"statuses":{"href":"https://api.github.com/repos/netty/netty/statuses/720f223ba8b30bf7ddf20254660628615fcb605e"}},"author_association":"NONE"}}
{"actor":{"display_login":"carl-mastrangelo"}}