none
Databound INotifyPropertyChanged class updated via BackgroundWorker not updating UI

    Question

  • I have a class that implements INotifyPropertyChanged that looks thru a list for a set of values (it has been simplified to point out my issue).  It does this search on a BackgroundWorker thread.  As it finds a value it writes the value to a CurrentValue property which raises the PropertyChangedEvent.  The CurrentValue property is databound to a label.  With the Sleep(1) in there it works fine but is quite slow.  Without the Sleep(1), the label is not updated until the BackgroundWorker completely finishes.  I am trying to understand what is happening and what the solutions are.

    I beleive I read somewhere that INotify properties are automatically marshalled to the UI thread which seems to be the case when the background thread pauses.  The marshalling must be occuring on the background thread in a synchronous fasion?

    I realize I could use the progress reporting event on the BackgroundWorker and update the label manually, but that would get messy if I was updating a lot of fields.  Is there some mechanism for me to force the marshalling to occur, but in a way that doesn't tie up the background thread?

    void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { Stopwatch stopwatch = newStopwatch(); stopwatch.Start(); for (Int64 i = 3; stopwatch.ElapsedMilliseconds < 10000; i++) { Boolean check = false; for (Int32 j = 0; j < FoundValues.Count; j++) { if (FoundValues[j] == 0) check = true; break; } if (check == false) { CurrentValue=i;

    Thread.Sleep(1); } } }

    Tuesday, April 10, 2012 5:47 PM

Answers

  • OnSourcePropertyChanged - the method I referenced in my last post basically looks like:

          if (this.Dispatcher.Thread == Thread.CurrentThread)
          {
            this.PW.OnPropertyChangedAtLevel(level);
          }
          else
          {
            this.SetTransferIsPending(true);
            this.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, (Delegate) new DispatcherOperationCallback(this.ScheduleTransferOperation), (object) new object[2]
            {
              o,
              (object) propName
            });

    So, if you are NOT on the UI's (Dispatcher's) thread, then it calls Dispatcher.BeginInvoke to asynchronously update the UI.  That is why it does not block your worker thread.  However, as previously discussed, you are sending these updates much faster than the UI can actually process and render them.

    The first thing to remember here is to separate in your head the act of assigning data to a control (data binding), and the act of the control actually drawing itself to the screen (rendering).

    Note that calling BeginInvoke basically takes your delegate and puts it in the queue for the UI to process.  Note however, that it is not a simple queue, but a priority queue.  Note that this action is going in with a priority of DataBind.  It is also important to note that the act of rendering gets it's own entry in the priority queue, at a different priority - "Render".

    If you check the DispatcherPriority enumeration documentation, DataBind has a value of 8, and Render has a value of 7.  The documentation for Render says it is equivalent to DataBind, but I think our experience in this thread shows that given a DataBind and a Render entry, the DataBind is getting the higher priority (which it should based on the enumeration itself - 8 > 7).

    So, your constant sending of priority 8 databinds keeps the slightly lower priority 7 render from ever taking place.  The UI does KNOW that all of those updates took place, and the textbox's value is actually changing, it just doesn't actually get rendered.

    Does that make sense?

    • Marked as answer by b_levitt Friday, April 13, 2012 8:45 PM
    Friday, April 13, 2012 8:39 PM

