Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Caching & Cross Thread exception #15

Open
remcoros opened this issue Sep 9, 2011 · 4 comments
Open

Caching & Cross Thread exception #15

remcoros opened this issue Sep 9, 2011 · 4 comments

Comments

@remcoros
Copy link

remcoros commented Sep 9, 2011

Hey,

from time to time (cannot reproduce this, it happens after some time working with our silverlight app) I get this exception (see below).

Using the latest (1.3) from NuGet.

Any idea?

Remco Ros


Default.aspx:59Uncaught Error: Unhandled Error in Silverlight Application
Code: 4004
Category: ManagedRuntimeError
Message: System.UnauthorizedAccessException: Invalid cross-thread access.

at MS.Internal.XcpImports.CheckThread()

at MS.Internal.XcpImports.CreateObjectByTypeIndex(UInt32 typeIndex)

at System.Windows.Threading.DispatcherTimer..ctor(UInt32 nativeTypeIndex)

at System.Windows.Threading.DispatcherTimer..ctor()

at Agatha.Common.Caching.Timers.TimerWrapper.Start()

at Agatha.Common.Caching.InMemoryCache.Store(Request request, Response response, TimeSpan expiration)

at Agatha.Common.Caching.CacheManager.StoreInCache(Request request, Response response, TimeSpan expiration, String region)

at Agatha.Common.Caching.CacheManager.StoreInCache(Request request, Response response)

at Agatha.Common.ResponseReceiver.AddCacheableResponsesToCache(Response[] receivedResponses, Request[] requestsToSend)

at Agatha.Common.ResponseReceiver.ReceiveResponses(ProcessRequestsAsyncCompletedArgs args, Response[] tempResponseArray, Request[] requestsToSendAsArray)

at Agatha.Common.AsyncRequestDispatcher.OnProcessRequestsCompleted(ProcessRequestsAsyncCompletedArgs args, ResponseReceiver responseReciever, Response[] tempResponseArray, Request[] requestsToSendAsArray)

at Agatha.Common.AsyncRequestDispatcher.<>c__DisplayClass4.b__0(ProcessRequestsAsyncCompletedArgs a)

at Agatha.Common.WCF.AsyncRequestProcessorProxy.OnProcessRequestsCompleted(Object state)

at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)

at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)

at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()

at System.Threading.ThreadPoolWorkQueue.Dispatch()

at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

@davybrion
Copy link
Owner

no idea, unfortunately...

seeing as how you can't reproduce this, i assume it happens infrequently? if so, can you keep an eye on it to see if it always happens for the same type of request? If so, it would be interesting to see in how many places the request is sent, and if it always fails for a specific call, or if it fails on multiple calls.

@gschuager
Copy link

Hi, I had a similar problem when issuing requests on background threads.
The problem was that TimerWrapper (particular implementation of ITimer) does not support this because it is using DispatcherTimer to do the timing.
I solved the issue by registering my own implementation of ITimerProvider before configuring Agatha (I'm using Castle Windsor, so my implementation takes precedence over the default one).

Something like this:

public class TimerProvider : ITimerProvider
{
    public ITimer GetTimer(double interval)
    {
        return new NonDispatcherTimer(interval);
    }
}

public class NonDispatcherTimer : ITimer
{
    private readonly double interval;
    private readonly Timer timer;

    public NonDispatcherTimer(double interval)
    {
        this.interval = interval;
        timer = new Timer(TimerCallback);
    }

    private void TimerCallback(object state)
    {
        Elapsed(this, EventArgs.Empty);
    }

    public void Dispose()
    {
        timer.Dispose();
    }

    public void Start()
    {
        timer.Change((int) interval, (int) interval);
    }

    public void Stop()
    {
        timer.Change(Timeout.Infinite, Timeout.Infinite);
    }

    public event EventHandler Elapsed = delegate { };
}

@remcoros
Copy link
Author

Yeah, I was executing this in OnInitialize (from the Caliburn framework):

            IsBusy = true;

            Execute.OnBackgroundThread(
                () => this.manufacturerRepository.FindAll(
                    result => Execute.OnUIThread(() =>
                        {
                            this.manufacturers = result;
                            this.LoadFilters();
                        })),
                (object s, RunWorkerCompletedEventArgs e) =>
                {
                    IsBusy = false;
                });

(the repository call translates to a Agatha request)

I first tried without the Execute on UI thread. then I figured the Timer uses a DispatcherTimer so I tried to execute the callback on the UI thread. Both error.

If I remove the execute on background thread, it works. But it blocks the UI

I will try your timer implementation, but with a normal Timer, don't you have to handle UI thread synchronisation yourself?

Edit: seems to work, no issues yet.

@remcoros remcoros reopened this Sep 15, 2011
@gschuager
Copy link

There is no need to do UI thread sync when using Caliburn.... the framework marshals PropertyChanged events to the UI thread regardless of the thread from which you are modifying your properties (see http://caliburn.codeplex.com/wikipage?title=PropertyChangedBase%20and%20BindableCollection&referringTitle=Documentation )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants