[PATCH 3/7] go: Allow notmuch objects to be garbage collected
authorAdrien Bustany <adrien@bustany.org>
Wed, 18 Jul 2012 18:34:31 +0000 (21:34 +0300)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:48:22 +0000 (09:48 -0800)
db/3a9ad84ec00c33e7e6241f94a1aaedda51adb2 [new file with mode: 0644]

diff --git a/db/3a9ad84ec00c33e7e6241f94a1aaedda51adb2 b/db/3a9ad84ec00c33e7e6241f94a1aaedda51adb2
new file mode 100644 (file)
index 0000000..523329f
--- /dev/null
@@ -0,0 +1,363 @@
+Return-Path: <adrien@bustany.org>\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 677E7431FDE\r
+       for <notmuch@notmuchmail.org>; Wed, 18 Jul 2012 11:41:09 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
+       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 Fgysct7-kN8i for <notmuch@notmuchmail.org>;\r
+       Wed, 18 Jul 2012 11:41:07 -0700 (PDT)\r
+Received: from mail.bustany.org (bustany.org [176.31.244.208])\r
+       by olra.theworths.org (Postfix) with ESMTP id 81E4A431FC4\r
+       for <notmuch@notmuchmail.org>; Wed, 18 Jul 2012 11:41:05 -0700 (PDT)\r
+Received: from localhost.localdomain (91-158-2-79.elisa-laajakaista.fi\r
+       [91.158.2.79])\r
+       by mail.bustany.org (Postfix) with ESMTPSA id 6A6D91400C2\r
+       for <notmuch@notmuchmail.org>; Wed, 18 Jul 2012 20:37:08 +0200 (CEST)\r
+From: Adrien Bustany <adrien@bustany.org>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH 3/7] go: Allow notmuch objects to be garbage collected\r
+Date: Wed, 18 Jul 2012 21:34:31 +0300\r
+Message-Id: <1342636475-16057-4-git-send-email-adrien@bustany.org>\r
+X-Mailer: git-send-email 1.7.7.6\r
+In-Reply-To: <1342636475-16057-1-git-send-email-adrien@bustany.org>\r
+References: <1342636475-16057-1-git-send-email-adrien@bustany.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 18:41:10 -0000\r
+\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
+ \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
+-- \r
+1.7.7.6\r
+\r