Permalink
Browse files

Lock the database so we can load all of the tweets into the

database on the background thread.

PReviously I loaded everything on the main thread and that locked
up the thread as we dumped all the junk in there.

Now I use locks on Database.Main to access the databaes and ensure
that the UI thread does not block for long periods
  • Loading branch information...
1 parent 4e10b65 commit 43c664d379515eb1a876a5e0ce345b949a20fb65 Miguel de Icaza committed Jun 4, 2010
View
10 README
@@ -90,6 +90,16 @@ Design Goals
the settings to take place right away, even if there is no
network connectivity
+Implementation Notes
+====================
+
+ In the original TweetStation, I ended up doing a of database
+ operations on the main thread, as opposed to doing it on a
+ background thread.
+
+ I am moving towards locking the Database.Main to allow the
+ parsing of initial tweets to take place in the background
+
OAuth and xAuth
===============
View
7 TODO
@@ -7,8 +7,11 @@
Open (for links)
Quote
- Shrink links should only be enabled if there is a URL in the
- text or multiple spaces
+ New detailed tweet view, animates insertion of "Reply-To".
+
+ Rotated web view ends up without the bottom bar
+
+ Probably should not rotate to upside down mode
* Missing Stuff
View
72 TweetStation/Data/Tweet.cs
@@ -202,30 +202,33 @@ static public int LoadJson (Stream stream, int localAccount, TweetKind kind)
var start = DateTime.UtcNow;
var usersSeen = new HashSet<long> ();
- Database.Main.Execute ("BEGIN");
- foreach (JsonObject jentry in root){
- var juser = jentry [userKey];
- bool result;
-
- if (!ParseUser ((JsonObject) juser, user, usersSeen))
- continue;
-
- if (kind == TweetKind.Direct)
- result = tweet.TryPopulateDirect (jentry);
- else
- result = tweet.TryPopulate (jentry);
-
- if (result){
- PopulateUser (tweet, user);
- tweet.Insert (db);
- count++;
- }
-
- // Repeat user loading for the retweet info
- if (tweet.Retweeter != null)
- ParseUser ((JsonObject)(jentry ["retweeted_status"]["user"]), user, usersSeen);
+
+ lock (db){
+ db.Execute ("BEGIN");
+ foreach (JsonObject jentry in root){
+ var juser = jentry [userKey];
+ bool result;
+
+ if (!ParseUser ((JsonObject) juser, user, usersSeen))
+ continue;
+
+ if (kind == TweetKind.Direct)
+ result = tweet.TryPopulateDirect (jentry);
+ else
+ result = tweet.TryPopulate (jentry);
+
+ if (result){
+ PopulateUser (tweet, user);
+ tweet.Insert (db);
+ count++;
+ }
+
+ // Repeat user loading for the retweet info
+ if (tweet.Retweeter != null)
+ ParseUser ((JsonObject)(jentry ["retweeted_status"]["user"]), user, usersSeen);
+ }
+ db.Execute ("COMMIT");
}
- Database.Main.Execute ("COMMIT");
var end = DateTime.UtcNow;
Console.WriteLine ("With transactions: Spent {0} ticks in inserting {1} elements", (end-start).Ticks, count);
return count;
@@ -385,7 +388,7 @@ public static IEnumerable<Tweet> TweetsFromSearchResults (Stream stream)
}
}
- public static Tweet ParseTweet (Stream stream)
+ static Tweet ParseTweet (Stream stream)
{
JsonObject jentry;
@@ -398,7 +401,8 @@ public static Tweet ParseTweet (Stream stream)
try {
var user = new User ();
user.UpdateFromJson ((JsonObject) jentry ["user"]);
- Database.Main.Insert (user, "OR REPLACE");
+ lock (Database.Main)
+ Database.Main.Insert (user, "OR REPLACE");
return FromJsonEntry (jentry, user);
} catch (Exception e){
@@ -418,7 +422,8 @@ static Tweet FromJsonEntry (JsonObject jentry, User user)
if (tweet.Retweeter != null){
user = new User ();
user.UpdateFromJson ((JsonObject)(jentry ["retweeted_status"]["user"]));
- Database.Main.Insert (user, "OR REPLACE");
+ lock (Database.Main)
+ Database.Main.Insert (user, "OR REPLACE");
}
return tweet;
}
@@ -449,7 +454,8 @@ static void PopulateUser (Tweet tweet, User user)
//
public static Tweet FromId (long id)
{
- return Database.Main.Query<Tweet> ("SELECT * FROM Tweet WHERE Id = ?", id).FirstOrDefault ();
+ lock (Database.Main)
+ return Database.Main.Query<Tweet> ("SELECT * FROM Tweet WHERE Id = ?", id).FirstOrDefault ();
}
public delegate void LoadCallback (Tweet tweet);
@@ -589,7 +595,8 @@ public void UpdateFromJson (JsonObject json)
// Loads the users from the stream, as a convenience,
// returns the last user loaded (which during lookups is a single one)
//
- static public IEnumerable<User> LoadUsers (Stream source)
+ // Requires datbase lock to be taken.
+ static public IEnumerable<User> UnlockedLoadUsers (Stream source)
{
JsonValue root;
@@ -623,7 +630,8 @@ static public User LoadUser (Stream source)
}
User user = new User ();
user.UpdateFromJson ((JsonObject) root);
- Database.Main.Insert (user, "OR REPLACE");
+ lock (Database.Main)
+ Database.Main.Insert (user, "OR REPLACE");
return user;
}
@@ -632,12 +640,14 @@ static public User LoadUser (Stream source)
//
public static User FromId (long id)
{
- return Database.Main.Query<User> ("SELECT * FROM User WHERE Id = ?", id).FirstOrDefault ();
+ lock (Database.Main)
+ return Database.Main.Query<User> ("SELECT * FROM User WHERE Id = ?", id).FirstOrDefault ();
}
public static User FromName (string screenname)
{
- return Database.Main.Query<User> ("SELECT * From User WHERE Screenname = ?", screenname).FirstOrDefault ();
+ lock (Database.Main)
+ return Database.Main.Query<User> ("SELECT * From User WHERE Screenname = ?", screenname).FirstOrDefault ();
}
}
}
View
60 TweetStation/Data/TwitterAccount.cs
@@ -80,11 +80,13 @@ public static TwitterAccount FromId (int id)
return accounts [id];
}
- var account = Database.Main.Query<TwitterAccount> ("select * from TwitterAccount where LocalAccountId = ?", id).FirstOrDefault ();
- if (account != null)
- accounts [account.LocalAccountId] = account;
-
- return account;
+ lock (Database.Main){
+ var account = Database.Main.Query<TwitterAccount> ("select * from TwitterAccount where LocalAccountId = ?", id).FirstOrDefault ();
+ if (account != null)
+ accounts [account.LocalAccountId] = account;
+
+ return account;
+ }
}
public static TwitterAccount Create (OAuthAuthorizer oauth)
@@ -95,7 +97,8 @@ public static TwitterAccount Create (OAuthAuthorizer oauth)
OAuthToken = oauth.AccessToken,
OAuthTokenSecret = oauth.AccessTokenSecret
};
- Database.Main.Insert (account);
+ lock (Database.Main)
+ Database.Main.Insert (account);
accounts [account.LocalAccountId] = account;
return account;
@@ -109,13 +112,15 @@ public static void Remove (TwitterAccount account)
if (accounts.ContainsKey (id))
accounts.Remove (id);
- Database.Main.Execute ("DELETE FROM Tweet where LocalAccountId = ?", account.LocalAccountId);
- Database.Main.Delete<TwitterAccount> (account);
+ lock (Database.Main){
+ Database.Main.Execute ("DELETE FROM Tweet where LocalAccountId = ?", account.LocalAccountId);
+ Database.Main.Delete<TwitterAccount> (account);
- if (pickNewDefault){
- var newDefault = Database.Main.Query<TwitterAccount> ("SELECT LocalAccountId FROM TwitterAccount WHERE OAuthToken != \"\"").FirstOrDefault ();
- if (newDefault != null)
- Util.Defaults.SetInt (newDefault.LocalAccountId, DEFAULT_ACCOUNT);
+ if (pickNewDefault){
+ var newDefault = Database.Main.Query<TwitterAccount> ("SELECT LocalAccountId FROM TwitterAccount WHERE OAuthToken != \"\"").FirstOrDefault ();
+ if (newDefault != null)
+ Util.Defaults.SetInt (newDefault.LocalAccountId, DEFAULT_ACCOUNT);
+ }
}
}
@@ -153,19 +158,18 @@ public void ReloadTimeline (TweetKind kind, long? since, long? max_id, Action<in
(since.HasValue ? "&since_id=" + since.Value : "") +
(max_id.HasValue ? "&max_id=" + max_id.Value : "");
- Download (req, true, result => {
- if (result == null){
-
- done (-1);
- } else {
- int count = -1;
+ Download (req, false, result => {
+ int count = -1;
+
+ if (result != null){
try {
count = Tweet.LoadJson (new MemoryStream (result), LocalAccountId, kind);
} catch (Exception e) {
Console.WriteLine (e);
}
- done (count);
}
+
+ invoker.BeginInvokeOnMainThread (delegate { done (count); });
});
}
@@ -368,15 +372,18 @@ public void Post (string url, string content)
Url = url,
PostData = content,
};
- Database.Main.Insert (qtask);
+ lock (Database.Main)
+ Database.Main.Insert (qtask);
FlushTasks ();
}
void FlushTasks ()
{
- var tasks = Database.Main.Query<QueuedTask> ("SELECT * FROM QueuedTask where AccountId = ? ORDER BY TaskId DESC", LocalAccountId).ToArray ();
- ThreadPool.QueueUserWorkItem (delegate { PostTask (tasks); });
+ lock (Database.Main){
+ var tasks = Database.Main.Query<QueuedTask> ("SELECT * FROM QueuedTask where AccountId = ? ORDER BY TaskId DESC", LocalAccountId).ToArray ();
+ ThreadPool.QueueUserWorkItem (delegate { PostTask (tasks); });
+ }
}
//
@@ -402,13 +409,8 @@ void PostTask (QueuedTask [] tasks)
// Can happen if we had already favorited this status
}
- invoker.BeginInvokeOnMainThread (delegate {
- try {
- Database.Main.Execute ("DELETE FROM QueuedTask WHERE TaskId = ?", task.TaskId);
- } catch (Exception e){
- Console.WriteLine (e);
- }
- });
+ lock (Database.Main)
+ Database.Main.Execute ("DELETE FROM QueuedTask WHERE TaskId = ?", task.TaskId);
}
} catch (Exception e) {
Console.WriteLine (e);
View
10 TweetStation/Dialogs/EditAccount.cs
@@ -92,10 +92,12 @@ public EditAccount (IAccountContainer container, TwitterAccount account, bool pu
account.Username = info.Login;
//account.Password = info.Password;
- if (newAccount)
- Database.Main.Insert (account);
- else
- Database.Main.Update (account);
+ lock (Database.Main){
+ if (newAccount)
+ Database.Main.Insert (account);
+ else
+ Database.Main.Update (account);
+ }
account.SetDefaultAccount ();
DismissModalViewControllerAnimated (true);
View
22 TweetStation/Dialogs/Settings.cs
@@ -68,17 +68,19 @@ Section MakeAccounts ()
{
var section = new Section (Locale.GetText ("Accounts"));
- foreach (var account in Database.Main.Query<TwitterAccount> ("SELECT * from TwitterAccount")){
- var copy = account;
- var element = new AccountElement (account);
- element.Tapped += delegate {
- DismissModalViewControllerAnimated (true);
-
- TwitterAccount.SetDefault (copy);
- Util.MainAppDelegate.Account = copy;
+ lock (Database.Main){
+ foreach (var account in Database.Main.Query<TwitterAccount> ("SELECT * from TwitterAccount")){
+ var copy = account;
+ var element = new AccountElement (account);
+ element.Tapped += delegate {
+ DismissModalViewControllerAnimated (true);
+
+ TwitterAccount.SetDefault (copy);
+ Util.MainAppDelegate.Account = copy;
+ };
+ section.Add (element);
};
- section.Add (element);
- };
+ }
var addAccount = new StringElement (Locale.GetText ("Add account"));
addAccount.Tapped += delegate {
Util.MainAppDelegate.AddAccount (this, delegate {
View
3 TweetStation/UI/Composer.cs
@@ -524,7 +524,8 @@ static void Init ()
if (inited)
return;
inited = true;
- Database.Main.CreateTable<Draft> ();
+ lock (Database.Main)
+ Database.Main.CreateTable<Draft> ();
}
[PrimaryKey]
View
4 TweetStation/UI/DetailTweetViewController.cs
@@ -203,7 +203,9 @@ public DetailTweetView (RectangleF rect, Tweet tweet, TweetView.TappedEvent hand
Util.MainAppDelegate.FavoriteChanged (tweet);
TwitterAccount.CurrentAccount.Post (String.Format ("http://api.twitter.com/1/favorites/{0}/{1}.json", tweet.Favorited ? "create" : "destroy", tweet.Id),"");
UpdateButtonImage (tweet);
- tweet.Replace (Database.Main);
+
+ lock (Database.Main)
+ tweet.Replace (Database.Main);
};
AddSubview (buttonView);
View
3 TweetStation/UI/FullProfileView.cs
@@ -66,7 +66,8 @@ void ProcessUserReturn (byte [] res, string diagMsg)
Root = Util.MakeError (diagMsg);
return;
}
- user = User.LoadUsers (new MemoryStream (res)).FirstOrDefault ();
+ lock (Database.Main)
+ user = User.UnlockedLoadUsers (new MemoryStream (res)).FirstOrDefault ();
if (user == null)
Root = Util.MakeError (diagMsg);
else
View
3 TweetStation/UI/Searches.cs
@@ -152,7 +152,8 @@ public override SearchMirrorElement MakeMirror ()
public override void PopulateSearch (Section entries)
{
- entries.Add (from x in Database.Main.Query<User> ("SELECT * from User ORDER BY Screenname")
+ lock (Database.Main)
+ entries.Add (from x in Database.Main.Query<User> ("SELECT * from User ORDER BY Screenname")
select (Element) new UserElement (x));
}
View
26 TweetStation/UI/Timeline.cs
@@ -145,7 +145,7 @@ public TimelineViewController (string title, TweetKind kind, bool pushing) : bas
// that the timeline is continuous
//
bool continuous;
- IEnumerable<Element> FetchTweets (int limit, long lastId, int skip)
+ IEnumerable<Element> UnlockedFetchTweets (int limit, long lastId, int skip)
{
continuous = false;
foreach (var tweet in Database.Main.Query<Tweet> (
@@ -172,7 +172,11 @@ IEnumerable<Element> FetchTweets (int limit, long lastId, int skip)
public override void ReloadTimeline ()
{
long? since = null;
- var res = Database.Main.Query<Tweet> ("SELECT Id FROM Tweet WHERE LocalAccountId = ? AND Kind = ? ORDER BY Id DESC LIMIT 1", Account.LocalAccountId, kind).FirstOrDefault ();
+ Tweet res;
+
+ lock (Database.Main)
+ res = Database.Main.Query<Tweet> ("SELECT Id FROM Tweet WHERE LocalAccountId = ? AND Kind = ? ORDER BY Id DESC LIMIT 1", Account.LocalAccountId, kind).FirstOrDefault ();
+
if (res != null){
// This should return one overlapping value.
since = res.Id - 1;
@@ -183,9 +187,6 @@ public override void ReloadTimeline ()
void DownloadTweets (int insertPoint, long? since, long? max_id, Element removeOnInsert)
{
- //if (kind != TweetKind.Home)
- // return;
-
Account.ReloadTimeline (kind, since, max_id, count => {
mainSection.Remove (removeOnInsert);
if (count == -1){
@@ -201,7 +202,13 @@ void DownloadTweets (int insertPoint, long? since, long? max_id, Element removeO
long lastId = GetTableTweetId (insertPoint == 0 ? 0 : insertPoint-1) ?? 0;
continuous = false;
- int nParsed = mainSection.Insert (insertPoint, UITableViewRowAnimation.None, FetchTweets (count, lastId, insertPoint));
+ int nParsed;
+
+ Util.ReportTime ("Before Loading tweet from DB");
+ lock (Database.Main)
+ nParsed = mainSection.Insert (insertPoint, UITableViewRowAnimation.None, UnlockedFetchTweets (count, lastId, insertPoint));
+ Util.ReportTime ("Time spent loading tweets");
+
NavigationController.TabBarItem.BadgeValue = (nParsed > 1) ? nParsed.ToString () : null;
if (!continuous){
@@ -228,9 +235,10 @@ void DownloadTweets (int insertPoint, long? since, long? max_id, Element removeO
protected override void ResetState ()
{
- mainSection = new Section () {
- FetchTweets (200, 0, 0)
- };
+ lock (Database.Main)
+ mainSection = new Section () {
+ UnlockedFetchTweets (200, 0, 0)
+ };
Root = new RootElement (timelineTitle) {
UnevenRows = true
View
20 TweetStation/UI/User.cs
@@ -95,15 +95,17 @@ public StreamedUserViewController (string title, string url, User reference) : b
protected override void PopulateRootFrom (byte [] result)
{
- Database.Main.Execute ("BEGIN");
- var userStream = User.LoadUsers (new MemoryStream (result));
-
- Root = new RootElement (StreamedTitle){
- new Section () {
- from user in userStream select (Element) new UserElement (user)
- }
- };
- Database.Main.Execute ("END");
+ lock (Database.Main){
+ Database.Main.Execute ("BEGIN");
+ var userStream = User.UnlockedLoadUsers (new MemoryStream (result));
+
+ Root = new RootElement (StreamedTitle){
+ new Section () {
+ from user in userStream select (Element) new UserElement (user)
+ }
+ };
+ Database.Main.Execute ("END");
+ }
}
}

0 comments on commit 43c664d

Please sign in to comment.