public static WorkEvents GetEvent(long workid) { WorkEvents e; if (events.TryGetValue(workid, out e)) { return(e); } return(events[workid] = new WorkEvents()); }
public Task SetWorkChaptersAsync(IDictionary <long, WorkChapter> works) { return(Task.Run(() => { using (ReadWriteLock.WriteLock()) { long time = DateTime.UtcNow.ToUnixTime(); Dictionary <long, Work> newitems = new Dictionary <long, Work>(); bool do_delayed = false; foreach (var work in works) { if (work.Value.workid == 0) { work.Value.workid = work.Key; } else if (work.Value.workid != work.Key) { throw new ArgumentException("Value.workid != Key", nameof(works)); } if (!storage.TryGetValue(work.Key, out var old) || old.LessThan(work.Value)) { var seq = work.Value.seq; if (seq == null && old != null) { seq = old.Seq; } var w = new Work { workid = work.Key, number = work.Value.number, chapterid = work.Value.chapterid, location = work.Value.location, Timestamp = time, Seq = seq ?? 0, }; unsynced[work.Key] = newitems[work.Key] = storage[work.Key] = w; // Do a delayed since if we finished a chapter, or started a new one if (old == null || work.Value.location == null || work.Value.location == 0 || work.Value.number > old.number || work.Value.seq > old.Seq) { do_delayed = true; WorkEvents.TryGetEvent(work.Key)?.OnChapterNumChanged(this, w); } else { WorkEvents.TryGetEvent(work.Key)?.OnLocationChanged(this, w); } } } if (newitems.Count > 0) { App.Database.SaveItems(newitems.Values.ToReadOnly()); if (serversync == SyncState.Ready || serversync == SyncState.Delayed) { if (do_delayed) { DelayedSyncAsync(10 * 1000); } else { DoSyncAsync(); } } } } })); }
public void DoSyncAsync(bool force = false) { Task.Factory.StartNew(() => { using (ReadWriteLock.UpgradeableReadLock()) { var settings = new JsonSerializerSettings() { MissingMemberHandling = MissingMemberHandling.Ignore }; Dictionary <long, Work> items; Dictionary <long, Work> current; long time; long old_sync; Task <HttpResponseMessage> responseTask; HttpResponseMessage response; Task <string> contentTask; string content; using (ReadWriteLock.WriteLock()) { if (serversync == SyncState.Disabled) { EndSyncEvent?.Invoke(this, new EventArgs <bool>(false)); //console.warn("dosync: FAILED. No credentials"); return; } // if we have waiting readers do it now if (ReadWriteLock.WaitingReadCount > 0) { force = true; } // Enforce 5 minutes gap between server sync. Don't want to hammer the server while scrolling through a fic var now = DateTime.UtcNow.Ticks / 10000; if (!force && now < no_sync_until) { //console.log("dosync: have to wait %i for timeout", no_sync_until - now); DelayedSyncAsync((int)(no_sync_until - now)); return; } serversync = SyncState.Syncing; // set to syncing! if (DelayedSyncCTS != null) { DelayedSyncCTS.Cancel(); DelayedSyncCTS = null; } no_sync_until = now + 5 * 60 * 1000; BeginSyncEvent?.Invoke(this, EventArgs.Empty); //console.log("dosync: sending GET request"); responseTask = HttpClient.GetAsync(new Uri(url_base, "Values?after=" + last_sync)); responseTask.TryWait(); response = responseTask.IsFaulted ? null : responseTask.Result; if (response == null || !response.IsSuccessStatusCode) { using (ReadWriteLock.WriteLock()) { //console.error("dosync: FAILED %s", response.ReasonPhrase); if (response != null && response.StatusCode == HttpStatusCode.Forbidden) { serversync = SyncState.Disabled; } else { serversync = SyncState.Ready; } EndSyncEvent?.Invoke(this, new EventArgs <bool>(false)); return; } } contentTask = response.Content.ReadAsStringAsync(); contentTask.Wait(); content = contentTask.Result; old_sync = last_sync; try { items = JsonConvert.DeserializeObject <Dictionary <long, Work> >(content, settings); } catch (Exception e) { App.Log(e); serversync = SyncState.Disabled; EndSyncEvent?.Invoke(this, new EventArgs <bool>(false)); return; } Dictionary <long, Work> newitems = new Dictionary <long, Work>(); foreach (var item in items) { item.Value.workid = item.Key; // Highest time value of incoming item is our new sync time if (item.Value.Timestamp > last_sync) { last_sync = item.Value.Timestamp; } Work old = null; if (!storage.ContainsKey(item.Key) || (old = storage[item.Key]).LessThanOrEqual(item.Value)) { // Remove from unsynced list (if it exists) if (unsynced.ContainsKey(item.Key)) { unsynced.Remove(item.Key); } // Grab the new details newitems[item.Key] = storage[item.Key] = item.Value; if (old == null || item.Value.location == null || item.Value.location == 0 || (old != null && item.Value.number > old.number)) { WorkEvents.TryGetEvent(item.Key)?.OnChapterNumChanged(this, item.Value); } else { WorkEvents.TryGetEvent(item.Key)?.OnLocationChanged(this, item.Value); } } // This kinda shouldn't happen, but apparently it did... we can deal with it though else { // Update the timestamp to newer than newest if (storage[item.Key].Timestamp <= item.Value.Timestamp) { storage[item.Key].Timestamp = item.Value.Timestamp + 1; } else { item.Value.Timestamp += 1; } // set as unsynced unsynced[item.Key] = storage[item.Key]; } } App.Database.SaveItems(newitems.Values.ToReadOnly()); current = unsynced; unsynced = new Dictionary <long, Work>(); time = last_sync; if (current.Count == 0) { App.Database.SaveVariable("last_sync", last_sync); serversync = SyncState.Ready; EndSyncEvent?.Invoke(this, new EventArgs <bool>(true)); return; } } foreach (var item in current.Values) { if (item.Timestamp > time) { time = item.Timestamp; } } var json = JsonConvert.SerializeObject(current); var postBody = new StringContent(json, System.Text.Encoding.UTF8, "application/json"); responseTask = HttpClient.PostAsync(new Uri(url_base, "Values"), postBody); responseTask.TryWait(); response = responseTask.IsFaulted ? null : responseTask.Result; if (response == null || !response.IsSuccessStatusCode) { using (ReadWriteLock.WriteLock()) { //console.error("dosync: FAILED %s", response.ReasonPhrase); if (response != null && response.StatusCode == HttpStatusCode.Forbidden) { last_sync = 0; serversync = SyncState.Disabled; } else { last_sync = old_sync; serversync = SyncState.Ready; unsynced = current; } App.Database.SaveVariable("last_sync", last_sync); EndSyncEvent?.Invoke(this, new EventArgs <bool>(false)); return; } } contentTask = response.Content.ReadAsStringAsync(); contentTask.Wait(); content = contentTask.Result; try { items = JsonConvert.DeserializeObject <Dictionary <long, Work> >(content, settings); } catch (Exception e) { App.Log(e); using (ReadWriteLock.WriteLock()) { serversync = SyncState.Disabled; App.Database.SaveVariable("last_sync", 0L); last_sync = 0; EndSyncEvent?.Invoke(this, new EventArgs <bool>(false)); return; } } //console.log("dosync: SUCCESS. %i conflicted items", Object.keys(items).length); using (ReadWriteLock.WriteLock()) { if (items.Count > 0) { App.Database.SaveVariable("last_sync", 0L); last_sync = 0; DoSyncAsync(true); return; } if (time > last_sync) { last_sync = time; App.Database.SaveVariable("last_sync", time); } if (unsynced.Count > 0) { DoSyncAsync(true); } else { serversync = SyncState.Ready; EndSyncEvent?.Invoke(this, new EventArgs <bool>(true)); } } } }, TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning); }