From 4c7a99cff6e484e02021c0a930c6df63b14d235c Mon Sep 17 00:00:00 2001 From: Aviv Keshet Date: Wed, 18 Jul 2012 18:15:10 -0400 Subject: [PATCH 1/3] Proper handling of udp input port already being in use. --- .../AtticusServerCommunicator.cs | 8 ++- DataStructures/Timing/NetworkClockProvider.cs | 69 +++++++++++++++---- WordGenerator/Controls/MainClientForm.cs | 7 +- 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/AtticusServer/ServerRuntime/AtticusServerCommunicator.cs b/AtticusServer/ServerRuntime/AtticusServerCommunicator.cs index fba985e6..50b14e77 100644 --- a/AtticusServer/ServerRuntime/AtticusServerCommunicator.cs +++ b/AtticusServer/ServerRuntime/AtticusServerCommunicator.cs @@ -209,7 +209,13 @@ public AtticusServerCommunicator(ServerSettings settings) resetNetworkClocks(); DataStructures.Timing.NetworkClockProvider.registerStaticMessageLogHandler(this.messageLog); - DataStructures.Timing.NetworkClockProvider.startListener(DataStructures.Timing.NetworkClockEndpointInfo.HostTypes.Atticus_Server); + bool created = DataStructures.Timing.NetworkClockProvider.startListener(DataStructures.Timing.NetworkClockEndpointInfo.HostTypes.Atticus_Server); + if (!created) + { + messageLog(this, new MessageEvent("Unable to start network clock listener. Is it possible that a separate Cicero instance is running on this computer?", + 0, MessageEvent.MessageTypes.Error, MessageEvent.MessageCategories.Networking)); + + } diff --git a/DataStructures/Timing/NetworkClockProvider.cs b/DataStructures/Timing/NetworkClockProvider.cs index 0d44bca1..1be0d24f 100644 --- a/DataStructures/Timing/NetworkClockProvider.cs +++ b/DataStructures/Timing/NetworkClockProvider.cs @@ -54,20 +54,46 @@ private static void staticLogMessage(MessageEvent e) private static Dictionary providers = new Dictionary(); private static NetworkClockEndpointInfo.HostTypes myListenerType; - public static void startListener(NetworkClockEndpointInfo.HostTypes listenerType) { + /// + /// Returns true if successful, false otherwise. + /// + /// + /// + public static bool startListener(NetworkClockEndpointInfo.HostTypes listenerType) { lock (staticLockObj) { staticLogMessage(new MessageEvent("Starting network clock listener...", 0, MessageEvent.MessageTypes.Routine, MessageEvent.MessageCategories.SoftwareClock)); myListenerType = listenerType; + + // Check if a listener thread is running already. if (listenerThread != null) { throw new SoftwareClockProviderException("Attempted to start listener thread when already started."); } + + + // Open a UDP socket + staticLogMessage(new MessageEvent("Opening UDP input socket for network clock.", 1, MessageEvent.MessageTypes.Log, MessageEvent.MessageCategories.Networking)); + try + { + udpClient = new UdpClient(NetworkClockEndpointInfo.getListenerPort(myListenerType)); + } + catch (System.Net.Sockets.SocketException e) + { + staticLogMessage(new MessageEvent("Unable to open input socket for network clock due to socket exception: " + e.Message, + 0, MessageEvent.MessageTypes.Error, MessageEvent.MessageCategories.Networking)); + udpClient = null; + return false; + } + + + listenerThread = new Thread(listenerThreadProc); listenerThread.Start(); providers = new Dictionary(); - staticLogMessage(new MessageEvent("...done", 0, MessageEvent.MessageTypes.Routine, MessageEvent.MessageCategories.SoftwareClock)); + } + return true; } public static void shutDown() @@ -77,11 +103,17 @@ public static void shutDown() staticLogMessage(new MessageEvent("Shutting down network clock listener.", 0, MessageEvent.MessageTypes.Routine, MessageEvent.MessageCategories.SoftwareClock)); if (listenerThread == null) { - throw new SoftwareClockProviderException("Attempted to stop a listener that was not running."); + //throw new SoftwareClockProviderException("Attempted to stop a listener that was not running."); + return; } listenerThread.Abort(); listenerThread = null; + if (udpClient != null) + { + udpClient.Close(); + udpClient = null; + } List providerIDs = providers.Keys.ToList(); foreach (uint id in providerIDs) @@ -90,8 +122,6 @@ public static void shutDown() } providers = null; - - staticLogMessage(new MessageEvent("...done")); } } @@ -109,20 +139,33 @@ public static void registerStaticMessageLogHandler(EventHandler ha private static UdpClient udpClient; + + private static void listenerThreadProc() { - lock (staticLockObj) - { - staticLogMessage(new MessageEvent("Starting Network Clock listener thread...", 1, MessageEvent.MessageTypes.Log, MessageEvent.MessageCategories.Networking)); - udpClient = new UdpClient(NetworkClockEndpointInfo.getListenerPort(myListenerType)); - staticLogMessage(new MessageEvent("...done", 1, MessageEvent.MessageTypes.Log, MessageEvent.MessageCategories.Networking)); - } + staticLogMessage(new MessageEvent("Network Clock listener thread is alive", 1, MessageEvent.MessageTypes.Log, MessageEvent.MessageCategories.Networking)); + while (true) { + // Wait for a datagram from the udpClient. + // This makes use of a temporary local reference to udpClient so that we can be sure + // that even if the static udpClient member is changed when we don't hold the static lock + // we will at least run the callback on the correct instance of udpClient. + // (I didn't want to lock the receive callback on staticLockObj, because the callback may + // take arbitrarily long to return, and I don't want to be locked out of other static activities like + // shutting down the listener. + System.Net.IPEndPoint remoteEnd = null; - IAsyncResult result = udpClient.BeginReceive(null, null); - byte[] received = udpClient.EndReceive(result, ref remoteEnd); + IAsyncResult result; + byte[] received; + UdpClient udpClientNonstatic; + lock (staticLockObj) + { + udpClientNonstatic = udpClient; + result = udpClient.BeginReceive(null, null); + } + received = udpClientNonstatic.EndReceive(result, ref remoteEnd); if (received.Length != NetworkClockDatagram.datagramByteLength) { diff --git a/WordGenerator/Controls/MainClientForm.cs b/WordGenerator/Controls/MainClientForm.cs index 8a2be6a1..b83f8b35 100644 --- a/WordGenerator/Controls/MainClientForm.cs +++ b/WordGenerator/Controls/MainClientForm.cs @@ -590,7 +590,12 @@ private void mainClientForm_Load(object sender, EventArgs e) this.WindowState = FormWindowState.Maximized; formOpen = true; DataStructures.Timing.NetworkClockProvider.registerStaticMessageLogHandler(addMessageLogText); - DataStructures.Timing.NetworkClockProvider.startListener(DataStructures.Timing.NetworkClockEndpointInfo.HostTypes.Cicero_Client); + bool listenerCreated = + DataStructures.Timing.NetworkClockProvider.startListener(DataStructures.Timing.NetworkClockEndpointInfo.HostTypes.Cicero_Client); + if (!listenerCreated) + { + MessageBox.Show("Unable to start network clock listener. Is it possible that a separate Cicero instance is running on this computer?", "Unable to create clock listener."); + } /* CiceroSplashForm splash = new CiceroSplashForm(); splash.Show();*/ } From 295b6da371cba6c253fafc463f5ba69f4195a4b8 Mon Sep 17 00:00:00 2001 From: Aviv Keshet Date: Wed, 18 Jul 2012 18:15:40 -0400 Subject: [PATCH 2/3] Typo correction. --- AtticusServer/ServerRuntime/AtticusServerCommunicator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AtticusServer/ServerRuntime/AtticusServerCommunicator.cs b/AtticusServer/ServerRuntime/AtticusServerCommunicator.cs index 50b14e77..c1871e9c 100644 --- a/AtticusServer/ServerRuntime/AtticusServerCommunicator.cs +++ b/AtticusServer/ServerRuntime/AtticusServerCommunicator.cs @@ -212,7 +212,7 @@ public AtticusServerCommunicator(ServerSettings settings) bool created = DataStructures.Timing.NetworkClockProvider.startListener(DataStructures.Timing.NetworkClockEndpointInfo.HostTypes.Atticus_Server); if (!created) { - messageLog(this, new MessageEvent("Unable to start network clock listener. Is it possible that a separate Cicero instance is running on this computer?", + messageLog(this, new MessageEvent("Unable to start network clock listener. Is it possible that a separate Atticus instance is running on this computer?", 0, MessageEvent.MessageTypes.Error, MessageEvent.MessageCategories.Networking)); } From a59e67b021457858e2c6be6bb397e829ccd1f1e9 Mon Sep 17 00:00:00 2001 From: Aviv Keshet Date: Wed, 18 Jul 2012 18:31:53 -0400 Subject: [PATCH 3/3] Better behaving splash screens. --- AtticusServer/AtticusSplashForm.Designer.cs | 3 +-- AtticusServer/AtticusSplashForm.cs | 4 ++-- WordGenerator/Controls/Dialogs/CiceroSplashForm.Designer.cs | 3 +-- WordGenerator/Controls/Dialogs/CiceroSplashForm.cs | 4 ++-- WordGenerator/Controls/MainClientForm.cs | 5 +++-- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/AtticusServer/AtticusSplashForm.Designer.cs b/AtticusServer/AtticusSplashForm.Designer.cs index 2c6b7fc0..5ee5cb62 100644 --- a/AtticusServer/AtticusSplashForm.Designer.cs +++ b/AtticusServer/AtticusSplashForm.Designer.cs @@ -49,7 +49,7 @@ private void InitializeComponent() // timer1 // this.timer1.Enabled = true; - this.timer1.Interval = 6000; + this.timer1.Interval = 2000; this.timer1.Tick += new System.EventHandler(this.timer1_Tick); // // label1 @@ -74,7 +74,6 @@ private void InitializeComponent() this.Name = "AtticusSplashForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "AtticusSplashForm"; - this.TopMost = true; ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); diff --git a/AtticusServer/AtticusSplashForm.cs b/AtticusServer/AtticusSplashForm.cs index d48b2202..b67cfaae 100644 --- a/AtticusServer/AtticusSplashForm.cs +++ b/AtticusServer/AtticusSplashForm.cs @@ -26,9 +26,9 @@ private void timer1_Tick(object sender, EventArgs e) this.Close(); } - public AtticusSplashForm(bool runTimer) : this() + public AtticusSplashForm(bool autoCloseAfterTimeout) : this() { - this.timer1.Enabled = runTimer; + this.timer1.Enabled = autoCloseAfterTimeout; } } } \ No newline at end of file diff --git a/WordGenerator/Controls/Dialogs/CiceroSplashForm.Designer.cs b/WordGenerator/Controls/Dialogs/CiceroSplashForm.Designer.cs index 536ec608..bb2c46ff 100644 --- a/WordGenerator/Controls/Dialogs/CiceroSplashForm.Designer.cs +++ b/WordGenerator/Controls/Dialogs/CiceroSplashForm.Designer.cs @@ -49,7 +49,7 @@ private void InitializeComponent() // timer1 // this.timer1.Enabled = true; - this.timer1.Interval = 7000; + this.timer1.Interval = 2000; this.timer1.Tick += new System.EventHandler(this.timer1_Tick); // // label1 @@ -76,7 +76,6 @@ private void InitializeComponent() this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "CiceroSplashForm"; - this.TopMost = true; ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); diff --git a/WordGenerator/Controls/Dialogs/CiceroSplashForm.cs b/WordGenerator/Controls/Dialogs/CiceroSplashForm.cs index d6eb0829..5fd2016d 100644 --- a/WordGenerator/Controls/Dialogs/CiceroSplashForm.cs +++ b/WordGenerator/Controls/Dialogs/CiceroSplashForm.cs @@ -34,10 +34,10 @@ private void timer1_Tick(object sender, EventArgs e) this.Close(); } - public CiceroSplashForm(bool runTimer) + public CiceroSplashForm(bool autoCloseAfterTimeout) : this() { - this.timer1.Enabled = runTimer; + this.timer1.Enabled = autoCloseAfterTimeout; } diff --git a/WordGenerator/Controls/MainClientForm.cs b/WordGenerator/Controls/MainClientForm.cs index b83f8b35..29d8cbd7 100644 --- a/WordGenerator/Controls/MainClientForm.cs +++ b/WordGenerator/Controls/MainClientForm.cs @@ -250,9 +250,7 @@ public MainClientForm(RunLog runLog) debugToolStripMenuItem.Visible = true; #endif - CiceroSplashForm splash = new CiceroSplashForm(); - splash.Show(); // bind F9 hotkey to run button: if (hotKeyBindings == null) @@ -598,6 +596,9 @@ private void mainClientForm_Load(object sender, EventArgs e) } /* CiceroSplashForm splash = new CiceroSplashForm(); splash.Show();*/ + CiceroSplashForm splash = new CiceroSplashForm(); + + splash.ShowDialog(); } protected override bool IsInputChar(char charCode)