sqlite.net + monotouch = SIGSEGV crashes

主宰稳场 提交于 2019-12-07 09:18:04

问题


We're using the following:

  • Xamarin 3 (Xamarin Forms)
  • MonoTouch
  • sqlite.net
  • iOS simulator/hardware

The app synchronizes data with a server on a background thread. There is only one SQLite connection object shared by the entire app. Foreground queries are executed at the same time the background sync is running. All of this has worked fine on a Windows 8.1 version of the app (i.e., on MSFT Surface and similar). However once we switched to Xamarin/mono we started getting constant crashes as shown below.

Research led to this article: http://www.aaronheise.com/2012/12/monotouch-sqlite-sigsegv/

He's using using Mono.Data.SqliteClient, not sqlite.net as we are.

His solution involves explicitly disposing of Command objects in order to ensure the GC can keep up etc. When I tried to wrap my Command objects (from sqlite.net) in a using(){} clause I found out they are not disposable.

I've tried inserting 100ms delays and that stops the crashes, however it's not a viable solution for us.

Is there any hope for sqlite.net here, or should I look for a different way to use sqlite?

    mono-rt: Stacktrace:


mono-rt:   at <unknown> <0xffffffff>

mono-rt:   at (wrapper managed-to-native) SQLite.SQLite3.Prepare2 (intptr,string,int,intptr&,intptr) <IL 0x0003c, 0xffffffff>

...

mono-rt: 
Native stacktrace:


mono-rt: 
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.

回答1:


I'm pretty sure I was getting meaningful errors instead of SIGSEGV's when I tried hammering the same sqlite.net connection from multiple threads, but if you believe that that's the culprit, the solution is simple: you need to restrict access to any sqlite.net methods which touch the database to one thread at a time.

In the scenario where you're sharing a single SQLiteConnection instance in your app (which is a perfectly valid way of doing things), I recommend creating a simplified proxy class wrapping your sqlite.net connection, exposing only the methods that you want and protecting access to those with 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 be null or empty.");

        this.Connection = new SQLiteConnection(databasePath);
    }

    public IEnumerable<T> Entities<T>() where T : new()
    {
        lock (this.Lock)
        {
            return this.Connection.Table<T>();
        }
    }

    public IEnumerable<T> Query<T>(string query, params object[] args) where 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. Obviously since you're doing things on multiple threads you need to be very careful not to introduce race conditions, which is why, for example, I included the Upsert method that is guaranteed to perform the two-step "update or insert" operation atomically.




回答2:


Try adding the flags: SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex to your SQLite connection constructor. Solved our problem. Looks like SQLite still does some background work after transactions, using the internal mutex ensures the base consistency.



来源:https://stackoverflow.com/questions/24454045/sqlite-net-monotouch-sigsegv-crashes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!