« September 2008 | Main | November 2008 »
October 26, 2008
Concurrency Pre-Con Highlights
I attended the "Concurrent, Multi-Core Programming on .NET and Windows" pre-conference talk at PDC. It was primarily a summary of the current state of concurrent programming on Windows (native and managed), with a preview of future advances in the platform.
David Callahan, a distinguished engineer at Microsoft, presented first, gave a summary of the "free lunch" being over and what the Parallel Computing Platform team at Microsoft is doing to address that problem. Their goal is to allow developers to easily express latent parallelism in their code, so that when manycore systems are available it can be turned into actual parallelism, giving software a different type of "free lunch". One aspect of this long-term goal is to "eliminate multi-threading"; that is, removing explicit thread management by providing better abstractions on which to build concurrent applications. One takeaway was that even though we currently only have dual- or quad-core systems, we should overdecompose problems now to gain scalability in the future.
Stephen Toub and Joe Duffy presented sessions on the mechanisms that exist for concurrency right now (with quite a number of live demos of threads, thread pool, APM, BackgroundWorker, etc.), best practices for using those mechanisms (lock hierarchies, granularity, etc.), how you can write very low-level code (lock-free, memory barriers), and why you shouldn't do that, but just use the types built into the Framework (and more are coming in .NET 4.0).
Some interesting points from these sessions were:
- The object header that is used by Monitor.Enter to get a "thin lock" is also used by the CLR's implementation of Object.GetHashCode to persist the hashcode; don't call GetHashCode on an object you're using for locking. (Note that this only applies to types that don't override GetHashCode, but just use the default implementation.)
- Interlocked operations are ultimately not scalable (due to inter-CPU communication); the more work that can be done in isolation by a thread, the better.
- A good way to measure improvements being made to multi-threaded code is to count the number of CAS (compare and swap) instructions being executed (and try to get fewer).
- Avoid Thread.Get/SetData; although it sounds like it's using thread-local storage, it actually takes a lock on a global table.
Lastly, they covered improvements that will be coming in .NET 4.0. Essentially, the Parallel Extensions are becoming part of the core framework--they'll even be integrated into mscorlib.dll, etc. instead of being supplied in a separate assembly (as in the CTPs). The TPL's task scheduler will be baked into the standard ThreadPool so that there's one master scheduler that controls all the background work for the process. PLINQ will, of course, be supported and, like all these new features, available to all .NET languages; there are no new language extensions or compiler changes required. New data structures (ConcurrentQueue, ConcurrentDictionary, etc.) will be supplied in a new System.Collections.Concurrent namespace; there will be new locks (SpinLock, ManualResetEventSlim, SemaphoreSlim, etc.) and other helper classes (blocking collections, CountdownEvent, etc.). Additionally, VC++ 10 is getting its own native concurrency libraries and task scheduler. More details on all of these items will be available at various "Deep Dive" talks later in the week.
Posted by Bradley Grainger at 9:52 PM | Comments (0) | TrackBack
October 22, 2008
How to Reverse a Unicode String in C#
Perhaps due to the lack of a built-in String.Reverse method in the .NET Framework, it's very common (1, 2, 3, 4, 5, 6, 7) for implementations of such a method to be posted.
Unfortunately, most of these implementations do not handle characters outside Unicode's Basic Multilingual Plane correctly. These supplementary characters have code points between U+10000 and U+10FFFF and so cannot be represented with one 16-bit char. In UTF-16 (which is how .NET strings are encoded), these Unicode characters are represented as two C# chars, a high surrogate followed by a low surrogate. When the string is reversed, the order of these two chars has to be preserved.
Here's our method that reverses a string while handling surrogate code units correctly:
/// <summary>
/// Reverses the specified string.
/// </summary>
/// <param name="input">The string to reverse.</param>
/// <returns>The input string, reversed.</returns>
/// <remarks>This method correctly reverses strings containing supplementary characters
/// (which are encoded with two surrogate code units).</remarks>
public static string Reverse(this string input)
{
if (input == null)
throw new ArgumentNullException("input");
// allocate a buffer to hold the output
char[] output = new char[input.Length];
for (int outputIndex = 0, inputIndex = input.Length - 1; outputIndex < input.Length; outputIndex++, inputIndex--)
{
// check for surrogate pair
if (input[inputIndex] >= 0xDC00 && input[inputIndex] <= 0xDFFF &&
inputIndex > 0 && input[inputIndex - 1] >= 0xD800 && input[inputIndex - 1] <= 0xDBFF)
{
// preserve the order of the surrogate pair code units
output[outputIndex + 1] = input[inputIndex];
output[outputIndex] = input[inputIndex - 1];
outputIndex++;
inputIndex--;
}
else
{
output[outputIndex] = input[inputIndex];
}
}
return new string(output);
}
Posted by Bradley Grainger at 9:07 PM | Comments (4) | TrackBack
Detecting Bindings that should be OneTime
In WPF, a Binding's source can be any .NET object; the target of the Binding will be updated when the specified property on that source changes. This works best when the source property is a DependencyProperty, or when the source object implements INotifyPropertyChanged; these objects have built-in support for property value changed notifications. In other cases, the ComponentModel infrastructure (as exposed by the PropertyDescriptor class) stores the source object in a global table in order to track clients who wish to be notified when a property value changes.
Binding to a regular property of a regular .NET object (that doesn't implement INotifyPropertyChanged) has two drawbacks:
- It may be needlessly inefficient. If, for example, the source object is not implementing INotifyPropertyChanged because it's immutable, creating and attaching value changed handlers is unnecessary overhead.
- It can cause a memory leak.
Both these problems can be eliminated by setting the Mode of the Binding to OneTime, but in a large application, determining all the bindings that could be OneTime is not an easy task. Some spelunking (with .NET Memory Profiler and .NET Reflector) showed that the (internal) ReflectTypeDescriptionProvider class has a static Hashtable containing all objects that have had value changed handlers added. A common reason for objects to end up in that Hashtable is their participation in a WPF binding, so enumerating this Hashtable at runtime can help track down bindings that may need to be changed. (And if an object is never removed from this hashtable, that may be a sign of a memory leak.)
This method uses reflection to dump the contents of the ReflectTypeDescriptionProvider._propertyCache hashtable for diagnostic purposes (the definition of the ReflectPropertyDescriptorInfo class is given later):
private static ReadOnlyCollection<ReflectPropertyDescriptorInfo> GetReflectPropertyDescriptorInfo()
{
List<ReflectPropertyDescriptorInfo> listInfo = new List<ReflectPropertyDescriptorInfo>();
// get the ReflectTypeDescriptionProvider._propertyCache field
Type typeRtdp = typeof(PropertyDescriptor).Module.
GetType("System.ComponentModel.ReflectTypeDescriptionProvider");
FieldInfo propertyCacheFieldInfo = typeRtdp.GetField("_propertyCache",
BindingFlags.Static | BindingFlags.NonPublic);
Hashtable propertyCache = (Hashtable) propertyCacheFieldInfo.GetValue(null);
if (propertyCache != null)
{
// try to make a copy of the hashtable as quickly as possible (this object can be accessed by other threads)
DictionaryEntry[] entries = new DictionaryEntry[propertyCache.Count];
propertyCache.CopyTo(entries, 0);
FieldInfo valueChangedHandlersFieldInfo = typeof(PropertyDescriptor).GetField("valueChangedHandlers",
BindingFlags.Instance | BindingFlags.NonPublic);
// count the "value changed" handlers for each type
foreach (DictionaryEntry entry in entries)
{
PropertyDescriptor[] pds = (PropertyDescriptor[]) entry.Value;
if (pds != null)
{
foreach (PropertyDescriptor pd in pds)
{
Hashtable valueChangedHandlers = (Hashtable) valueChangedHandlersFieldInfo.GetValue(pd);
if (valueChangedHandlers != null && valueChangedHandlers.Count != 0)
listInfo.Add(new ReflectPropertyDescriptorInfo(entry.Key.ToString(), pd.Name,
valueChangedHandlers.Count));
}
}
}
}
listInfo.Sort();
return listInfo.AsReadOnly();
}
The following code implements a window that displays all the properties that were found. It can be used by adding it to a WPF application and creating a special diagnostic button or keystroke that opens the window. You can open two windows and compare the lists side-by-side, or use the Refresh button to regenerate the list (after interacting with your application's UI) to see if any properties have been added or removed.
ReflectPropertyDescriptorWindow.xaml:
<Window x:Class="OneTimeBinding.ReflectPropertyDescriptorWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:OneTimeBinding"
Title=".NET Properties used in Binding Paths" Height="450" Width="450" WindowStartupLocation="CenterScreen"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type src:ReflectPropertyDescriptorInfo}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding TypeName, Mode=OneTime}"/>
<TextBlock>.</TextBlock>
<TextBlock FontWeight="Bold" Text="{Binding PropertyName, Mode=OneTime}"/>
<TextBlock Text="{Binding DisplayHandlerCount, Mode=OneTime}"/>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<DockPanel>
<Button DockPanel.Dock="Top" Margin="4"
Click="RefreshButton_Click">_Refresh</Button>
<ScrollViewer Margin="4">
<ItemsControl ItemsSource="{Binding ReflectProperties}"/>
</ScrollViewer>
</DockPanel>
</Window>
ReflectPropertyDescriptorWindow.xaml.cs:
public partial class ReflectPropertyDescriptorWindow : Window
{
public ReflectPropertyDescriptorWindow()
{
InitializeComponent();
ReflectProperties = GetReflectPropertyDescriptorInfo();
}
public static readonly DependencyProperty ReflectPropertiesProperty =
DependencyProperty.Register("ReflectProperties", typeof(ReadOnlyCollection<ReflectPropertyDescriptorInfo>),
typeof(ReflectPropertyDescriptorWindow), new PropertyMetadata());
public ReadOnlyCollection<ReflectPropertyDescriptorInfo> ReflectProperties
{
get { return (ReadOnlyCollection<ReflectPropertyDescriptorInfo>) GetValue(ReflectPropertiesProperty); }
set { SetValue(ReflectPropertiesProperty, value); }
}
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
ReflectProperties = GetReflectPropertyDescriptorInfo();
}
private static ReadOnlyCollection<ReflectPropertyDescriptorInfo> GetReflectPropertyDescriptorInfo()
{
// as shown above
}
}
And finally, the definition of the immutable ReflectPropertyDescriptorInfo object, which is used as the source of a OneTime binding in the UI:
public sealed class ReflectPropertyDescriptorInfo : IEquatable<ReflectPropertyDescriptorInfo>,
IComparable<ReflectPropertyDescriptorInfo>
{
public ReflectPropertyDescriptorInfo(string typeName, string propertyName, int handlerCount)
{
m_typeName = typeName;
m_propertyName = propertyName;
m_handlerCount = handlerCount;
}
public string TypeName
{
get { return m_typeName; }
}
public string PropertyName
{
get { return m_propertyName; }
}
public int HandlerCount
{
get { return m_handlerCount; }
}
public string DisplayHandlerCount
{
get { return m_handlerCount == 1 ? "" : string.Format(CultureInfo.InvariantCulture,
" ({0:n0} handlers)", m_handlerCount); }
}
public int CompareTo(ReflectPropertyDescriptorInfo other)
{
if (object.ReferenceEquals(other, null))
return 1;
int compareResult = m_typeName.CompareTo(other.m_typeName);
if (compareResult == 0)
compareResult = m_propertyName.CompareTo(other.m_propertyName);
if (compareResult == 0)
compareResult = m_handlerCount.CompareTo(other.m_handlerCount);
return compareResult;
}
// Implementations of Equals, GetHashCode, operators, etc. elided for brevity
readonly string m_typeName;
readonly string m_propertyName;
readonly int m_handlerCount;
}
Posted by Bradley Grainger at 6:57 PM | Comments (1) | TrackBack
October 16, 2008
Going to PDC
I'm going to PDC on 26 October. If you'd like to meet up there, send a note with your contact details to bgrainger at logos.com.
Posted by Bradley Grainger at 6:33 PM | Comments (0) | TrackBack
October 15, 2008
Keep your WPF UI responsive
Hopefully, everyone writing Windows applications understands the value of keeping the UI responsive. That is, we all know that it is important to avoid long-running or long-waiting work in the UI thread, because it prevents the window from doing anything - it can't respond to keystrokes or clicks; it can't even update its display. So, we make sure that any work we do is so fast that the user won't notice, or we move that work into background threads.
Still, it is easy to miss something, especially when an operation usually completes quickly, but can be much slower under certain circumstances. Until recently, we could get away with it from time to time - in fact, we might display a "Working" message right before we started the work, or we might set the mouse cursor to the "hourglass" on the hopes that the user won't get too impatient and terminate the application.
These days, however, Windows is much less forgiving. Under Windows Vista, at least, if a window is not responsive for five seconds or so, the window flickers a bit and Windows adds the humiliating "(Not Responding)" message to the window caption.

