This makes notmuch appropriately free the underlying notmuch C objects when garbage collecting their Go wrappers. To make sure we don't break the underlying links between objects (for example, a notmuch_messages_t being GC'ed before a notmuch_message_t belonging to it), we add for each wraper struct a pointer to the owner object (Go objects with a reference pointing to them don't get garbage collected). --- bindings/go/src/notmuch/notmuch.go | 153 +++++++++++++++++++++++++++++++----- 1 files changed, 134 insertions(+), 19 deletions(-)
diff --git a/bindings/go/src/notmuch/notmuch.go b/bindings/go/src/notmuch/notmuch.go index 1d77fd2..3f436a0 100644 --- a/bindings/go/src/notmuch/notmuch.go +++ b/bindings/go/src/notmuch/notmuch.go @@ -11,6 +11,7 @@ package notmuch #include "notmuch.h" */ import "C" +import "runtime" import "unsafe" // Status codes used for the return values of most functions @@ -47,40 +48,152 @@ func (self Status) String() string { /* Various opaque data types. For each notmuch_<foo>_t see the various * notmuch_<foo> functions below. */ +type Object interface {} + type Database struct { db *C.notmuch_database_t } +func createDatabase(db *C.notmuch_database_t) *Database { + self := &Database{db: db} + + runtime.SetFinalizer(self, func(x *Database) { + if (x.db != nil) { + C.notmuch_database_destroy(x.db) + } + }) + + return self +} + type Query struct { query *C.notmuch_query_t + owner Object +} + +func createQuery(query *C.notmuch_query_t, owner Object) *Query { + self := &Query{query: query, owner: owner} + + runtime.SetFinalizer(self, func(x *Query) { + if (x.query != nil) { + C.notmuch_query_destroy(x.query) + } + }) + + return self } type Threads struct { threads *C.notmuch_threads_t + owner Object +} + +func createThreads(threads *C.notmuch_threads_t, owner Object) *Threads { + self := &Threads{threads: threads, owner: owner} + + runtime.SetFinalizer(self, func(x *Threads) { + if (x.threads != nil) { + C.notmuch_threads_destroy(x.threads) + } + }) + + return self } type Thread struct { thread *C.notmuch_thread_t + owner Object +} + +func createThread(thread *C.notmuch_thread_t, owner Object) *Thread { + self := &Thread{thread: thread, owner: owner} + + runtime.SetFinalizer(self, func(x *Thread) { + if (x.thread != nil) { + C.notmuch_thread_destroy(x.thread) + } + }) + + return self } type Messages struct { messages *C.notmuch_messages_t + owner Object +} + +func createMessages(messages *C.notmuch_messages_t, owner Object) *Messages { + self := &Messages{messages: messages, owner: owner} + + return self } type Message struct { message *C.notmuch_message_t + owner Object +} + +func createMessage(message *C.notmuch_message_t, owner Object) *Message { + self := &Message{message: message, owner: owner} + + runtime.SetFinalizer(self, func(x *Message) { + if (x.message != nil) { + C.notmuch_message_destroy(x.message) + } + }) + + return self } type Tags struct { tags *C.notmuch_tags_t + owner Object +} + +func createTags(tags *C.notmuch_tags_t, owner Object) *Tags { + self := &Tags{tags: tags, owner: owner} + + runtime.SetFinalizer(self, func(x *Tags) { + if (x.tags != nil) { + C.notmuch_tags_destroy(x.tags) + } + }) + + return self } type Directory struct { dir *C.notmuch_directory_t + owner Object +} + +func createDirectory(directory *C.notmuch_directory_t, owner Object) *Directory { + self := &Directory{dir: directory, owner: owner} + + runtime.SetFinalizer(self, func(x *Directory) { + if (x.dir != nil) { + C.notmuch_directory_destroy(x.dir) + } + }) + + return self } type Filenames struct { fnames *C.notmuch_filenames_t + owner Object +} + +func createFilenames(filenames *C.notmuch_filenames_t, owner Object) *Filenames { + self := &Filenames{fnames: filenames, owner: owner} + + runtime.SetFinalizer(self, func(x *Filenames) { + if (x.fnames != nil) { + C.notmuch_filenames_destroy(x.fnames) + } + }) + + return self } type DatabaseMode C.notmuch_database_mode_t @@ -100,12 +213,13 @@ func NewDatabase(path string) (*Database, Status) { return nil, STATUS_OUT_OF_MEMORY } - self := &Database{db: nil} - st := Status(C.notmuch_database_create(c_path, &self.db)) + var db *C.notmuch_database_t; + st := Status(C.notmuch_database_create(c_path, &db)) if st != STATUS_SUCCESS { return nil, st } - return self, st + + return createDatabase(db), st } /* Open an existing notmuch database located at 'path'. @@ -134,12 +248,13 @@ func OpenDatabase(path string, mode DatabaseMode) (*Database, Status) { return nil, STATUS_OUT_OF_MEMORY } - self := &Database{db: nil} - st := Status(C.notmuch_database_open(c_path, C.notmuch_database_mode_t(mode), &self.db)) + var db *C.notmuch_database_t; + st := Status(C.notmuch_database_open(c_path, C.notmuch_database_mode_t(mode), &db)) if st != STATUS_SUCCESS { return nil, st } - return self, st + + return createDatabase(db), st } /* Close the given notmuch database, freeing all associated @@ -204,7 +319,7 @@ func (self *Database) GetDirectory(path string) (*Directory, Status) { if st != STATUS_SUCCESS || c_dir == nil { return nil, st } - return &Directory{dir: c_dir}, st + return createDirectory(c_dir, nil), st } /* Add a new message to the given notmuch database. @@ -258,7 +373,7 @@ func (self *Database) AddMessage(fname string) (*Message, Status) { var c_msg *C.notmuch_message_t = new(C.notmuch_message_t) st := Status(C.notmuch_database_add_message(self.db, c_fname, &c_msg)) - return &Message{message: c_msg}, st + return createMessage(c_msg, nil), st } /* Remove a message from the given notmuch database. @@ -319,12 +434,12 @@ func (self *Database) FindMessage(message_id string) (*Message, Status) { return nil, STATUS_OUT_OF_MEMORY } - msg := &Message{message: nil} - st := Status(C.notmuch_database_find_message(self.db, c_msg_id, &msg.message)) + var msg *C.notmuch_message_t + st := Status(C.notmuch_database_find_message(self.db, c_msg_id, &msg)) if st != STATUS_SUCCESS { return nil, st } - return msg, st + return createMessage(msg, nil), st } /* Return a list of all tags found in the database. @@ -339,7 +454,7 @@ func (self *Database) GetAllTags() *Tags { if tags == nil { return nil } - return &Tags{tags: tags} + return createTags(tags, nil) } /* Create a new query for 'database'. @@ -379,7 +494,7 @@ func (self *Database) CreateQuery(query string) *Query { if q == nil { return nil } - return &Query{query: q} + return createQuery(q, nil) } /* Sort values for notmuch_query_set_sort */ @@ -459,7 +574,7 @@ func (self *Query) SearchThreads() *Threads { if threads == nil { return nil } - return &Threads{threads: threads} + return createThreads(threads, self) } /* Execute a query for messages, returning a notmuch_messages_t object @@ -505,7 +620,7 @@ func (self *Query) SearchMessages() *Messages { if msgs == nil { return nil } - return &Messages{messages: msgs} + return createMessages(msgs, self) } /* Destroy a notmuch_query_t along with any associated resources. @@ -607,7 +722,7 @@ func (self *Messages) Get() *Message { if msg == nil { return nil } - return &Message{message: msg} + return createMessage(msg, self) } /* Move the 'messages' iterator to the next message. @@ -659,7 +774,7 @@ func (self *Messages) CollectTags() *Tags { if tags == nil { return nil } - return &Tags{tags: tags} + return createTags(tags, self) } /* Get the message ID of 'message'. @@ -739,7 +854,7 @@ func (self *Message) GetReplies() *Messages { if msgs == nil { return nil } - return &Messages{messages: msgs} + return createMessages(msgs, self) } /* Get a filename for the email corresponding to 'message'. @@ -871,7 +986,7 @@ func (self *Message) GetTags() *Tags { if tags == nil { return nil } - return &Tags{tags: tags} + return createTags(tags, self) } /* The longest possible tag value. */ -- 1.7.7.6