[smuxi] Frontend(-GNOME): revert ChatViewSyncManager's state machine



commit b1a904951e9bfa024bb4fd430982bda3bb05b2f6
Author: Mirco Bauer <meebey meebey net>
Date:   Tue Sep 9 11:39:01 2014 +0200

    Frontend(-GNOME): revert ChatViewSyncManager's state machine
    
    The state machine has a corner case bug with the synchronization between
    QueueAdd() and QueueSync(). Sync can be executed before the Added/ReadyToSync
    state has been reached. The frontend is probably triggering this race condition
    and should be avoided if possible. If it can't be avoided then the state machine
    based sync manager needs to deal with this like the old sync manager did.
    
    Revert "Frontend: fixed Trace.Call() calls in ChatViewSyncManager"
    Revert "Frontend: enhanced exception messages of state machine"
    Revert "Frontend: removed unreachable code from State.ctor()"
    Revert "Frontend: fixed SyncInfo.SyncRoot being never initialized"
    Revert "Frontend: fixed logger calls"
    Revert "Frontend: updated copyright info"
    Revert "Frontend: coding standards fixes"
    Revert "Frontend: stop using Activator.CreateInstance()"
    Revert "Frontend: make ChatViewSyncManager.SetState() internal"
    Revert "Frontend: avoid logic in State ctors"
    Revert "Frontend: catch all exceptions in thread pool workers to prevent AppDomain shutdowns"
    Revert "Frontend: fixed leaking ChatViewSyncManager.Chats"
    Revert "Frontend: moved private methods into worker dispatch context as they won't be reused"
    Revert "Frontend: /rejoin didn't work with ChatViewSyncManager due calling Sync while already synced"
    Revert "Frontend(-GNOME): ChatViewSyncManager's internals refactored into a state-machine"
    
    This reverts commit 1495be27eece4456ef4e340228b169bd2e199e34.
    This reverts commit a204e6f9f5c6c6b33b4962c01bdcd006d9dd941b.
    This reverts commit b44ee256eb6687561bb0e1163cbdd3499c84e772.
    This reverts commit 0f999e6ae60869d66cf9c833c6faa96f52ff4028.
    This reverts commit 863f0a069652a66936912e8038b7551e84d91596.
    This reverts commit 64bb18419503c80087748ff4d3f1f2da0406c8dd.
    This reverts commit 3cedf73c6564c9613daf486e060ffecaab535d14.
    This reverts commit d626bf9f8227e68c5c7a216e4bdb476efb8b0597.
    This reverts commit 36b23ae0bbcf9b953cf538c86ccf73b31e44fcde.
    This reverts commit 5bd7b2ecb8b12ac30525cb8d4a9ac3350e996bf1.
    This reverts commit a5e8d5030d983f08e4a8eb55748c9c198fb7ba08.
    This reverts commit dce4f7b57efb6ca82d63d740d2879bf1f9582c73.
    This reverts commit eadcf35d829cf7610a4c31613f1c9e7b202789ae.
    This reverts commit ccd8981b3cb830eaba446f48425c6c8c35c402ab.
    This reverts commit cfddd43d9c096f56e62a5ca9947d6223beccd97d.

 src/Frontend-GNOME/ChatViewManager.cs |   36 +--
 src/Frontend/AssemblyInfo.cs          |    2 +-
 src/Frontend/ChatViewSyncManager.cs   |  645 ++++++++-------------------------
 3 files changed, 158 insertions(+), 525 deletions(-)
---
diff --git a/src/Frontend-GNOME/ChatViewManager.cs b/src/Frontend-GNOME/ChatViewManager.cs
index 0551d59..afae62a 100644
--- a/src/Frontend-GNOME/ChatViewManager.cs
+++ b/src/Frontend-GNOME/ChatViewManager.cs
@@ -114,7 +114,6 @@ namespace Smuxi.Frontend.Gnome
             SyncManager = new ChatViewSyncManager();
             SyncManager.ChatAdded += OnChatAdded;
             SyncManager.ChatSynced += OnChatSynced;