Even so, surely the user could deal with that from time to time. A temporarily frozen user interface isn't the end of the world; after all, that program is working hard! Just let it finish and everything will be fine again.
Unfortunately, things get much worse when running a WPF application under Windows Vista with Aero enabled (the glassy window captions). If that application becomes unresponsive, you get the "(Not Responding)" message, as well as something we like to call the "black screen of death" - the entire content of the window goes pitch black.

This is surely unacceptable - nothing says "terminate me" like a black window. In the end, I suppose this will be best for the user, because we're trying extra hard to make sure that nothing we do on the UI could ever take five seconds. But it's certainly a lot harder than displaying the trusty old hourglass.
Posted by Ed Ball at 9:11 AM | Comments (0) | TrackBack
October 7, 2008
Passing An Array Parameter To SQL Server Stored Procedures
SQL Server (2000 & 2005) does not support array parameters for stored procedures. As a workaround, an array of values can be passed into SQL Server as a delimited string.
There are many articles on the web on how to do this, but this is my preferred method because the conversion of the delimited string into table values is done in a reusable function and the function itself can be placed inside a select query and act as a table.
First, we need to convert a delimited string to a table of values. This can be done through the following table-valued function:
CREATE Function [dbo].[fnSplit](@text text, @delimitor nchar(1))
RETURNS
@table TABLE
(
[Index] int Identity(0,1),
[SplitText] varchar(10)
)
AS
BEGIN
declare @current varchar(10)
declare @endIndex int
declare @textlength int
declare @startIndex int
set @startIndex = 1
if(@text is not null)
begin
set @textLength = datalength(@text)
while(1=1)
begin
set @endIndex = charindex(@delimitor, @text, @startIndex)
if(@endIndex != 0)
begin
set @current = substring(@text,@startIndex, @endIndex - @StartIndex)
Insert Into @table ([SplitText]) values(@current)
set @startIndex = @endIndex + 1
end
else
begin
set @current = substring(@text, @startIndex, datalength(@text)-@startIndex+1)
Insert Into @table ([SplitText]) values(@current)
break
end
end
end
return
END
To use this function, simply treat it as a table in the query:
select SplitText
from dbo.fnSplit('a,b,c',',')
Returns:
a
b
c
The complete process goes like this:
1. Convert the array of values to a delimited string.
2. Pass this string to the stored procedure.
3. Use the above fnSplit function to convert the string to a table of values which can be used in queries.
Posted by Bill Simpkins at 4:10 PM | Comments (4) | TrackBack
Introduction: Bill Simpkins
In high school I was horrible at anything remotely technical, except for music. I began teaching guitar and music theory when I was 16 and then took my own sweet time in community college while I was singing and playing guitar in various rock bands. In the mid 90's I took an interest in astronomy, which led to physics, which led to math and programming. In 2001 I received my B.S in Mathematics from WWU(with about all the coursework for a physics). I occasionally go back and take classes.
I continued to work as an audio engineer, which I did all through college to pay the bills, until I became a financial analyst at a large insurance company(I got married). I mined tons of data and became rather good with Oracle and SQL Server, along with creating makeshift dashboards using Java, XML, Javascript, HTML and butchered PostScript. I later worked at a hospital doing data analysis and creating data mining tools in C# and VB. On my free time I created simulators in C++ to test gambling schemes.
I started work at LOGOS as a Web Developer in October 2007 and I enjoy the innovative environment very much. I work on a variety of projects including, but not limited to, data integration and CRM development.
I am currently interested in Object Databases, physics processing and mathematics engines. When I'm not programming I climb cliffs and mountains, snowboard and play music.
Posted by Bill Simpkins at 2:12 PM | Comments (0) | TrackBack
October 6, 2008
Using "Background Processing Mode" from C#
The Windows Vista kernel added support for I/O and memory priorities. These allow background work (such as search indexing or virus scanning) to reduce its impact on foreground applications beyond what is possible simply by using a low thread CPU priority. According to the SetThreadPriority documentation, "For threads that perform background work such as file I/O, network I/O, or data processing, it is not sufficient to adjust the CPU scheduling priority; even an idle CPU priority thread can easily interfere with system responsiveness when it uses the disk and memory."
Applications can opt in to low I/O and memory priority by passing new flags to SetThreadPriority(THREAD_MODE_BACKGROUND_BEGIN and THREAD_MODE_BACKGROUND_END) or SetPriorityClass (PROCESS_MODE_BACKGROUND_BEGIN and PROCESS_MODE_BACKGROUND_END).
These new priority levels aren't exposed through the .NET Framework, but can be accessed by using P/Invoke. First, declare the constants and functions from the Windows API:
internal static class Win32
{
public const int THREAD_MODE_BACKGROUND_BEGIN = 0x00010000;
public const int THREAD_MODE_BACKGROUND_END = 0x00020000;
}
internal static class NativeMethods
{
[DllImport("Kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GetCurrentThread();
[DllImport("Kernel32.dll", ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetThreadPriority(IntPtr hThread, int nPriority);
}
Second, write a C# wrapper for those functions. I use Thread.BeginThreadAffinity to notify the runtime (strictly speaking, the CLR host) that the code that's being executed depends on the identity of the underlying OS thread. The return type, Scope, has been covered already on this blog.
public static class ThreadUtility
{
/// <summary>
/// Puts the current thread into background processing mode.
/// </summary>
/// <returns>A Scope that must be disposed to leave background processing mode.</returns>
[SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlThread)]
public static Scope EnterBackgroundProcessingMode()
{
Thread.BeginThreadAffinity();
IntPtr hThread = SafeNativeMethods.GetCurrentThread();
if (IsWindowsVista() && NativeMethods.SetThreadPriority(hThread,
Win32.THREAD_MODE_BACKGROUND_BEGIN))
{
// OS supports background processing; return Scope that exits this mode
return Scope.Create(() =>
{
NativeMethods.SetThreadPriority(hThread, Win32.THREAD_MODE_BACKGROUND_END);
Thread.EndThreadAffinity();
});
}
// OS doesn't support background processing mode (or setting it failed)
Thread.EndThreadAffinity();
return Scope.Empty;
}
// Returns true if the current OS is Windows Vista (or Server 2008) or higher.
private static bool IsWindowsVista()
{
OperatingSystem os = Environment.OSVersion;
return os.Platform == PlatformID.Win32NT && os.Version >= new Version(6, 0);
}
}
Third, use the wrapper like so:
using (ThreadUtility.EnterBackgroundProcessingMode())
{
PerformSomeBackgroundWork();
}
Note that this only has effect on Windows Vista, Windows Server 2008, and later; you'd also want to lower Thread.Priority during the background work if your application runs on earlier operating systems.
And while this does appear to work quite nicely in testing, it could actually be dangerous in production code. It's possible that this could have unpredictable and hazardous interactions with the garbage collector, the finalizer thread, or other components of the .NET Runtime. Furthermore, if a CLR host ever multiplexes many managed threads to one OS thread, changing the priority of the OS thread would be too heavy-handed. Perhaps a future version of the framework will expose background processing mode to managed threads in a safe way; until then it's probably best to consider advanced native threading features (background mode, fibers, CPU affinity, etc.) to be off limits to managed code.
Posted by Bradley Grainger at 8:17 AM | Comments (0) | TrackBack
October 1, 2008
Displaying a Splash Screen with C++ (Part IV)
This is Part IV of a series on creating a splash screen application in native code. For more information, see the Introduction and the Code License.
Part IV: Dismissing the Splash Screen
When the WPF application has initialised and is ready to display its main window, the splash screen needs to be dismissed. The easiest way to accomplish this is by having a named event that the native application creates and the WPF application sets.
Firstly, we'll create the named event that will dismiss the splash screen. This also has the beneficial side effect that we can prevent more than one splash screen application from running at once. (Note that the WPF application will also need to ensure that only one instance runs at a time, if that is desired. On the other hand, if you want to be able to launch multiple instances of the splash screen and WPF applications at once, each will need to have a unique event.)
// create the named close splash screen event, making sure we're the first process to create it
SetLastError(ERROR_SUCCESS);
HANDLE hCloseSplashEvent = CreateEvent(NULL, TRUE, FALSE, _T("CloseSplashScreenEvent"));
if (GetLastError() == ERROR_ALREADY_EXISTS)
ExitProcess(0);
Once we know it's safe to continue running, we can use the code shown in Parts I-III to display the splash screen and launch the WPF application, then wait for the "close splash screen" event to be set or the WPF application to exit; once either of these events happens, it's time to dismiss the splash screen.
// call LoadSplashImage() etc.
// call SetSplashImage() etc.
// launch the WPF application
HANDLE hProcess = LaunchWpfApplication();
AllowSetForegroundWindow(GetProcessId(hProcess));
// display the splash screen for as long as it's needed
HANDLE aHandles[2] = { hProcess, hCloseSplashEvent };
PumpMsgWaitForMultipleObjects(2, &aHandles[0], INFINITE);
The PumpMsgWaitForMultipleObjects method has not yet been defined. It's similar (in API) to the Win32 WaitForMultipleObjects function, but it also dispatches window messages as they arrive. Since we have created a window on this thread, running a message pump is essential. We use MsgWaitForMultipleObjects to wait for either of two HANDLEs while also being woken up when a window message arrives. (Note that this implementation is more generic than is necessary in this example: the timeout is always INFINITE, and there's no outer message loop that would need to reprocess the WM_QUIT message.)
inline DWORD PumpMsgWaitForMultipleObjects(DWORD nCount, LPHANDLE pHandles, DWORD dwMilliseconds)
{
// useful variables
const DWORD dwStartTickCount = ::GetTickCount();
// loop until done
for (;;)
{
// calculate timeout
const DWORD dwElapsed = GetTickCount() - dwStartTickCount;
const DWORD dwTimeout = dwMilliseconds == INFINITE ? INFINITE :
dwElapsed < dwMilliseconds ? dwMilliseconds - dwElapsed : 0;
// wait for a handle to be signaled or a message
const DWORD dwWaitResult = MsgWaitForMultipleObjects(nCount, pHandles, FALSE, dwTimeout, QS_ALLINPUT);
if (dwWaitResult == WAIT_OBJECT_0 + nCount)
{
// pump messages
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE)
{
// check for WM_QUIT
if (msg.message == WM_QUIT)
{
// repost quit message and return
PostQuitMessage((int) msg.wParam);
return WAIT_OBJECT_0 + nCount;
}
// dispatch thread message
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
// timeout on actual wait or any other object
return dwWaitResult;
}
}
}
Lastly, the WPF application needs to signal the splash screen application to close. (Finally, some C# code!)
private void CloseSplashScreen()
{
// signal the native process (that launched us) to close the splash screen
using (var closeSplashEvent = new EventWaitHandle(false,
EventResetMode.ManualReset, "CloseSplashScreenEvent"))
{
closeSplashEvent.Set();
}
}
As long as the same name is used, the event will be shared across the two processes and the splash screen application will exit when the event is set.
On my slow XP computer, the native application displays the splash screen in well under a second, even from a cold start. Sometimes it's necessary to drop back to native code for small feature areas with strict performance demands. Displaying UI as soon as possible when the application is launched is one of those areas; a few hundred lines of native code can result in a perceived decrease in application startup time and a better experience for the end user.
Posted by Bradley Grainger at 8:31 AM | Comments (5) | TrackBack