--- /dev/null
+Return-Path: <amdragon@mit.edu>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by olra.theworths.org (Postfix) with ESMTP id D128C431FAF\r
+ for <notmuch@notmuchmail.org>; Wed, 18 Jul 2012 13:40:05 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.7\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
+ tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+ by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id QnCXkpR3SVQ9 for <notmuch@notmuchmail.org>;\r
+ Wed, 18 Jul 2012 13:40:05 -0700 (PDT)\r
+Received: from dmz-mailsec-scanner-4.mit.edu (DMZ-MAILSEC-SCANNER-4.MIT.EDU\r
+ [18.9.25.15])\r
+ by olra.theworths.org (Postfix) with ESMTP id C3DEE431FAE\r
+ for <notmuch@notmuchmail.org>; Wed, 18 Jul 2012 13:40:04 -0700 (PDT)\r
+X-AuditID: 1209190f-b7f306d0000008b4-27-50071f2441b5\r
+Received: from mailhub-auth-4.mit.edu ( [18.7.62.39])\r
+ by dmz-mailsec-scanner-4.mit.edu (Symantec Messaging Gateway) with SMTP\r
+ id 42.8A.02228.42F17005; Wed, 18 Jul 2012 16:40:04 -0400 (EDT)\r
+Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
+ by mailhub-auth-4.mit.edu (8.13.8/8.9.2) with ESMTP id q6IKe3VR000780; \r
+ Wed, 18 Jul 2012 16:40:03 -0400\r
+Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
+ (authenticated bits=0)\r
+ (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+ by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id q6IKe1Gm002688\r
+ (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+ Wed, 18 Jul 2012 16:40:02 -0400 (EDT)\r
+Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.77)\r
+ (envelope-from <amdragon@mit.edu>)\r
+ id 1Srb2P-0001Cd-AG; Wed, 18 Jul 2012 16:40:01 -0400\r
+Date: Wed, 18 Jul 2012 16:40:01 -0400\r
+From: Austin Clements <amdragon@MIT.EDU>\r
+To: Adrien Bustany <adrien@bustany.org>\r
+Subject: Re: [PATCH 3/7] go: Allow notmuch objects to be garbage collected\r
+Message-ID: <20120718204001.GT31670@mit.edu>\r
+References: <1342636475-16057-1-git-send-email-adrien@bustany.org>\r
+ <1342636475-16057-4-git-send-email-adrien@bustany.org>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=us-ascii\r
+Content-Disposition: inline\r
+In-Reply-To: <1342636475-16057-4-git-send-email-adrien@bustany.org>\r
+User-Agent: Mutt/1.5.21 (2010-09-15)\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFmpkleLIzCtJLcpLzFFi42IRYrdT11WRZw8w+LpH2GL9nbVsFtdvzmR2\r
+ YPL4eOAek8ezVbeYA5iiuGxSUnMyy1KL9O0SuDLOv7EqWOJbcXVaN1sDY7tNFyMHh4SAicTy\r
+ xZZdjJxAppjEhXvr2UBsIYF9jBLPWmW6GLmA7A2MEndWzGOHcE4ySUy/uokRwlnCKNG4cDkr\r
+ SAuLgKrExtNzWEBsNgENiW37lzOC2CIC6hI7OtvBbGYBaYlvv5uZQGxhAS+J25t7mEFsXgEd\r
+ iXOTl7BCrK6W2HZqBRtEXFDi5MwnLBC9WhI3/r1kArkaZM7yfxwgYU4BZ4kpUxaBtYoKqEhM\r
+ ObmNbQKj0Cwk3bOQdM9C6F7AyLyKUTYlt0o3NzEzpzg1Wbc4OTEvL7VI10QvN7NELzWldBMj\r
+ KKQ5Jfl3MH47qHSIUYCDUYmH98Eu1gAh1sSy4srcQ4ySHExKoryfhNgDhPiS8lMqMxKLM+KL\r
+ SnNSiw8xSnAwK4nwPhAEyvGmJFZWpRblw6SkOViUxHmvptz0FxJITyxJzU5NLUgtgsnKcHAo\r
+ SfA6ywE1ChalpqdWpGXmlCCkmTg4QYbzAA23BKnhLS5IzC3OTIfIn2JUlBLnjQJJCIAkMkrz\r
+ 4HphKecVozjQK8K8NiBVPMB0Bdf9CmgwE9Bg7mI2kMEliQgpqQZGq6d/jZKvCazZqsqV//r4\r
+ m71TN+206DNQ2xPhfrCP8Qgnf2/Mu3ULrBpsbAL81scsEZ38XPCiUpS5o3jZvw378/PPsIq0\r
+ TrLrP+T7k7P6xrFm9sf9Zl8TTIv2TeBaeKVmHVv6g+6ab9UP55/8/mDyqqmCIZHeN5leMDNy\r
+ Xymfu+qp/WoN6U/7lFiKMxINtZiLihMB2JbsphQDAAA=\r
+Cc: notmuch@notmuchmail.org\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Wed, 18 Jul 2012 20:40:06 -0000\r
+\r
+This is subtle enough that I think it deserves a comment in the source\r
+code explaining that tracking the talloc owner reference, combined\r
+with the fact that Go finalizers are run in dependency order, ensures\r
+that the C objects will always be destroyed from the talloc leaves up.\r
+\r
+Just one inline comment below. Otherwise, I think this is all\r
+correct.\r
+\r
+Is reproducing the talloc hierarchy in all of the bindings really the\r
+right approach? I feel like there has to be a better way (or that the\r
+way we use talloc in the library is slightly broken). What if the\r
+bindings created an additional talloc reference to each managed\r
+object, just to keep the object alive, and used talloc_unlink instead\r
+of the destroy functions?\r
+\r
+Quoth Adrien Bustany on Jul 18 at 9:34 pm:\r
+> This makes notmuch appropriately free the underlying notmuch C objects\r
+> when garbage collecting their Go wrappers. To make sure we don't break\r
+> the underlying links between objects (for example, a notmuch_messages_t\r
+> being GC'ed before a notmuch_message_t belonging to it), we add for each\r
+> wraper struct a pointer to the owner object (Go objects with a reference\r
+> pointing to them don't get garbage collected).\r
+> ---\r
+> bindings/go/src/notmuch/notmuch.go | 153 +++++++++++++++++++++++++++++++-----\r
+> 1 files changed, 134 insertions(+), 19 deletions(-)\r
+> \r
+> diff --git a/bindings/go/src/notmuch/notmuch.go b/bindings/go/src/notmuch/notmuch.go\r
+> index 1d77fd2..3f436a0 100644\r
+> --- a/bindings/go/src/notmuch/notmuch.go\r
+> +++ b/bindings/go/src/notmuch/notmuch.go\r
+> @@ -11,6 +11,7 @@ package notmuch\r
+> #include "notmuch.h"\r
+> */\r
+> import "C"\r
+> +import "runtime"\r
+> import "unsafe"\r
+> \r
+> // Status codes used for the return values of most functions\r
+> @@ -47,40 +48,152 @@ func (self Status) String() string {\r
+> /* Various opaque data types. For each notmuch_<foo>_t see the various\r
+> * notmuch_<foo> functions below. */\r
+> \r
+> +type Object interface {}\r
+> +\r
+> type Database struct {\r
+> db *C.notmuch_database_t\r
+> }\r
+> \r
+> +func createDatabase(db *C.notmuch_database_t) *Database {\r
+> + self := &Database{db: db}\r
+> +\r
+> + runtime.SetFinalizer(self, func(x *Database) {\r
+> + if (x.db != nil) {\r
+> + C.notmuch_database_destroy(x.db)\r
+> + }\r
+> + })\r
+> +\r
+> + return self\r
+> +}\r
+> +\r
+> type Query struct {\r
+> query *C.notmuch_query_t\r
+> + owner Object\r
+> +}\r
+> +\r
+> +func createQuery(query *C.notmuch_query_t, owner Object) *Query {\r
+> + self := &Query{query: query, owner: owner}\r
+> +\r
+> + runtime.SetFinalizer(self, func(x *Query) {\r
+> + if (x.query != nil) {\r
+> + C.notmuch_query_destroy(x.query)\r
+> + }\r
+> + })\r
+> +\r
+> + return self\r
+> }\r
+> \r
+> type Threads struct {\r
+> threads *C.notmuch_threads_t\r
+> + owner Object\r
+> +}\r
+> +\r
+> +func createThreads(threads *C.notmuch_threads_t, owner Object) *Threads {\r
+> + self := &Threads{threads: threads, owner: owner}\r
+> +\r
+> + runtime.SetFinalizer(self, func(x *Threads) {\r
+> + if (x.threads != nil) {\r
+> + C.notmuch_threads_destroy(x.threads)\r
+> + }\r
+> + })\r
+> +\r
+> + return self\r
+> }\r
+> \r
+> type Thread struct {\r
+> thread *C.notmuch_thread_t\r
+> + owner Object\r
+> +}\r
+> +\r
+> +func createThread(thread *C.notmuch_thread_t, owner Object) *Thread {\r
+> + self := &Thread{thread: thread, owner: owner}\r
+> +\r
+> + runtime.SetFinalizer(self, func(x *Thread) {\r
+> + if (x.thread != nil) {\r
+> + C.notmuch_thread_destroy(x.thread)\r
+> + }\r
+> + })\r
+> +\r
+> + return self\r
+> }\r
+> \r
+> type Messages struct {\r
+> messages *C.notmuch_messages_t\r
+> + owner Object\r
+> +}\r
+> +\r
+> +func createMessages(messages *C.notmuch_messages_t, owner Object) *Messages {\r
+> + self := &Messages{messages: messages, owner: owner}\r
+> +\r
+> + return self\r
+> }\r
+> \r
+> type Message struct {\r
+> message *C.notmuch_message_t\r
+> + owner Object\r
+> +}\r
+> +\r
+> +func createMessage(message *C.notmuch_message_t, owner Object) *Message {\r
+> + self := &Message{message: message, owner: owner}\r
+> +\r
+> + runtime.SetFinalizer(self, func(x *Message) {\r
+> + if (x.message != nil) {\r
+> + C.notmuch_message_destroy(x.message)\r
+> + }\r
+> + })\r
+> +\r
+> + return self\r
+> }\r
+> \r
+> type Tags struct {\r
+> tags *C.notmuch_tags_t\r
+> + owner Object\r
+> +}\r
+> +\r
+> +func createTags(tags *C.notmuch_tags_t, owner Object) *Tags {\r
+> + self := &Tags{tags: tags, owner: owner}\r
+> +\r
+> + runtime.SetFinalizer(self, func(x *Tags) {\r
+> + if (x.tags != nil) {\r
+> + C.notmuch_tags_destroy(x.tags)\r
+> + }\r
+> + })\r
+> +\r
+> + return self\r
+> }\r
+> \r
+> type Directory struct {\r
+> dir *C.notmuch_directory_t\r
+> + owner Object\r
+> +}\r
+> +\r
+> +func createDirectory(directory *C.notmuch_directory_t, owner Object) *Directory {\r
+> + self := &Directory{dir: directory, owner: owner}\r
+> +\r
+> + runtime.SetFinalizer(self, func(x *Directory) {\r
+> + if (x.dir != nil) {\r
+> + C.notmuch_directory_destroy(x.dir)\r
+> + }\r
+> + })\r
+> +\r
+> + return self\r
+> }\r
+> \r
+> type Filenames struct {\r
+> fnames *C.notmuch_filenames_t\r
+> + owner Object\r
+> +}\r
+> +\r
+> +func createFilenames(filenames *C.notmuch_filenames_t, owner Object) *Filenames {\r
+> + self := &Filenames{fnames: filenames, owner: owner}\r
+> +\r
+> + runtime.SetFinalizer(self, func(x *Filenames) {\r
+> + if (x.fnames != nil) {\r
+> + C.notmuch_filenames_destroy(x.fnames)\r
+> + }\r
+> + })\r
+> +\r
+> + return self\r
+> }\r
+> \r
+> type DatabaseMode C.notmuch_database_mode_t\r
+> @@ -100,12 +213,13 @@ func NewDatabase(path string) (*Database, Status) {\r
+> return nil, STATUS_OUT_OF_MEMORY\r
+> }\r
+> \r
+> - self := &Database{db: nil}\r
+> - st := Status(C.notmuch_database_create(c_path, &self.db))\r
+> + var db *C.notmuch_database_t;\r
+> + st := Status(C.notmuch_database_create(c_path, &db))\r
+> if st != STATUS_SUCCESS {\r
+> return nil, st\r
+> }\r
+> - return self, st\r
+> +\r
+> + return createDatabase(db), st\r
+> }\r
+> \r
+> /* Open an existing notmuch database located at 'path'.\r
+> @@ -134,12 +248,13 @@ func OpenDatabase(path string, mode DatabaseMode) (*Database, Status) {\r
+> return nil, STATUS_OUT_OF_MEMORY\r
+> }\r
+> \r
+> - self := &Database{db: nil}\r
+> - st := Status(C.notmuch_database_open(c_path, C.notmuch_database_mode_t(mode), &self.db))\r
+> + var db *C.notmuch_database_t;\r
+> + st := Status(C.notmuch_database_open(c_path, C.notmuch_database_mode_t(mode), &db))\r
+> if st != STATUS_SUCCESS {\r
+> return nil, st\r
+> }\r
+> - return self, st\r
+> +\r
+> + return createDatabase(db), st\r
+> }\r
+> \r
+> /* Close the given notmuch database, freeing all associated\r
+> @@ -204,7 +319,7 @@ func (self *Database) GetDirectory(path string) (*Directory, Status) {\r
+> if st != STATUS_SUCCESS || c_dir == nil {\r
+> return nil, st\r
+> }\r
+> - return &Directory{dir: c_dir}, st\r
+> + return createDirectory(c_dir, nil), st\r
+\r
+It looks like you have a nil owner for anything whose talloc parent is\r
+the database. Is this intentional? Shouldn't the owner be self in\r
+these cases, too?\r
+\r
+> }\r
+> \r
+> /* Add a new message to the given notmuch database.\r
+> @@ -258,7 +373,7 @@ func (self *Database) AddMessage(fname string) (*Message, Status) {\r
+> var c_msg *C.notmuch_message_t = new(C.notmuch_message_t)\r
+> st := Status(C.notmuch_database_add_message(self.db, c_fname, &c_msg))\r
+> \r
+> - return &Message{message: c_msg}, st\r
+> + return createMessage(c_msg, nil), st\r
+> }\r
+> \r
+> /* Remove a message from the given notmuch database.\r
+> @@ -319,12 +434,12 @@ func (self *Database) FindMessage(message_id string) (*Message, Status) {\r
+> return nil, STATUS_OUT_OF_MEMORY\r
+> }\r
+> \r
+> - msg := &Message{message: nil}\r
+> - st := Status(C.notmuch_database_find_message(self.db, c_msg_id, &msg.message))\r
+> + var msg *C.notmuch_message_t\r
+> + st := Status(C.notmuch_database_find_message(self.db, c_msg_id, &msg))\r
+> if st != STATUS_SUCCESS {\r
+> return nil, st\r
+> }\r
+> - return msg, st\r
+> + return createMessage(msg, nil), st\r
+> }\r
+> \r
+> /* Return a list of all tags found in the database.\r
+> @@ -339,7 +454,7 @@ func (self *Database) GetAllTags() *Tags {\r
+> if tags == nil {\r
+> return nil\r
+> }\r
+> - return &Tags{tags: tags}\r
+> + return createTags(tags, nil)\r
+> }\r
+> \r
+> /* Create a new query for 'database'.\r
+> @@ -379,7 +494,7 @@ func (self *Database) CreateQuery(query string) *Query {\r
+> if q == nil {\r
+> return nil\r
+> }\r
+> - return &Query{query: q}\r
+> + return createQuery(q, nil)\r
+> }\r
+> \r
+> /* Sort values for notmuch_query_set_sort */\r
+> @@ -459,7 +574,7 @@ func (self *Query) SearchThreads() *Threads {\r
+> if threads == nil {\r
+> return nil\r
+> }\r
+> - return &Threads{threads: threads}\r
+> + return createThreads(threads, self)\r
+> }\r
+> \r
+> /* Execute a query for messages, returning a notmuch_messages_t object\r
+> @@ -505,7 +620,7 @@ func (self *Query) SearchMessages() *Messages {\r
+> if msgs == nil {\r
+> return nil\r
+> }\r
+> - return &Messages{messages: msgs}\r
+> + return createMessages(msgs, self)\r
+> }\r
+> \r
+> /* Destroy a notmuch_query_t along with any associated resources.\r
+> @@ -607,7 +722,7 @@ func (self *Messages) Get() *Message {\r
+> if msg == nil {\r
+> return nil\r
+> }\r
+> - return &Message{message: msg}\r
+> + return createMessage(msg, self)\r
+> }\r
+> \r
+> /* Move the 'messages' iterator to the next message.\r
+> @@ -659,7 +774,7 @@ func (self *Messages) CollectTags() *Tags {\r
+> if tags == nil {\r
+> return nil\r
+> }\r
+> - return &Tags{tags: tags}\r
+> + return createTags(tags, self)\r
+> }\r
+> \r
+> /* Get the message ID of 'message'.\r
+> @@ -739,7 +854,7 @@ func (self *Message) GetReplies() *Messages {\r
+> if msgs == nil {\r
+> return nil\r
+> }\r
+> - return &Messages{messages: msgs}\r
+> + return createMessages(msgs, self)\r
+> }\r
+> \r
+> /* Get a filename for the email corresponding to 'message'.\r
+> @@ -871,7 +986,7 @@ func (self *Message) GetTags() *Tags {\r
+> if tags == nil {\r
+> return nil\r
+> }\r
+> - return &Tags{tags: tags}\r
+> + return createTags(tags, self)\r
+> }\r
+> \r
+> /* The longest possible tag value. */\r