v2.0: concurrency issues during map rendering

Topics: SharpMap v2.0
Developer
Jan 24, 2011 at 2:07 PM
Edited Jan 24, 2011 at 2:17 PM

I'm working with some experiments in a branch called CustomWmsSamples, where I use SharpMap v2 as WMS server and OpenLayers as WMS client.

Setting the client to request tiled data (i.e. 256x256 tiles of my maps), any request of a tile is made creating a new map object that loads the geometry data and render the map.

With the default map handlers I see that some tiles are correctly requested, but are not rendered.

My problem is fixed if I override my handler with code like this:

 

public override bool IsReusable        
{
  get { return false; }        
}
private static readonly object lockobj = new object(); 

public override void ProcessRequest(System.Web.HttpContext context)
{
  lock (lockobj)
    base.ProcessRequest(context);
}

that of course avoid concurrent creation/dispose of the processing part of the renderers.

I suspect that sharpmap maintain some state that generates issues like this... any suggestion to how to start my search?

 

EDIT: I'm using GdiImageRenderer and from other experiments looks like the problem can be inside the GdiImageRenderer.Render method

 

Coordinator
Jan 24, 2011 at 2:54 PM
Edited Jan 24, 2011 at 2:59 PM

Hi Diego, you have stumbled into GDI hell :p " A generic GDI Exception has occurred" and "Object in use elsewhere". I suspect that it is due to me caching the color/ brush and other styling objects. 

in visual studio if you do ctrl + alt + e and set break on all clr exceptions you may get more helpful exceptions.

If I think of any other pertinent info I will be sure to let you know.. jd

 

EDIT also MS do not support running GDI in session 0 (service) you get all manner of issues even when it is going well. When I get a chance I really want to finish the D2D renderer I was working on. D2D is supported in a server environment...

Developer
Jan 24, 2011 at 2:59 PM

Adding only this code to AsyncWmsHandlerBase.Render method (line 84):

lock (lockobj)                        

ConfigureMapView();

 

where lockobj is a static readonly object let gdi works as expected.

I hope to be close to find the problem ;)

Developer
Jan 24, 2011 at 5:12 PM

It's hard to find the problem (at least, for me) but using this code in SharpMap.Presentation.Presenters.MapPresenter2D,

both the rendering errors and the GDI Exception are fixed.

        private static readonly object lockobj = new object();

        /// <summary>
        /// Renders all layers and displays the result on the view.
        /// </summary>
        protected void RenderAllLayers()
        {
            lock (lockobj)
            {
                OnRenderingAllLayers();
                RenderAllLayers(RenderPhase.Normal);
                RenderAllLayers(RenderPhase.Selected);
                RenderAllLayers(RenderPhase.Highlighted);
                _currentRenderPhase = RenderPhase.None;
                OnRenderedAllLayers();
            }
        }

        protected void RenderLayer(ILayer layer)
        {
            lock (lockobj)
            {
                OnRenderingLayer();
                RenderLayer(layer, RenderPhase.Normal);
                RenderLayer(layer, RenderPhase.Selected);
                RenderLayer(layer, RenderPhase.Highlighted);
                _currentRenderPhase = RenderPhase.None;
                OnRenderedLayer();
            }
        }

Coordinator
Jan 24, 2011 at 5:33 PM

Hi Diego, while that will work, an unfortunate side effect is that all renders (across simultaneous requests) will be serialized - though I think you knew that already :) 

jd

Developer
Jan 25, 2011 at 7:35 AM
johndiss wrote:

Hi Diego, while that will work, an unfortunate side effect is that all renders (across simultaneous requests) will be serialized - though I think you knew that already :) 

jd

yes, I hope to find the actual problem and fix it without serialize all render requests.

Developer
Jan 25, 2011 at 8:40 PM

This problem looks even more strange as I explore into this issue.

Using this code into MapPresenter.RenderFeatureLayer(IFeatureLayer layer, RenderPhase phase)

IFeatureRenderer renderer = GetRenderer<IFeatureRenderer>(layer);
lock (renderer)
{
...
}

fix the error about tiles rendered as white for certain layers, but amplify the GDI error (object already used)

I suspect that the GraphicsPath object is shared across many rendering phases, so I need to isolate how it's used and how to release resources...

Coordinator
Jan 26, 2011 at 7:42 AM

You might want to check the CairoImageRenderer (resides in trunk) or the WpfImageRenderer (resides in branches/initalgdalintegration).

Cheers FObermaier

Developer
Jan 26, 2011 at 8:05 AM

CairoImageRenderer is also in my branch (I think that the trunk version is the same that is in my branch) but don't render any image (I need to investigate).

I've not tried with WPFRenderer, I try soon.

Thanks ;)

Developer
Jan 26, 2011 at 2:29 PM

Close to solve the problem!

Inside GdiImageRenderer.RenderObject you can solve the problem using cloned instances of the pen and brush objects (i.e. outline, fill, ecc ecc): no need to clone also GdiPath object.

This fix the problem in my branch, although I think that this add memory pressure (but maybe I can dispose objects as soon as they're used).

 

Developer
Jan 26, 2011 at 3:42 PM

here's my proposed patch:

https://docs.google.com/leaf?id=0B2rsw4ikqmh9ZmQ1YmMzYWEtNDRhZi00MWYyLWE5MGEtMGNmNmNjM2Q0OTk0&hl=it+

hope it helps

Coordinator
Jan 27, 2011 at 7:46 AM

thanks diego, for finding this.

While I glanced through your code, I wondered if it'd be possible to make the ViewConverter thread aware for cached objects like pens, fonts and brushes? that way we could omit cloning and disposing of these objects.

FObermaier

 

 

Developer
Jan 27, 2011 at 8:20 AM

I agree with you, FObermaier.

While my patch works, I still consider it a workaround.

Making all code ThreadAware can resolve maybe other issues.