-            SyncManager.ChatRemoved += OnChatRemoved;
             SyncManager.WorkerException += OnWorkerException;
         }
 
@@ -132,33 +131,25 @@ namespace Smuxi.Frontend.Gnome
 
         public override void RemoveChat(ChatModel chat)
         {
-            if (chat == null) {
-#if LOG4NET
+            ChatView chatView = GetChat(chat);
+            if (chatView == null) {
+ #if LOG4NET
                 f_Logger.Warn("RemoveChat(): chatView is null!");
 #endif
                 return;
             }
-            SyncManager.QueueRemove(chat);
-        }
 
-        void OnChatRemoved(object sender, ChatViewRemovedEventArgs e)
-        {
-            var chatView = (ChatView)e.ChatView;
-            GLib.Idle.Add(delegate {
-                f_Notebook.RemovePage(f_Notebook.PageNum(chatView));
-                TreeView.Remove(chatView);
-                f_Chats.Remove(chatView);
-                SyncedChats.Remove(chatView);
+            f_Notebook.RemovePage(f_Notebook.PageNum(chatView));
+            TreeView.Remove(chatView);
+            f_Chats.Remove(chatView);
+            SyncManager.Remove(chat);
+            SyncedChats.Remove(chatView);
 
-                if (ChatRemoved != null) {
-                    ChatRemoved(this, new ChatViewManagerChatRemovedEventArgs(chatView));
-                }
+            if (ChatRemoved != null) {
+                ChatRemoved(this, new ChatViewManagerChatRemovedEventArgs(chatView));
+            }
 
-                chatView.Dispose();
-                
-                SyncManager.QueueRemoveFinished(chatView.ChatModel);
-                return false;
-            });
+            chatView.Dispose();
         }
 
         public override void EnableChat(ChatModel chat)
@@ -302,7 +293,7 @@ namespace Smuxi.Frontend.Gnome
                 TreeView.Append(chatView);
 
                 // notify the sync manager that the ChatView is ready to be synced
-                SyncManager.QueueReadyToSync(chatView);
+                SyncManager.ReleaseSync(chatView);
 
 #if GTK_SHARP_2_10
                 f_Notebook.SetTabReorderable(chatView, true);
@@ -373,7 +364,6 @@ namespace Smuxi.Frontend.Gnome
                 if (ChatSynced != null) {
                     ChatSynced(this, new ChatViewManagerChatSyncedEventArgs(chatView));
                 }
-                SyncManager.QueueSyncFinished(chatView);
                 return false;
             });
         }
diff --git a/src/Frontend/AssemblyInfo.cs b/src/Frontend/AssemblyInfo.cs
index bf78364..10e5851 100644
--- a/src/Frontend/AssemblyInfo.cs
+++ b/src/Frontend/AssemblyInfo.cs
@@ -32,7 +32,7 @@ using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
 [assembly: AssemblyTitle("Smuxi - frontend library")]
-[assembly: AssemblyCopyright("2005-2014 (C) Mirco Bauer <meebey meebey net>")]
+[assembly: AssemblyCopyright("2005-2013 (C) Mirco Bauer <meebey meebey net>")]
 
 [assembly: AssemblyDelaySign(false)]
 [assembly: AssemblyKeyFile("")]
diff --git a/src/Frontend/ChatViewSyncManager.cs b/src/Frontend/ChatViewSyncManager.cs
index f211893..7a726b4 100644
--- a/src/Frontend/ChatViewSyncManager.cs
+++ b/src/Frontend/ChatViewSyncManager.cs
@@ -1,7 +1,6 @@
 // Smuxi - Smart MUltipleXed Irc
 //
-// Copyright (c) 2011, 2013-2014 Mirco Bauer <meebey meebey net>
-// Copyright (c) 2014 Oliver Schneider <mail oli-obk de>
+// Copyright (c) 2011, 2013 Mirco Bauer <meebey meebey net>
 //
 // Full GPL License: <http://www.gnu.org/licenses/gpl.txt>
 //
