xenadmin/XenAdmin/Wizards/BugToolWizardFiles/BugToolPageSelectCapabilities.cs
Gabor Apati-Nagy 92f0499911 CA-149867: Invoke on MainWindow instead of various controls
We observed that some threads can reach deadlock-ish state after they have Invoked into a control's UI thread. When it happens they are all in a waiting for join or in sleep state for very long time, although there should not be any deadlock situations.
It seems this has something to do with multiple parent controls and with which control we invoked on. This should not make a difference, because we have got one UI thread (for MainWindow) they should wait for, but we have seen it does.

The solution that fixed this issue was to invoke on the MainWindow instead of various controls (see a4fe507adf ).

This changeset is changing all our Invokes to invoke into MainWindow
instead of a control itself. (MainWindow's UI thread is the only UI thread
all Control is using in XenCenter)
This changeset should be in place until we have found the root cause or the exact reason for the above.
2015-04-08 15:16:10 +01:00

629 lines
21 KiB
C#

/* Copyright (c) Citrix Systems Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Xml;
using XenAdmin.Controls;
using XenAdmin.Core;
using XenAPI;
using XenAdmin.Actions;
using XenAdmin.Dialogs;
namespace XenAdmin.Wizards.BugToolWizardFiles
{
public partial class BugToolPageSelectCapabilities : XenTabPage
{
#region Private fields
private List<Host> _hostList = new List<Host>();
private List<Capability> _capabilities = new List<Capability>();
private Dictionary<Host, List<Capability>> hostCapabilities;
private List<GetSystemStatusCapabilities> actions;
private ActionProgressDialog dialog;
private bool cancelled;
#endregion
public BugToolPageSelectCapabilities()
{
InitializeComponent();
//set this here due to a framework bug
splitContainer1.Panel1MinSize = 250;
splitContainer1.Panel2MinSize = 200;
}
public override string Text{get { return Messages.BUGTOOL_PAGE_CAPABILITIES_TEXT; }}
public override string PageTitle { get { return Messages.BUGTOOL_PAGE_CAPABILITIES_PAGETITLE; } }
public override string HelpID { get { return "SelectReportContents"; } }
public override bool EnableNext()
{
return wizardCheckAnyChecked();
}
public override void PageLoaded(PageLoadedDirection direction)
{
base.PageLoaded(direction);
if (direction == PageLoadedDirection.Forward)
{
ClearButton.Enabled = wizardCheckAnyChecked();
SelectButton.Enabled = wizardCheckAnyUnchecked();
}
}
private bool wizardCheckAnyChecked()
{
foreach (DataGridViewRow row in dataGridViewItems.Rows)
{
var capRow = row as CapabilityRow;
if (capRow != null && capRow.Capability.Checked)
return true;
}
return false;
}
private bool wizardCheckAnyUnchecked()
{
foreach (DataGridViewRow row in dataGridViewItems.Rows)
{
var capRow = row as CapabilityRow;
if (capRow != null && !capRow.Capability.Checked)
return true;
}
return false;
}
/// <summary>
///
/// </summary>
/// <param name="HostList"></param>
/// <returns>If success of not - false with stop moving to next page</returns>
public bool GetCommonCapabilities(List<Host> HostList)
{
if (!cancelled && Helpers.ListsContentsEqual<Host>(_hostList, HostList))
return true;
_hostList = HostList;
dataGridViewItems.Rows.Clear();
if (dialog != null && dialog.Visible)
dialog.Close();
hostCapabilities = new Dictionary<Host, List<Capability>>();
actions = new List<GetSystemStatusCapabilities>();
cancelled = false;
dialog = new ActionProgressDialog(Messages.SERVER_STATUS_REPORT_GET_CAPABILITIES);
dialog.ShowCancel = true;
dialog.CancelClicked += new EventHandler(dialog_CancelClicked);
foreach (Host host in _hostList)
{
GetSystemStatusCapabilities action = new GetSystemStatusCapabilities(host);
actions.Add(action);
action.Completed += Common_action_Completed;
action.RunAsync();
}
if (_hostList.Count > 0)
dialog.ShowDialog(this);
return !cancelled;
}
private void dialog_CancelClicked(object sender, EventArgs e)
{
dialog.Close();
cancelled = true;
// Need to unhook events on current actions, to stop them
// interfeering when they return in 20mins
foreach (GetSystemStatusCapabilities action in actions)
action.Completed -= Common_action_Completed;
}
private void Common_action_Completed(ActionBase sender)
{
Program.Invoke(Program.MainWindow, delegate()
{
if (cancelled)
return;
GetSystemStatusCapabilities caps = sender as GetSystemStatusCapabilities;
if (caps == null)
return;
hostCapabilities[caps.Host] =
caps.Succeeded ? parseXMLToList(caps) : null;
if (hostCapabilities.Count >= _hostList.Count)
{
dialog.Close();
CombineCapabilitiesLists();
}
});
}
private List<Capability> parseXMLToList(Actions.GetSystemStatusCapabilities action)
{
List<Capability> capabilities = new List<Capability>();
XmlDocument doc = new XmlDocument();
doc.LoadXml(action.Result);
foreach (XmlNode node in doc.GetElementsByTagName("capability"))
{
Capability c = new Capability();
foreach (XmlAttribute a in node.Attributes)
{
string name = a.Name;
string value = a.Value;
if (name == "content-type")
c.ContentType = value == "text/plain" ? ContentType.text_plain : ContentType.application_data;
else if (name == "default-checked")
c.DefaultChecked = value == "yes" ? true : false;
else if (name == "key")
c.Key = value;
else if (name == "max-size")
c.MaxSize = Int64.Parse(value);
else if (name == "min-size")
c.MinSize = Int64.Parse(value);
else if (name == "max-time")
c.MaxTime = Int64.Parse(value);
else if (name == "min-time")
c.MinTime = Int64.Parse(value);
else if (name == "pii")
c.PII = value == "yes" ? PrivateInformationIncluded.yes :
value == "no" ? PrivateInformationIncluded.no :
value == "maybe" ? PrivateInformationIncluded.maybe : PrivateInformationIncluded.customized;
}
capabilities.Add(c);
}
return capabilities;
}
private void CombineCapabilitiesLists()
{
List<Capability> combination = null;
foreach (List<Capability> capabilities in hostCapabilities.Values)
{
if (capabilities == null)
continue;
if (combination == null)
{
combination = capabilities;
continue;
}
combination = Helpers.ListsCommonItems<Capability>(combination, capabilities);
}
if (combination == null || combination.Count <= 0)
{
new ThreeButtonDialog(
new ThreeButtonDialog.Details(
SystemIcons.Error,
Messages.SERVER_STATUS_REPORT_CAPABILITIES_FAILED,
Messages.SERVER_STATUS_REPORT)).ShowDialog(this);
cancelled = true;
return;
}
_capabilities = combination;
_capabilities.Add(GetClientCapability());
Sort_capabilities();
buildList();
}
private Capability GetClientCapability()
{
Capability clientCap = new Capability();
clientCap.ContentType = ContentType.text_plain;
clientCap.DefaultChecked = true;
clientCap.Key = "client-logs";
clientCap.MaxSize = getLogSize();
clientCap.MinSize = clientCap.MaxSize;
clientCap.MaxTime = -1;
clientCap.MinTime = -1;
clientCap.PII = PrivateInformationIncluded.yes;
return clientCap;
}
private void Sort_capabilities()
{
_capabilities.Sort(new Comparison<Capability>(delegate(Capability obj1, Capability obj2)
{
int piicompare = obj1.PII.CompareTo(obj2.PII);
if (piicompare == 0)
return StringUtility.NaturalCompare(obj1.ToString(), obj2.ToString());
else
return piicompare;
}));
}
private void OnCheckedCapabilitiesChanged()
{
string totalSize, totalTime;
CalculateTotalSizeAndTime(out totalSize, out totalTime);
TotalSizeValue.Text = totalSize;
TotalTimeValue.Text = totalTime;
ClearButton.Enabled = wizardCheckAnyChecked();
SelectButton.Enabled = wizardCheckAnyUnchecked();
OnPageUpdated();
}
private void buildList()
{
try
{
dataGridViewItems.SuspendLayout();
dataGridViewItems.Rows.Clear();
var list = new List<DataGridViewRow>();
foreach (Capability c in _capabilities)
list.Add(new CapabilityRow(c));
dataGridViewItems.Rows.AddRange(list.ToArray());
}
finally
{
dataGridViewItems.ResumeLayout();
}
OnCheckedCapabilitiesChanged();
}
private long getLogSize()
{
String path = Program.GetLogFile_();
if (path != null)
{
// Size of XenCenter.log
FileInfo logFileInfo = new FileInfo(path);
long total = logFileInfo.Length;
// Add size of any XenCenter.log.x
DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(path));
Regex regex = new Regex(Regex.Escape(Path.GetFileName(path)) + @"\.\d+");
foreach (FileInfo file in di.GetFiles())
{
if (regex.IsMatch(file.Name))
total += file.Length;
}
return total;
}
else
{
return 50 * Util.BINARY_MEGA;
}
}
private void CalculateTotalSizeAndTime(out string totalSize, out string totalTime)
{
var sizeMinList = new List<long>();
var sizeMaxList = new List<long>();
var timeMinList = new List<long>();
var timeMaxList = new List<long>();
foreach (var row in dataGridViewItems.Rows)
{
var capRow = row as CapabilityRow;
if (capRow != null && capRow.Capability.Checked)
{
var c = capRow.Capability;
int m = c.Key == "client-logs" ? 1 : _hostList.Count;
sizeMinList.Add(c.MinSize * m);
sizeMaxList.Add(c.MaxSize * m);
timeMinList.Add(c.MinTime * m);
timeMaxList.Add(c.MaxTime * m);
}
}
totalSize = Helpers.StringFromMaxMinSizeList(sizeMinList, sizeMaxList);
totalTime = Helpers.StringFromMaxMinTimeList(timeMinList, timeMaxList);
}
public IEnumerable<Capability> Capabilities
{
get
{
return from DataGridViewRow row in dataGridViewItems.Rows
let capRow = row as CapabilityRow
where capRow != null && capRow.Capability.Checked
select capRow.Capability;
}
}
#region Control event handlers
private void dataGridViewItems_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
var row1 = dataGridViewItems.Rows[e.RowIndex1] as CapabilityRow;
var row2 = dataGridViewItems.Rows[e.RowIndex2] as CapabilityRow;
if (row1 != null && row2 != null && e.Column.Index == columnImage.Index)
{
e.SortResult = row1.Capability.PII.CompareTo(row2.Capability.PII);
e.Handled = true;
}
}
private void dataGridViewItems_SelectionChanged(object sender, EventArgs e)
{
if (dataGridViewItems.SelectedRows.Count > 0)
{
var row = dataGridViewItems.SelectedRows[0] as CapabilityRow;
if (row == null)
return;
DescriptionValue.Text = row.Capability.Description;
SizeValue.Text = row.Capability.EstimatedSize;
TimeValue.Text = row.Capability.EstimatedTime;
}
}
private void dataGridViewItems_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex != columnCheck.Index)
return;
if (e.RowIndex < 0 || e.RowIndex >= dataGridViewItems.RowCount)
return;
var row = dataGridViewItems.Rows[e.RowIndex] as CapabilityRow;
if (row == null)
return;
row.Capability.Checked = !row.Capability.Checked;
row.Update();
OnCheckedCapabilitiesChanged();
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
try
{
System.Diagnostics.Process.Start(InvisibleMessages.PRIVACY);
}
catch
{
new ThreeButtonDialog(
new ThreeButtonDialog.Details(
SystemIcons.Error,
Messages.HOMEPAGE_ERROR_MESSAGE,
Messages.XENCENTER)).ShowDialog(this);
}
}
private void SelectButton_Click(object sender, EventArgs e)
{
foreach (DataGridViewRow row in dataGridViewItems.Rows)
{
var capRow = row as CapabilityRow;
if (capRow != null && !capRow.Capability.Checked)
{
capRow.Capability.Checked = true;
capRow.Update();
}
}
OnCheckedCapabilitiesChanged();
}
private void ClearButton_Click(object sender, EventArgs e)
{
foreach (DataGridViewRow row in dataGridViewItems.Rows)
{
var capRow = row as CapabilityRow;
if (capRow != null && capRow.Capability.Checked)
{
capRow.Capability.Checked = false;
capRow.Update();
}
}
OnCheckedCapabilitiesChanged();
}
#endregion
private class CapabilityRow : DataGridViewRow
{
public readonly Capability Capability;
private readonly DataGridViewCheckBoxCell cellCheck = new DataGridViewCheckBoxCell();
private readonly DataGridViewTextBoxCell cellItem = new DataGridViewTextBoxCell();
private readonly DataGridViewImageCell cellImage = new DataGridViewImageCell();
public CapabilityRow(Capability capability)
{
Capability = capability;
Cells.AddRange(cellCheck, cellItem, cellImage);
Update();
}
public void Update()
{
cellCheck.Value = Capability.Checked;
cellItem.Value = Capability.Name;
switch(Capability.PII)
{
case PrivateInformationIncluded.maybe:
cellImage.Value = Properties.Resources.alert2_16;
break;
case PrivateInformationIncluded.customized:
cellImage.Value = Properties.Resources.alert3_16;
break;
case PrivateInformationIncluded.no:
cellImage.Value = Properties.Resources.alert4_16;
break;
case PrivateInformationIncluded.yes:
default:
cellImage.Value = Properties.Resources.alert1_16;
break;
}
cellImage.ToolTipText = Capability.PiiText;
}
}
}
public enum ContentType { text_plain, application_data };
public enum PrivateInformationIncluded { yes, maybe, customized, no};
public class Capability : IComparable<Capability>, IEquatable<Capability>
{
public ContentType ContentType;
bool _defaultChecked;
public long MaxSize;
public long MinSize;
public long MaxTime;
public long MinTime;
public PrivateInformationIncluded PII;
private string _key;
private string _name;
private string _description;
public bool Checked;
public bool DefaultChecked
{
get
{
return _defaultChecked;
}
set
{
_defaultChecked = value;
Checked = value;
}
}
public string PiiText
{
get
{
switch (PII)
{
case PrivateInformationIncluded.yes:
return Messages.PII_YES;
case PrivateInformationIncluded.maybe:
return Messages.PII_MAYBE;
case PrivateInformationIncluded.customized:
return Messages.PII_CUSTOMISED;
case PrivateInformationIncluded.no:
default:
return Messages.PII_NO;
}
}
}
public string Key
{
get
{
return _key;
}
set
{
_key = value;
_name = Core.PropertyManager.GetFriendlyName(string.Format("Label-host.system_status-{0}", _key));
if (string.IsNullOrEmpty(_name))
_name = _key;
_description = Core.PropertyManager.GetFriendlyName(string.Format("Description-host.system_status-{0}", _key));
}
}
public string Name
{
get { return _name; }
}
public string Description
{
get { return _description; }
}
public string EstimatedSize
{
get
{
return Helpers.StringFromMaxMinSize(MinSize, MaxSize);
}
}
public string EstimatedTime
{
get
{
return Helpers.StringFromMaxMinTime(MinTime, MaxTime);
}
}
public override string ToString()
{
return _name;
}
public int CompareTo(Capability other)
{
return StringUtility.NaturalCompare(this.Key, other.Key);
}
public bool Equals(Capability other)
{
return this.Key.Equals(other.Key);
}
}
}