c# - sqlite.net + monotouch = SIGSEGV crashes -
we're using following:
- xamarin 3 (xamarin forms)
- monotouch
- sqlite.net
- ios simulator/hardware
the app synchronizes data server on background thread. there 1 sqlite connection object shared entire app. foreground queries executed @ same time background sync running. of has worked fine on windows 8.1 version of app (i.e., on msft surface , similar). once switched xamarin/mono started getting constant crashes shown below.
research led article: http://www.aaronheise.com/2012/12/monotouch-sqlite-sigsegv/
he's using using mono.data.sqliteclient, not sqlite.net are.
his solution involves explicitly disposing of command objects in order ensure gc can keep etc. when tried wrap command objects (from sqlite.net) in using(){} clause found out not disposable.
i've tried inserting 100ms delays , stops crashes, it's not viable solution us.
is there hope sqlite.net here, or should different way use sqlite?
mono-rt: stacktrace: mono-rt: @ <unknown> <0xffffffff> mono-rt: @ (wrapper managed-to-native) sqlite.sqlite3.prepare2 (intptr,string,int,intptr&,intptr) <il 0x0003c, 0xffffffff> ... mono-rt: native stacktrace: mono-rt: got sigsegv while executing native code. indicates fatal error in mono runtime or 1 of native libraries used application.
i'm pretty sure getting meaningful errors instead of sigsegv's when tried hammering same sqlite.net connection multiple threads, if believe that's culprit, solution simple: need restrict access sqlite.net methods touch database 1 thread @ time.
in scenario you're sharing single sqliteconnection
instance in app (which valid way of doing things), recommend creating simplified proxy class wrapping sqlite.net connection, exposing methods want , protecting access lock
statements, i.e:
public class databasewrapper : idisposable { // fields. private readonly sqliteconnection connection; private readonly object lock = new object(); public databasewrapper(string databasepath) { if (string.isnullorempty(databasepath)) throw new argumentexception("database path cannot null or empty."); this.connection = new sqliteconnection(databasepath); } public ienumerable<t> entities<t>() t : new() { lock (this.lock) { return this.connection.table<t>(); } } public ienumerable<t> query<t>(string query, params object[] args) t : new() { lock (this.lock) { return this.connection.query<t>(query, args); } } public int executenonquery(string sql, params object[] args) { lock (this.lock) { return this.connection.execute(sql, args); } } public t executescalar<t>(string sql, params object[] args) { lock (this.lock) { return this.connection.executescalar<t>(sql, args); } } public void insert<t>(t entity) { lock (this.lock) { this.connection.insert(entity); } } public void update<t>(t entity) { lock (this.lock) { this.connection.update(entity); } } public void upsert<t>(t entity) { lock (this.lock) { var rowcount = this.connection.update(entity); if (rowcount == 0) { this.connection.insert(entity); } } } public void delete<t>(t entity) { lock (this.lock) { this.connection.delete(entity); } } public void dispose() { this.connection.dispose(); } }
p.s. since you're doing things on multiple threads need careful not introduce race conditions, why, example, included upsert
method guaranteed perform two-step "update or insert" operation atomically.
Comments
Post a Comment