@@ -33,342 +32,12 @@ namespace Smuxi.Frontend
 #if LOG4NET
         private static readonly log4net.ILog Logger = 
log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
 #endif
-
-        /*
-         * TODO DisableChat is not in this system
-         * TODO DisableChat should SyncState ---Disable---> WaitingForSyncState
-         *
-         * InitialState ---Add---> AddedState
-         * AddedState ---Sync---> SyncQueuedState
-         *                  ---ReadyToSync---> WaitingForSyncState
-         * SyncQueuedState ---ReadyToSync---> SyncingState
-         * WaitingForSyncState ---Sync---> SyncingState
-         * SyncingState ---SyncFinished---> SyncState
-         * SyncState ---Sync---> SyncingState
-         *
-         * AddedState ---Remove---> RemovingState
-         * SyncQueuedState ---Remove---> RemovingState
-         * WaitingForSyncState ---Remove---> RemovingState
-         * SyncingState ---Remove---> RemovingState
-         * SyncState ---Remove---> RemovingState
-         * RemovingState ---RemoveFinished---> KILL IT WITH FIRE
-         */
-
-        abstract class State
-        {
-            protected SyncInfo SyncInfo { get; private set; }
-
-            protected State(SyncInfo chat)
-            {
-                if (chat == null) {
-                    throw new ArgumentNullException("chat");
-                }
-                SyncInfo = chat;
-            }
-
-            public virtual void Init()
-            {
-            }
-
-            public virtual void ExecuteAdd()
-            {
-                throw new InvalidStateException(
-                    String.Format("<{0}> could not add in {1}",
-                                  SyncInfo.ChatModel, GetType().Name)
-                );
-            }
-
-            public virtual void ExecuteRemove()
-            {
-                throw new InvalidStateException(
-                    String.Format("<{0}> could not remove in {1}",
-                                  SyncInfo.ChatModel, GetType().Name)
-                );
-            }
-
-            public virtual void ExecuteRemoveFinished()
-            {
-                throw new InvalidStateException(
-                    String.Format("<{0}> could not finished remove in {1}",
-                                  SyncInfo.ChatModel, GetType().Name)
-                );
-            }
-
-            public virtual void ExecuteSync()
-            {
-                throw new InvalidStateException(
-                    String.Format("<{0}> could not sync in {1}",
-                                  SyncInfo.ChatModel, GetType().Name)
-                );
-            }
-
-            public virtual void ExecuteReadyToSync()
-            {
-                throw new InvalidStateException(
-                    String.Format("<{0}> could not be ready to sync in {1}",
-                                  SyncInfo.ChatModel, GetType().Name)
-                );
-            }
-
-            public virtual void ExecuteSyncFinished()
-            {
-                throw new InvalidStateException(
-                    String.Format("<{0}> could not finish sync in {1}",
-                                  SyncInfo.ChatModel, GetType().Name)
-                );
-            }
-        }
-
-        class InitialState : State
-        {
-            public InitialState(SyncInfo chat) : base(chat)
-            {
-            }
-
-            public override void ExecuteAdd()
-            {
-                Trace.Call();
-                SyncInfo.State = new AddedState(SyncInfo);
-            }
-        }
-
-        class AddedState : State
-        {
-            public AddedState(SyncInfo chat) : base(chat)
-            {
-            }
-
-            public override void Init()
-            {
-#if LOG4NET
-                DateTime start = DateTime.UtcNow;
-#endif
-                // REMOTING CALL 1
-                var chatId = SyncInfo.ChatModel.ID;
-                // REMOTING CALL 2
-                var chatType = SyncInfo.ChatModel.ChatType;
-                // REMOTING CALL 3
-                var chatPosition = SyncInfo.ChatModel.Position;
-                // REMOTING CALL 4
-                var protocolManager = SyncInfo.ChatModel.ProtocolManager;
-                Type protocolManagerType = null;
-                if (protocolManager != null) {
-                    protocolManagerType = protocolManager.GetType();
-                }
-#if LOG4NET
-                DateTime stop = DateTime.UtcNow;
-                double duration = stop.Subtract(start).TotalMilliseconds;
-                Logger.Debug("Add() done, syncing took: " +
-                             Math.Round(duration) + " ms");
-#endif
-                SyncInfo.Manager.OnChatAdded(SyncInfo.ChatModel,
-                                             chatId,
-                                             chatType,
-                                             chatPosition,
-                                             protocolManager,
-                                             protocolManagerType);
-            }
-
-            public override void ExecuteReadyToSync()
-            {
-                Trace.Call();
-                SyncInfo.State = new WaitingForSyncState(SyncInfo);
-            }
-
-            public override void ExecuteSync()
-            {
-                Trace.Call();
-                SyncInfo.State = new SyncQueuedState(SyncInfo);
-            }
-
-            public override void ExecuteRemove()
-            {
-                Trace.Call();
-                SyncInfo.State = new RemovingState(SyncInfo);
-            }
-        }
-
-        class SyncQueuedState : State
-        {
-            public SyncQueuedState(SyncInfo chat) : base(chat)
-            {
-            }
-
-            public override void ExecuteReadyToSync()
-            {
-                Trace.Call();
-                SyncInfo.State = new SyncingState(SyncInfo);
-            }
-        }
-
-        class WaitingForSyncState : State
-        {
-            public WaitingForSyncState(SyncInfo chat) : base(chat)
-            {
-            }
-
-            public override void ExecuteSync()
-            {
-                Trace.Call();
-                SyncInfo.State = new SyncingState(SyncInfo);
-            }
-
-            public override void ExecuteRemove()
-            {
-                Trace.Call();
-                SyncInfo.State = new RemovingState(SyncInfo);
-            }
-        }
-
-        class SyncingState : State
-        {
-            public SyncingState(SyncInfo chat) : base(chat)
-            {
-            }
-
-            public override void Init()
-            {
-#if LOG4NET
-                DateTime start = DateTime.UtcNow;
-#endif
-                SyncInfo.ChatView.Sync();
-#if LOG4NET
-                DateTime stop = DateTime.UtcNow;
-                double duration = stop.Subtract(start).TotalMilliseconds;
-                Logger.Debug("Sync() <" + SyncInfo.ChatView.ID + ">.Sync() done, " +
-                             " syncing took: " + Math.Round(duration) + " ms");
-#endif
-                SyncInfo.Manager.OnChatSynced(SyncInfo.ChatView);
-            }
-
-            public override void ExecuteSyncFinished()
-            {
-                Trace.Call();
-                SyncInfo.State = new SyncState(SyncInfo);
-            }
-        }
-
-        class SyncState : State
-        {
-            public SyncState(SyncInfo chat) : base(chat)
-            {
-            }
-
-            public override void ExecuteRemove()
-            {
-                Trace.Call();
-                SyncInfo.State = new RemovingState(SyncInfo);
-            }
-
-            public override void ExecuteSync()
-            {
-                // this happens for example in /rejoin
-                Trace.Call();
-                SyncInfo.State = new SyncingState(SyncInfo);
-            }
-        }
-
-        class RemovingState : State
-        {
-            public RemovingState(SyncInfo chat) : base(chat)
-            {
-            }
-
-            public override void Init()
-            {
-                SyncInfo.Manager.OnChatRemoved(SyncInfo.ChatView);
-            }
-
-            public override void ExecuteRemoveFinished()
-            {
-                Trace.Call();
-                SyncInfo.Manager.Remove(SyncInfo.ChatModel);
-            }
-
-            public override void ExecuteReadyToSync()
-            {
-                Trace.Call();
-                // no-op
-                // this can happen when you add and remove very fast after each other.
-                // the add callback might be in a different thread and therefore be delayed
-            }
-        }
-
-        class SyncInfo
-        {
-            State f_State;
-            object SyncRoot { get; set; }
-            internal ChatViewSyncManager Manager { get; set; }
-            internal ChatModel ChatModel { get; set; }
-            internal IChatView ChatView { get; set; }
-
-            internal State State {
-                get {
-                    return f_State;
-                }
-                set {
-                    f_State = value;
-                    f_State.Init();
-                }
-            }
-
-            public SyncInfo(ChatViewSyncManager manager, ChatModel chatModel)
-            {
-                Manager = manager;
-                ChatModel = chatModel;
-                SyncRoot = new object();
-                State = new InitialState(this);
-            }
-
-            public void ExecuteAdd()
-            {
-                lock (SyncRoot) {
-                    State.ExecuteAdd();
-                }
-            }
-
-            public void ExecuteRemove()
-            {
-                lock (SyncRoot) {
-                    State.ExecuteRemove();
-                }
-            }
-
-            public void ExecuteRemoveFinished()
-            {
-                lock (SyncRoot) {
-                    State.ExecuteRemoveFinished();
-                }
-            }
-
-            public void ExecuteSync()
-            {
-                lock (SyncRoot) {
-                    State.ExecuteSync();
-                }
-            }
-
-            public void ExecuteReadyToSync()
-            {
-                lock (SyncRoot) {
-                    State.ExecuteReadyToSync();
-                }
-            }
-
-            public void ExecuteSyncFinished()
-            {
-                lock (SyncRoot) {
-                    State.ExecuteSyncFinished();
-                }
-            }
-        }
-
         ThreadPoolQueue WorkerQueue { set; get; }