All replies

  • As far as I know there is no such thing as auto marshalling.  Are you using winforms or WPF?

    JP Cowboy Coders Unite!

    Tuesday, April 10, 2012 7:29 PM
  • Hello, in order to understand the BackgroundWorker behavior in WPF you need to known that only the ProgressChanged and RunWorkerCompleted events are executed in the UI thread. The DoWork event is executed in a different thread and if you need to update the UI (or you change anything regarding the UI) you need to use a Dispatcher invocation in order to get the UI updated.

    Hope this helps,

    Miguel.

    Tuesday, April 10, 2012 7:46 PM
  • As far as I know there is no such thing as auto marshalling.  Are you using winforms or WPF?

    JP Cowboy Coders Unite!

    WPF.  As I mentioned, the binding is correctly updating the field as long as there is a sleep in the BackgroundWorker or if the worker completes.

    Here is a complete class as an example:

      public class MyINotifyClass : INotifyPropertyChanged
      {
    
        private Int32 currentNumber;
        public Int32 CurrentNumber
        {
          get { return this.currentNumber; }
          set
          {
            this.currentNumber = value;
            OnPropertyChanged("CurrentNumber");
          }
        }
    
        BackgroundWorker worker = new BackgroundWorker();
    
        public MyINotifyClass()
        {
          worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        }
    
        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
          Stopwatch stopwatch = new Stopwatch();
          stopwatch.Start();
          for (int i = 0; stopwatch.ElapsedMilliseconds < 3000; i++)
          {
            CurrentNumber++;
            //Thread.Sleep(1);
          }
        }
        public void Start()
        {
          worker.RunWorkerAsync();
        }
    
        #region INotifyPropertyChanged Members
    
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(String propertyName)
        {
          if (PropertyChanged != null)
          {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
          }
        }
    
        #endregion
    
        private void PropertyChangedCallback(IAsyncResult result)
        {
          PropertyChangingEventHandler caller = ((AsyncResult)result).AsyncDelegate as PropertyChangingEventHandler;
          caller.EndInvoke(result);
        }
      }

    Tuesday, April 10, 2012 8:34 PM
  • Hi b_levitt,

    Your class updates UI correctly on my side, my test Xaml is:

    <StackPanel>
        <Label Content="{Binding CurrentNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </StackPanel>

    Then I set DataContext as:

    MyINotifyClass DataSource = new MyINotifyClass ();
            public MainWindow()
            {
                InitializeComponent();
                this.DataContext = DataSource;
                DataSource.Start();
            }

    It works well, could you tell me what is your main conern.

    Best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, April 12, 2012 7:01 AM
    Moderator
  • If CurrentNumber is bound to WPF object it won't show up until you marshall the data onto GUI thread like this:

    MyLabel.Dispather.Invoke((Action)delegate(){
          MyLabel.Text = Current;
    
    });
    
    //Or in the setter method of Current
    
    set{
       this.Dispatcher.Invoke((Action)Delegate(){
         Current = value;
         NotifyPropChanged("Current");
      });
    }

    You can also use the DoWork E.Result field to pump to the GUI thread via a push operation from that other thread. You will have to set up an event handler in the BackgroundWorker. That event handler will be running on the GUI thread. Typically, in the past, I never attempted to update directly to GUI from worker thread. However, with that advent of the newer TASK class I do that often now, like this:

    //notice you don't need background worker any longer
    //this is much simpler to use
    
    Task task = Task.Factory.StartNew(p=>{
     Stopwatch stopwatch = new Stopwatch();
          stopwatch.Start();
          for (int i = 0; stopwatch.ElapsedMilliseconds < 3000; i++)
          {
            CurrentNumber++;
            //Thread.Sleep(1);
          }
    });

    Good Luck!


    JP Cowboy Coders Unite!

    Thursday, April 12, 2012 12:58 PM
  • Mr. Javaman, he is using a ViewModel which does not contain a Dispatcher, as for instance a Window would have.
    If one needs to call the Dispatcher in a ViewModel, one should use the one provided by the Application, like so.

    App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(()=>{}));

    And i do not think this should be the case for this situation.

    As Sheldon says, it seems to be functioning as it should. 
    What setup are you running, how fast is your machine, and have you attempted to build the application and run it without the debugger ?


    Developing is part of being a developer.

    Thursday, April 12, 2012 1:34 PM
  • As far as I know there is no such thing as auto marshalling.  Are you using winforms or WPF?

    JP Cowboy Coders Unite!


    Javaman, FYI this is related to the same topic I brought up here (http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/2ce935b1-d8ce-4580-8ef5-0415fe82669c) for which I'm still trying to figure out why I used to have to marshal over to the UI.  But the consensus (and seemingly current behavior) is that WPF will in fact marshal the databindings onto the UI thread automatically.
    Thursday, April 12, 2012 1:49 PM
  • Correct Viewmodel does not usually have a dispatcher and even if it did it would not be the same GUI dispachter right?  When needed the ViewModel can easily access dispatcher of view like this. 

    Application.Current.MainWindow.Dispatcher.Invoke

    I find this necessary when I'm running multiple threads.

    JP Cowboy Coders Unite!


    Thursday, April 12, 2012 2:44 PM
  • Your link doesn't work, I am interested in the Automarshalling concept.

    JP Cowboy Coders Unite!

    Thursday, April 12, 2012 2:44 PM
  • Mr. Javaman, do you code in MVVM or direct code behind ? To me it seems like you are doing code behind, in that case you will need to use the dispatcher to update the Element values, but when binding the case is different.

    His link is this

    http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/2ce935b1-d8ce-4580-8ef5-0415fe82669c


    Developing is part of being a developer.

    Thursday, April 12, 2012 2:54 PM
  • I do it both ways MVVM and code behind where ever it suits the design.

    Correct me if I'm wrong but here's my understanding.

    If a view is bound to MVVM collection, the ViewModel is a static class that can be instanciated in the View via the "Window.Resources" etc.  That particular binding in the VM IS on the View dispatcher beacuse it was created in the View Call stack.  Any updates to the collection that are allowed are automatically on the proper GUI thread as a result. 

    However, consider this:  A Model then creates a new ObservableCollection on it's own thread and want's to replace the collection to the View Control in the View Model?  Can it do it just by this type of code in the viewmodel?

    MyViewModelCollection = MyModelCollection;

    Where MyModelCollection was created on another thread?

    I think the answer is always no.  Cross threaded data updates are not allowed.


    JP Cowboy Coders Unite!



    Thursday, April 12, 2012 3:04 PM
  • Your link doesn't work, I am interested in the Automarshalling concept.

    JP Cowboy Coders Unite!

    Strange, not sure why the link didn't work.  Trying again: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/2ce935b1-d8ce-4580-8ef5-0415fe82669c/
    Thursday, April 12, 2012 3:26 PM
  • I do it both ways MVVM and code behind where ever it suits the design.

    Correct me if I'm wrong but here's my understanding.

    If a view is bound to MVVM collection, the ViewModel is a static class that can be instanciated in the View via the "Window.Resources" etc.  That particular binding in the VM IS on the View dispatcher beacuse it was created in the View Call stack.  Any updates to the collection that are allowed are automatically on the proper GUI thread as a result. 

    However, consider this:  A Model then creates a new ObservableCollection on it's own thread and want's to replace the collection to the View Control in the View Model?  Can it do it just by this type of code in the viewmodel?

    MyViewModelCollection = MyModelCollection;

    Where MyModelCollection was created on another thread?

    I think the answer is always no.  Cross threaded data updates are not allowed.


    JP Cowboy Coders Unite!



    I just did the following:

    <Window x:Class="testWpf.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:coll="clr-namespace:System.Collections.ObjectModel;assembly=System"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:v4="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
            Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:testWpf" xmlns:System="clr-namespace:System;assembly=mscorlib">
        <Window.Resources>
            <my:ViewModel x:Key="ViewModel" />
        </Window.Resources>
        
        <ListBox ItemsSource="{Binding Source={StaticResource ViewModel}, Path=ViewModelCollection}" />
    </Window>
    public partial class MainWindow
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
        }
    
        public class ViewModel : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            private ObservableCollection<string> viewModelCollection;
    
            public ObservableCollection<string> ViewModelCollection
            {
                get
                {
                    return viewModelCollection;
                }
                set
                {
                    viewModelCollection = value;
    
                    PropertyChangedEventHandler temp = PropertyChanged;
                    if (temp != null)
                    {
                        temp(this, new PropertyChangedEventArgs("ViewModelCollection"));
                    }
                }
            }
    
            /// <summary>
            /// Initializes a new instance of the <see cref="T:System.Object"/> class.
            /// </summary>
            public ViewModel()
            {
                new Timer(AddItem, null, 0, 1000);
            }
    
            private void AddItem(object state)
            {
                ObservableCollection<string> newCollection = new ObservableCollection<string>();
                newCollection.Add(DateTime.Now.ToString());
                ViewModelCollection = newCollection;
            }
        }

    And it worked.  For simplicitly, the list only contains a single item (with the current time), but my window updates every second with the current time.  Just realized I left my using statements out - that is System.Threading.Timer (not dispatcher timer).  I verified in a breakpoint that it is NOT getting called on the UI thread.

    Thursday, April 12, 2012 3:38 PM
  • More on topic to the OP's code - I just gave it a try and got the same result.  I don't think the problem is whether WPF is marshalling or not.  It IS marshalling the call (otherwise there would be exceptions getting thrown).

    I think the problem is simply the rate of updates.  Your background worker is throwing out constant updates - many many times a second.  IIRC, in WPF rendering is a separate process from setting data, and data binding has a higher priority than rendering.  I think the constant change notification is keeping the UI busy telling the textbox it needs new text.  It is so busy doing that, it never has a chance to actually RENDER that text.

    In a recent test run of the posted code, "CurrentNumber" reaches a value of 4,411,397 over that 3 second period.  That is over 1 million updates a second!!

    I think it is just flooding the UI...  what are you updating that needs to change that frequently?


    Edit:  Updated numbers, forgot I had a small change in place.  Numbers got slightly smaller, but still high.
    • Edited by Cabadam Thursday, April 12, 2012 3:55 PM
    Thursday, April 12, 2012 3:52 PM
  • Maybe the flooding is hardware dependent, because Sheldon and I have had no issues watching it update the values without freezing untill the last UI Update.

    Developing is part of being a developer.

    Thursday, April 12, 2012 3:55 PM
  • Maybe the flooding is hardware dependent, because Sheldon and I have had no issues watching it update the values without freezing untill the last UI Update.

    Developing is part of being a developer.


    Possibly.  To clarify - mine froze on "0" until the very end.  Windows 7 x64, Quadro FX 1700, Intel Xeon 3GHz quad-core
    Thursday, April 12, 2012 3:58 PM
  • I am running Windows 8 x64 ( Consumer Pre.), Intel Core 2 Duo P8600 @ 2.40Ghz and a Mobility Radeon 3650.

    So not the most powerful laptop is being used to test it here.


    Developing is part of being a developer.

    Thursday, April 12, 2012 4:18 PM
  • Yes that example does work, which is interesting because I'm working on stuff right now that forces me to marshall data.. I'll have to try to figure out why.

    JP Cowboy Coders Unite!

    Thursday, April 12, 2012 6:36 PM
  • Why not start a Thread Mr. Java, maybe the hivemind can assist you ;-)

    Developing is part of being a developer.

    Thursday, April 12, 2012 6:37 PM
  • Good idea...


    JP Cowboy Coders Unite!

    Thursday, April 12, 2012 9:18 PM
  • Started new thread on why sometimes Marshalling data onto GUI is needed, sometimes not.

    http://social.msdn.microsoft.com/Forums/en-US/parallelextensions/thread/46c4c99c-98d8-4958-9927-7d8fcc350cc4


    JP Cowboy Coders Unite!

    Friday, April 13, 2012 11:24 AM
  • Cabadam is correctly calling out the key problem here.  The UI is most certainly updating as you have all confirmed, but if you remove the sleep, it does not update until the worker thread is complete.  I tried calling raising the PropertyChange with a BeginInvoke, and that had some strange results - the ui updates a couple of times and then froze.

    Cabadam, to address your question, "what are you updating?"  To answer I should say that this technically is NOT an MVVM class, but a lower level representation of a long running calculation that needs a reporting mechanism.  Make no mistake, I understand there are numerous approaches to fixing the problem with other notification methods.  I suppose if I was an absolutely devout believer of unconditional SOC, I could also wrap this with another class that filters out PropertyChanged events (hmmm might be a neat "dynamic" proxy class :)  or just polls the wrapped class.  But I wasn't quite ready to abandon the simplicity of simply making a property support INotify.  Best case is that one of you were going to tell me how to use some barely-known binding feature to filter recurring updates.  Worst case is I get a "can't be done" but get some insite into databinding internals.

    So is there a "simple" solution?

    If not how would you handle status updates?  BackgroundWorkers's ProgressChanged event would work but what if I was updating 30 fields on this class?  I suppose I could copy data to a new instance of the same class and send that.  I kinda like my proxy class idea as well.

    Thank you all for your help so far.

    Friday, April 13, 2012 7:16 PM
  • Question, are all of your updates actually unique?  Like in this case, the counter was incrementing each time through the loop to a new unique value.

    If, however, "CurrentNumber" was actually getting set to say, the number of TotalMilliseconds, or TotalSeconds of the stopwatch, that would be very different.  There would be many invocations of the property changed notification that actually have the same value as the previous call (thus, it is a "waste" of a databind/re-render on the UI).

    To help that scenario, you could make your property look like:

    private Int32 currentNumber; public Int32 CurrentNumber { get { return this.currentNumber; } set {

    if (this.currentNumber != value) {

    this.currentNumber = value; OnPropertyChanged("CurrentNumber");

    } } }

    That way, it only posts data to the UI on an actual change.

    Friday, April 13, 2012 7:24 PM
  • Lets say the calculation is unque enough to where such code would only reduce updates by a small percentage or not at all - like calculating each digit of pi.  The only realistic fix that I've thought of so far that involves the event itself  is to make the stopwatch a global variable and only raise PropertyChanged if at some period of time has past.  However that would require some additional code to also fire on the last update.

    Friday, April 13, 2012 7:33 PM
  • BTW Cab,

      I dug into the Threading team's web blog a bit, and found that they've been tweeking ThreadStart logic.  I don't know how deep into the internals it goes, but the story from them is that when using a TASK.FACTORY.STARTNEW contruct, the default Scheduler will always try to attempt attaching on the GUI SCHEDULER.  Now I don't know if this just recently changed, but this explains a lot and one of the many reasons I've become a TASK fan.  I am still being told by some folks that I should NEVER attempt to just update a property on a thread from another thread even though it appears to work.  They say and it could be the same thing going on here, is that it IS NOT RELIABLE. 

    Couple that with the excellent discussion on the known laggard we call the WPF Rendering Engine; and, well it turns out that perhaps too many updates will show the Rendering Engine flaws.

    I was also chastised a few weeks back by Servy42 who told me that understanding the internals (deep internals) to .NET is very important.  95% of time he's wrong, but in cases like this one, he's right.  The question here is what is really going on...

    Only WINDBG can answer this one.  Right?


    JP Cowboy Coders Unite!


    Friday, April 13, 2012 7:33 PM
  • Lets say the calculation is unque enough to where such code would only reduce updates by a small percentage or not at all - like calculating each digit of pi.  The only realistic fix that I've thought of so far that involves the event itself  is to make the stopwatch a global variable and only raise PropertyChanged if at some period of time has past.  However that would require some additional code to also fire on the last update.

    Ouch, that's unfortunate :)

    The next best thing I can think of is that your UI (or your VM - although I think you said this was not in the MVVM environment) has a DispatcherTimer explicitly for updating this field.  It could be a fairly fast timer - afterall, a 1ms pause was enough for the UI to catch it's breath.  Maybe fire the timer every 100ms, or even 10ms and update the field (to keep it databound, you could create some secondary property to actually bind to, and the timer just moves the value from the real property to the secondary property, which in turn fires off the change notification to the UI.

    This is basically polling it, which I normally would not care for, but I'm not aware of anyway to get this tied in just with direct databinding.

    Friday, April 13, 2012 7:45 PM
  • BTW Cab,

      I dug into the Threading team's web blog a bit, and found that they've been tweeking ThreadStart logic.  I don't know how deep into the internals it goes, but the story from them is that when using a TASK.FACTORY.STARTNEW contruct, the default Scheduler will always try to attempt attaching on the GUI SCHEDULER.  Now I don't know if this just recently changed, but this explains a lot and one of the many reasons I've become a TASK fan.  I am still being told by some folks that I should NEVER attempt to just update a property on a thread from another thread even though it appears to work.  They say and it could be the same thing going on here, is that it IS NOT RELIABLE. 

    Couple that with the excellent discussion on the known laggard we call the WPF Rendering Engine; and, well it turns out that perhaps too many updates will show the Rendering Engine flaws.

    I was also chastised a few weeks back by Servy42 who told me that understanding the internals (deep internals) to .NET is very important.  95% of time he's wrong, but in cases like this one, he's right.  The question here is what is really going on...

    Only WINDBG can answer this one.  Right?


    JP Cowboy Coders Unite!


    I don't want to derail b_levitt's thread any more than we already have since his problem is not related to UI threading :)

    But... as for the updating of properties being reliable or not, I did dig in with dotPeek earlier and found where the UI databinding explicitly checks if a Dispatcher Invoke is needed or not - but only in the case of INotifyPropertyChanged bindings.  I found it in PresentationFramework.dll -> MS.Internal.Data.ClrBindingWorker.OnSourcePropertyChanged()

    That actually has nothing to do with it being reliable, other than I believe it will always do it, at least until the next version of .NET after which who knows!

    Friday, April 13, 2012 7:50 PM
  • Please, derail all you like.  If the answer to my OP "can't do it" yet I have a better understanding of the internals, I think that is a good answer.  You've all been helpful so I'd be happy here, but if you have time please continue with me if you are able to elaborate.

    So what's the deal here.  At some point the binding engine subscribed to my PropertyChanged event.  How is it subscribed without blocking my worker thread?  And what is it doing?  Is it simply keeping the last update and throwing the rest away?  What mechanism allows

    Friday, April 13, 2012 8:12 PM
  • OnSourcePropertyChanged - the method I referenced in my last post basically looks like:

          if (this.Dispatcher.Thread == Thread.CurrentThread)
          {
            this.PW.OnPropertyChangedAtLevel(level);
          }
          else
          {
            this.SetTransferIsPending(true);
            this.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, (Delegate) new DispatcherOperationCallback(this.ScheduleTransferOperation), (object) new object[2]
            {
              o,
              (object) propName
            });

    So, if you are NOT on the UI's (Dispatcher's) thread, then it calls Dispatcher.BeginInvoke to asynchronously update the UI.  That is why it does not block your worker thread.  However, as previously discussed, you are sending these updates much faster than the UI can actually process and render them.

    The first thing to remember here is to separate in your head the act of assigning data to a control (data binding), and the act of the control actually drawing itself to the screen (rendering).

    Note that calling BeginInvoke basically takes your delegate and puts it in the queue for the UI to process.  Note however, that it is not a simple queue, but a priority queue.  Note that this action is going in with a priority of DataBind.  It is also important to note that the act of rendering gets it's own entry in the priority queue, at a different priority - "Render".

    If you check the DispatcherPriority enumeration documentation, DataBind has a value of 8, and Render has a value of 7.  The documentation for Render says it is equivalent to DataBind, but I think our experience in this thread shows that given a DataBind and a Render entry, the DataBind is getting the higher priority (which it should based on the enumeration itself - 8 > 7).

    So, your constant sending of priority 8 databinds keeps the slightly lower priority 7 render from ever taking place.  The UI does KNOW that all of those updates took place, and the textbox's value is actually changing, it just doesn't actually get rendered.

    Does that make sense?

    • Marked as answer by b_levitt Friday, April 13, 2012 8:45 PM
    Friday, April 13, 2012 8:39 PM
  • > a class that implements INotifyPropertyChanged [...] It does this search on a BackgroundWorker thread. [...] The CurrentValue property is databound to a label. [...] Is there some mechanism for me to force the marshalling to occur
     
     
    an example is here (works properly without any changes under all application models: WPF, WinForms, ASP.NET)

      
    Friday, April 13, 2012 8:41 PM