-        Dictionary<object, SyncInfo> SyncInfos { set; get; }
+        Dictionary<object, AutoResetEvent> SyncWaitQueue { set; get; }
+        Dictionary<object, IChatView> SyncReleaseQueue { set; get; }
 
         public event EventHandler<ChatViewAddedEventArgs>  ChatAdded;
         public event EventHandler<ChatViewSyncedEventArgs> ChatSynced;
-        public event EventHandler<ChatViewRemovedEventArgs> ChatRemoved;
         public event EventHandler<WorkerExceptionEventArgs> WorkerException;
 
         public ChatViewSyncManager()
@@ -376,35 +45,48 @@ namespace Smuxi.Frontend
             WorkerQueue = new ThreadPoolQueue() {
                 MaxWorkers = 4
             };
-            SyncInfos = new Dictionary<object, SyncInfo>();
+            SyncWaitQueue = new Dictionary<object, AutoResetEvent>();
+            SyncReleaseQueue = new Dictionary<object, IChatView>();
         }
 
-        /// <remarks>
-        /// This method is thread safe.
-        /// </remarks>
-        void Remove(ChatModel chatModel)
+        public void Add(ChatModel chatModel)
         {
             Trace.Call(chatModel);
 
             if (chatModel == null) {
-
                 throw new ArgumentNullException("chatModel");
             }
 
-            var chatKey = GetChatKey(chatModel);
 #if LOG4NET
-            Logger.DebugFormat("Remove() <{0}> removing from release queue",
-                               chatKey);
+            DateTime start = DateTime.UtcNow;
 #endif
-            lock (SyncInfos) {
-                SyncInfos.Remove(chatKey);
+            // REMOTING CALL 1
+            var chatId = chatModel.ID;
+            // REMOTING CALL 2
+            var chatType = chatModel.ChatType;
+            // REMOTING CALL 3
+            var chatPosition = chatModel.Position;
+            // REMOTING CALL 4
+            IProtocolManager protocolManager = chatModel.ProtocolManager;
+            Type protocolManagerType = null;
+            if (protocolManager != null) {
+                protocolManagerType = protocolManager.GetType();
             }
+#if LOG4NET
+            DateTime stop = DateTime.UtcNow;
+            double duration = stop.Subtract(start).TotalMilliseconds;
+            Logger.Debug("Add() done, syncing took: " +
+                         Math.Round(duration) + " ms");
+#endif
+
+            OnChatAdded(chatModel, chatId, chatType, chatPosition,
+                        protocolManager, protocolManagerType);
         }
 
         /// <remarks>
         /// This method is thread safe.
         /// </remarks>
-        public void QueueAdd(ChatModel chatModel)
+        public void Remove(ChatModel chatModel)
         {
             Trace.Call(chatModel);
 
@@ -412,49 +94,42 @@ namespace Smuxi.Frontend
                 throw new ArgumentNullException("chatModel");
             }
 
-            var chat = GetOrCreateChat(chatModel);
-            WorkerQueue.Enqueue(delegate {
-                try {
-                    chat.ExecuteAdd();
-                } catch (Exception ex) {
+            var chatKey = GetChatKey(chatModel);
 #if LOG4NET
-                    Logger.Error("QueueAdd(): ExecuteAdd() threw exception!" , ex);
+            Logger.DebugFormat("Remove() <{0}> removing from release queue",
+                               chatKey);
 #endif
-                    OnWorkerException(chat.ChatModel, ex);
-                }
-            });
+            lock (SyncReleaseQueue) {
+                SyncReleaseQueue.Remove(chatKey);
+            }
         }
 
-        public void QueueRemove(ChatModel chatModel)
+        public void Sync(IChatView chatView)
         {
-            Trace.Call(chatModel);
+            Trace.Call(chatView);
 
-            if (chatModel == null) {
-                throw new ArgumentNullException("chatModel");
+            if (chatView == null) {
+                throw new ArgumentNullException("chatView");
             }
 
-            SyncInfo chat;
-            if (!TryGetChat(chatModel, out chat)) {
 #if LOG4NET
-                Logger.WarnFormat("QueueRemove() <{0}> already removed or " +
-                                  "never existed", chatModel);
+            DateTime start = DateTime.UtcNow;
 #endif
-                return;
-            }
-            WorkerQueue.Enqueue(delegate {
-                try {
-                    chat.ExecuteRemove();
-                } catch (Exception ex) {
+            chatView.Sync();
 #if LOG4NET
-                    Logger.Error("QueueRemove(): ExecuteRemove() threw " +
-                                 "exception!", ex);
+            DateTime stop = DateTime.UtcNow;
+            double duration = stop.Subtract(start).TotalMilliseconds;
+            Logger.Debug("Sync() <" + chatView.ID + ">.Sync() done, " +
+                         " syncing took: " + Math.Round(duration) + " ms");
 #endif
-                    OnWorkerException(chat.ChatModel, ex);
-                }
-            });
+
+            OnChatSynced(chatView);
         }
 
-        public void QueueRemoveFinished(ChatModel chatModel)
+        /// <remarks>
+        /// This method is thread safe.
+        /// </remarks>
+        public void QueueAdd(ChatModel chatModel)
         {
             Trace.Call(chatModel);
 
@@ -462,49 +137,18 @@ namespace Smuxi.Frontend
                 throw new ArgumentNullException("chatModel");
             }
 
-            SyncInfo chat;
-            if (!TryGetChat(chatModel, out chat)) {
+            var chatKey = GetChatKey(chatModel);
+            lock (SyncWaitQueue) {
+                SyncWaitQueue.Add(chatKey, new AutoResetEvent(false));
 #if LOG4NET
-                Logger.WarnFormat("QueueRemoveFinished() <{0}> already " +
-                                  "removed or never existed", chatModel);
+                Logger.Debug("QueueAdd() <" + chatKey + "> created sync lock");
 #endif
-                return;
             }
-
             WorkerQueue.Enqueue(delegate {
-                try {
-                    chat.ExecuteRemoveFinished();
-                } catch (Exception ex) {
-#if LOG4NET
-                    Logger.Error("QueueRemoveFinished(): " +
-                                 "ExecuteRemoveFinished() threw exception!", ex);
-#endif
-                    OnWorkerException(chat.ChatModel, ex);
-                }
+                AddWorker(chatModel);
             });
         }
 
-        bool TryGetChat(ChatModel chatModel, out SyncInfo chat)
-        {
-            var key = GetChatKey(chatModel);
-            lock (SyncInfos) {
-                return SyncInfos.TryGetValue(key, out chat);
-            }
-        }
-
-        SyncInfo GetOrCreateChat(ChatModel chatModel)
-        {
-            var key = GetChatKey(chatModel);
-            lock (SyncInfos) {
-                SyncInfo chat;
-                if (!SyncInfos.TryGetValue(key, out chat)) {
-                    chat = new SyncInfo(this, chatModel);
-                    SyncInfos.Add(key, chat);
-                }
-                return chat;
-            }
-        }
-
         /// <remarks>
         /// This method is thread safe.
         /// </remarks>
@@ -516,31 +160,15 @@ namespace Smuxi.Frontend
                 throw new ArgumentNullException("chatModel");
             }
 
-            SyncInfo chat;
-            if (!TryGetChat(chatModel, out chat)) {
-#if LOG4NET
-                Logger.WarnFormat("QueueSync() <{0}> unknow chat, cannot sync",
-                                  chatModel);
-#endif
-                return;
-            }
-
             WorkerQueue.Enqueue(delegate {
-                try {
-                    chat.ExecuteSync();
-                } catch (Exception ex) {
-#if LOG4NET
-                    Logger.Error("QueueSync(): ExecuteSync() threw exception!", ex);
-#endif
-                    OnWorkerException(chat.ChatModel, ex);
-                }
+                SyncWorker(chatModel);
             });
         }
 
         /// <remarks>
         /// This method is thread safe.
         /// </remarks>
-        public void QueueReadyToSync(IChatView chatView)
+        public void ReleaseSync(IChatView chatView)
         {
             Trace.Call(chatView);
 
@@ -548,67 +176,37 @@ namespace Smuxi.Frontend
                 throw new ArgumentNullException("chatView");
             }
 
-            SyncInfo chat;
-            if (!TryGetChat(chatView.ChatModel, out chat)) {
+            var chatKey = GetChatKey(chatView.ChatModel);
 #if LOG4NET
-                Logger.WarnFormat("QueueReadyToSync() <{0}> unknow chat, " +
-                                  "something is wrong", chatView.ChatModel);
+            Logger.Debug("ReleaseSync() <" + chatKey + "> releasing " +
+                         "<" + chatView.ID + ">");
 #endif
-                return;
+            lock (SyncReleaseQueue) {
+                SyncReleaseQueue.Add(chatKey, chatView);
             }
-            chat.ChatView = chatView;
-
-            WorkerQueue.Enqueue(delegate {
-                try {
-                    chat.ExecuteReadyToSync();
-                } catch (Exception ex) {
-#if LOG4NET
-                    Logger.Error("QueueReadyToSync(): ExecuteReadyToSync() " +
-                                 "threw exception!", ex);
-#endif
-                    OnWorkerException(chat.ChatModel, ex);
-                }
-            });
-        }
-
-        public void QueueSyncFinished(IChatView chatView)
-        {
-            Trace.Call(chatView);
-
-            if (chatView == null) {
-                throw new ArgumentNullException("chatView");
+            AutoResetEvent syncWait = null;
+            lock (SyncWaitQueue) {
+                SyncWaitQueue.TryGetValue(chatKey, out syncWait);
             }
-
-            SyncInfo chat;
-            if (!TryGetChat(chatView.ChatModel, out chat)) {
+            if (syncWait == null) {
 #if LOG4NET
-                Logger.WarnFormat(
-                    "QueueSyncFinished() <{0}> unknow chat, something is wrong",
-                    chatView.ChatModel
-                );
+                Logger.Error("ReleaseSync(<" + chatView.ID + ">): failed to release " +
+                             "<" + chatKey + "> as syncWait is null!");
 #endif
                 return;
             }
-
-            WorkerQueue.Enqueue(delegate {
-                try {
-                    chat.ExecuteSyncFinished();
-                } catch (Exception ex) {
-#if LOG4NET
-                    Logger.Error("QueueSyncFinished(): ExecuteSyncFinished() " +
-                                 "threw exception!", ex);
-#endif
-                    OnWorkerException(chat.ChatModel, ex);
-                }
-            });
+            // release the sync worker
+            syncWait.Set();
         }
 
         public void Clear()
         {
             Trace.Call();
 
-            lock (SyncInfos) {
-                SyncInfos.Clear();
+            lock (SyncWaitQueue)
+            lock (SyncReleaseQueue) {
+                SyncWaitQueue.Clear();
+                SyncReleaseQueue.Clear();
             }
         }
 
@@ -622,6 +220,76 @@ namespace Smuxi.Frontend
             return chatModel;
         }
 
+        void AddWorker(ChatModel chatModel)
+        {
+            try {
+                Add(chatModel);
+            } catch (Exception ex) {
+#if LOG4NET
+                Logger.Error("AddWorker(): Add() threw exception!" , ex);
+#endif
+                if (WorkerException != null) {
+                    WorkerException(
+                        this,
+                        new WorkerExceptionEventArgs(chatModel, ex)
+                    );
+                }
+                OnWorkerException(chatModel, ex);
+            }
+        }
+
+        void SyncWorker(ChatModel chatModel)
+        {
+            try {
+                var chatKey = GetChatKey(chatModel);
+                AutoResetEvent syncWait = null;
+                lock (SyncWaitQueue) {
+                    SyncWaitQueue.TryGetValue(chatKey, out syncWait);
+                }
+                if (syncWait != null) {
+#if LOG4NET
+                    Logger.Debug("SyncWorker() <" + chatKey + "> waiting for " +
+                                "sync lock release...");
+#endif
+                    // This chat was queued by QueueAdd() thus we need to wait
+                    // till the ChatView is created and ready to be synced
+                    syncWait.WaitOne();
+#if LOG4NET
+                    Logger.Debug("SyncWorker() <" + chatKey + "> " +
+                                 "sync lock released");
+#endif
+
+                    // no longer need the sync lock
+                    lock (SyncWaitQueue) {
+                        SyncWaitQueue.Remove(chatKey);
+                    }
+                }
+
+                IChatView chatView = null;
+                lock (SyncReleaseQueue) {
+                    if (!SyncReleaseQueue.TryGetValue(chatKey, out chatView)) {
+#if LOG4NET
+                        Logger.Warn("SyncWorker(): chatView is null! " +
+                                    "probably a reconnect, bailing out...");
+#endif
+                        return;
+                    }
+                    // no longer need the release slot
+                    // BUG: this breaks re-syncing an existing chat! For that
+                    // reason the frontend _must_ notify us via Remove() if the
+                    // chat sync state is no longer needed
+                    //SyncReleaseQueue.Remove(chatKey);
+                }
+
+                Sync(chatView);
+            } catch (Exception ex) {
+#if LOG4NET
+                Logger.Error("SyncWorker(): Exception!", ex);
+#endif
+                OnWorkerException(chatModel, ex);
+            }
+        }
+
         void OnChatAdded(ChatModel chatModel, string chatId,
                          ChatType chatType, int chatPosition,
                          IProtocolManager protocolManager,
@@ -643,13 +311,6 @@ namespace Smuxi.Frontend
             }
         }
 
-        void OnChatRemoved(IChatView chatView)
-        {
-            if (ChatRemoved != null) {
-                ChatRemoved(this, new ChatViewRemovedEventArgs(chatView));
-            }
-        }
-
         void OnWorkerException(ChatModel chatModel, Exception ex)
         {
             if (WorkerException != null) {
@@ -694,16 +355,6 @@ namespace Smuxi.Frontend
         }
     }
 
-    public class ChatViewRemovedEventArgs : EventArgs
-    {
-        public IChatView ChatView { get; private set; }
-
-        public ChatViewRemovedEventArgs(IChatView chatView)
-        {
-            ChatView = chatView;
-        }
-    }
-
     public class WorkerExceptionEventArgs : EventArgs
     {
         public ChatModel ChatModel { get; private set; }
@@ -715,12 +366,4 @@ namespace Smuxi.Frontend
             Exception = ex;
         }
     }
-
-    public class InvalidStateException : Exception
-    {
-        internal InvalidStateException(string msg) :
-                                  base(msg)
-        {
-        }
-    }
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]