GithubHelp home page GithubHelp logo

Crash Running on iOS about clipper2 HOT 14 CLOSED

divil5000 avatar divil5000 commented on August 21, 2024
Crash Running on iOS

from clipper2.

Comments (14)

divil5000 avatar divil5000 commented on August 21, 2024 1

Thanks for your help. If that can make its way into the next general release that would be great.

It might be worth taking a look at whether some types within the library would be more efficient being structs; things like polygon edges or horizontal segments. There is a high cost for allocations on iOS and Android and we've made huge performance improvements in our app by keeping cached buffers around and limiting the amount of class instantiations, simply by giving the GC less to do.

from clipper2.

AngusJohnson avatar AngusJohnson commented on August 21, 2024

Sorry, I don't have iOS to test this.
And while it's very unlikely to be the cause, could the compiler be misbehaving while parsing your very long path statement?
Wouldn't it be better wrapping it over a number of lines?

    public static void Test()
    {
      PathsD subject = new();
      subject.Add(Clipper.MakePath(new double[] {
    -8470.4501953125, -42813.28125, -8460.7470703125, -42814.30078125, -8440.5146484375, -42820.4609375,
     -8420.0205078125, -42825.6875, -8399.310546875, -42829.984375, -8378.4248046875, -42833.3359375,
     -8357.4091796875, -42835.73046875, -8336.3046875, -42837.17578125, -8315.15625, -42837.65234375,
     -8294.0087890625, -42837.17578125, -8272.904296875, -42835.73046875, -8251.8876953125, -42833.3359375,
     -8231.001953125, -42829.984375, -8210.2919921875, -42825.6875, -8189.798828125, -42820.4609375,
     -8169.56591796875, -42814.30078125, -8149.63525390625, -42807.234375, -8130.0478515625, -42799.26953125,
     -8110.84375, -42790.42578125, -8092.0634765625, -42780.71484375, -8073.74462890625, -42770.1640625,
     -8055.92626953125, -42758.80078125, -8038.64501953125, -42746.63671875, -8021.93505859375, -42733.69921875,
     -8005.83203125, -42720.02734375, -7990.3681640625, -42705.63671875, -7975.576171875, -42690.55859375,
     -7961.4853515625, -42674.8359375, -7948.12451171875, -42658.48828125, -7935.52099609375, -42641.5546875,
     -7923.7001953125, -42624.0703125, -7912.68603515625, -42606.078125, -7902.5, -42587.6015625,
     -7893.16357421875, -42568.69140625, -7884.6953125, -42549.3828125, -7877.11181640625, -42529.70703125,
     -7870.42822265625, -42509.71875, -7864.65673828125, -42489.45703125, -7859.8095703125, -42468.94921875,
     -7855.8955078125, -42448.2578125, -7852.92236328125, -42427.41015625, -7850.8955078125, -42406.453125,
     -7849.81884765625, -42385.43359375, -7849.0029296875, -42371.94921875, -7849.0029296875, -42012.6640625,
     -7849.0029296875, -41318.671875, -8224.318359375, -40734.03125, -8492.20703125, -40526.49609375,
     -8609.970703125, -40434.828125, -8811.7705078125, -40277.3125, -9790.8564453125, -40277.3125,
     -9026.5986328125, -40790.921875, -9027.2783203125, -40791.44140625, -9029.970703125, -40792.84765625,
     -9027.005859375, -40794.21875, -9027.822265625, -40795.77734375, -9026.9111328125, -40796.8046875,
     -9028.0400390625, -40796.8359375, -9024.7216796875, -40798.8203125, -9025.673828125, -40802.96875,
     -9022.1513671875, -40804.66796875, -9025.197265625, -40807.984375, -9025.224609375, -40809.875,
     -9017.21484375, -40822.0625, -9017.9501953125, -40826.51953125, -9014.9580078125, -40830.203125,
     -9013.326171875, -40836.69921875, -9009.1923828125, -40838.5390625, -9009.369140625, -40841.125,
     -9006.486328125, -40842.79296875, -9006.5673828125, -40846.52734375, -9001.658203125, -40853.26171875,
     -9047.294921875, -41015.08984375, -9048.568359375, -41014.8203125, -9066.6923828125, -41005.09765625,
     -9085.611328125, -40997.03125, -9105.1748046875, -40990.69140625, -9125.2265625, -40986.125,
     -9145.6025390625, -40983.375, -9166.14453125, -40982.453125, -9186.6865234375, -40983.375,
     -9207.0634765625, -40986.125, -9227.1142578125, -40990.69140625, -9246.677734375, -40997.03125,
     -9265.59765625, -41005.09765625, -9283.7216796875, -41014.8203125, -9300.9052734375, -41026.125,
     -9317.009765625, -41038.9296875, -9331.9091796875, -41053.12109375, -9345.478515625, -41068.59375,
     -9357.611328125, -41085.22265625, -9368.2099609375, -41102.87109375, -9377.1875, -41121.40234375,
     -9384.470703125, -41140.671875, -9390, -41160.51171875, -9393.73046875, -41180.78125,
     -9395.6298828125, -41201.30078125, -9395.6826171875, -41221.91796875, -9393.88671875, -41242.45703125,
     -9390.255859375, -41262.76171875, -9388.34375, -41269.80859375, -9254.806640625, -41225.50390625,
     -9246.5263671875, -41223.62109375, -9230.2265625, -41220.1015625, -9213.6455078125, -41218.3359375,
     -9196.970703125, -41218.3359375, -9180.3896484375, -41220.1015625, -9164.08984375, -41223.62109375,
     -9148.2548828125, -41228.8515625, -9133.0615234375, -41235.73046875, -9118.6845703125, -41244.1875,
     -9105.2841796875, -41254.12109375, -9093.8017578125, -41264.2265625, -9093.013671875, -41265.421875,
     -9082.009765625, -41277.9609375, -9072.3984375, -41291.60546875, -9064.2890625, -41306.1953125,
     -9061.708984375, -41311.69140625, -8998.6123046875, -41463.6875, -8992.357421875, -41478.72265625,
     -8900.16015625, -41700.05078125, -8731.5400390625, -41968.6875, -8534.0908203125, -42289.3125,
     -8437.814453125, -42444.76171875, -8470.4501953125, -42813.28125
      }));
      PathsD solution = Clipper.InflatePaths(subject, 0.5, Clipper2Lib.JoinType.Round, Clipper2Lib.EndType.Polygon);

      SvgWriter svg = new SvgWriter();
      SvgUtils.AddSubject(svg, subject);
      SvgUtils.AddSolution(svg, solution, false);
      string filename = @"..\..\..\test.svg";
      SvgUtils.SaveToFile(svg, filename, FillRule.NonZero, 400, 400, 10);
      ClipperFileIO.OpenFileWithDefaultApp(filename);
    }

from clipper2.

divil5000 avatar divil5000 commented on August 21, 2024

It's nothing to do with the long statement; I just made that up for the repro. In our actual code, the polygons are not created that way (it's an entity on a map). I was hoping that because you know what's going on inside ClipperBase.ExecuteInternal (and everything called by it) you might recognize something that could cause the runtime to fail so spectacularly.

from clipper2.

AngusJohnson avatar AngusJohnson commented on August 21, 2024

I was hoping that because you know what's going on inside ClipperBase.ExecuteInternal (and everything called by it) you might recognize something

LOL 🤣.
There is just about the entire clipping algorithm encapsulated there, so it could be just about anywhere.
I can't debug this myself, since I don't have any Apple Inc products to debug this.
Anyhow, I'm surprised you can't localise the problem by viewing a callstack.
Assuming you don't have a callstack, I suggest the following (that will require a little patience 😜):

  1. Within ExecuteInternal there's a loop where you can set debugging breakpoint neat the top.
  2. Set it to break when Y equals roughly the middle Y in your sample path.
  3. Manually (binary) adjust that Y value up or down to quickly determine the Y where the crash occurs.
  4. Step over each function call within ExecuteInternal until the crash occurs.
    Recursively repeat step 4 inside each problematic function.

from clipper2.

divil5000 avatar divil5000 commented on August 21, 2024

Yeah, that makes sense. There's no callstack because the entire managed runtime seems to crash. Breakpoints sometimes work, they sometimes don't. I spent an hour yesterday narrowing options down, then stepping through ExecuteInternal. I traced the crash reliably, ascertaining it happened when stepping over a call to ConvertHorzSegsToJoins. Then I stepped into that method, and it crashed inside _horzSegList.Sort(new HorzSegSorter());. Then I stepped into that method, and stepped through it, and everything worked, the function returned successfully.

I do wonder if the number of allocations made during these options is causing a problem. I've never spent much time looking at Clipper code, but having tried to do lots of debugging lately, there are a LOT of allocations that could perhaps be avoided with some pooling/caching. It might not be a problem under most environments but mobile (iOS/Android) is where allocations really start to cause problems.

I've opened an issue against the .net runtime itself because this sort of random crashing behaviour suggests that something is simply making the runtime unstable.

dotnet/runtime#98410

from clipper2.

AngusJohnson avatar AngusJohnson commented on August 21, 2024

it happened when stepping over a call to ConvertHorzSegsToJoins.

Unfortunately ConvertHorzSegsToJoins is truely ugly. Even I have to work hard to follow what I'm doing there.
And that code is non-essential, except that library users seem to prefer that polygons are in their simplest forms (ie avoiding polygons that share touching collinear edges).

I do wonder if the number of allocations made during these options is causing a problem.

I seriously doubt that the problem is due to insufficient memory if the code sample above is crashing. I guess it's possible you're somehow encountering an endless loop but that should be picked up doing the steps outlined above.

from clipper2.

olologin avatar olologin commented on August 21, 2024

I wonder if mono is using std::sort from libc++ directly (libc++ is the C++ implementation of STL that is used on MacOS by default).
Because libc++'s sort is very intolerant to incorrect (non-strict-weak) sorting predicates, it is the main cause of crashing std::sort in C++.

But I am not sure, problem with bad predicate in this project would have been noticed long ago, feels too obvious.

from clipper2.

philstopford avatar philstopford commented on August 21, 2024

Are you forced to use Mono? I had no end of trouble with it on macOS and that all went away when I moved to the Microsoft dotnet packages - stuff that would randomly or persistently fail only under mono and worked perfectly under dotnet proper. The mono developers never showed much interest in resolving them, so I would not be entirely surprised if this is coming from mono itself.

from clipper2.

divil5000 avatar divil5000 commented on August 21, 2024

I share your thoughts on Mono, however this is happening using the latest and greatest net8.0-ios and net7.0-ios runtimes. I've tested both.

from clipper2.

AngusJohnson avatar AngusJohnson commented on August 21, 2024

I suggest you try (temporarily) disabling some of this clipping code since it appears to be happening in non-essential routines. Doing this can help localise where it's crashing.
For example:

  1. in ClipperBase::ConvertHorzSegsToJoins(), place a return statement just above the std::stable_sort() call.
  2. See your test samples still crash.

If that stops the crashing, then repeat the above steps, except to place the return statement just below the sort function call.

from clipper2.

divil5000 avatar divil5000 commented on August 21, 2024

Thanks Angus. Not being familiar with the internal workings of the library, I didn't realise that this function was non-essential.

Returning the ConvertHorzSegsToJoins function just before the sort appears to solve the issue.
Returning the ConvertHorzSegsToJoins function just after the sort and we still get the crash.

Having just run the test above again, I've confirmed the results, and preventing the HorzSegSorter dedicated type and its function from being executed definitely appears to make the runtime issue go away.

As an experiment, I removed the HorzSegSorter type altogether (there's no need to a distinct type here) and instead just passed a new HorzSegSorter method (in ClipperBase) to the Sort function instead. This also seems to work, and I didn't change the method contents:

`_horzSegList.Sort(HorzSegSorter);

private int HorzSegSorter(HorzSegment hs1, HorzSegment hs2)
{
if (hs1 == null || hs2 == null) return 0;
if (hs1.rightOp == null)
{
return hs2.rightOp == null ? 0 : 1;
}
else if (hs2.rightOp == null)
return -1;
else
return hs1.leftOp!.pt.X.CompareTo(hs2.leftOp!.pt.X);
}`

So there's some progress, anyway. I'm not really comfortable shipping like this, but perhaps you could take a look at it. Maybe the runtime didn't like the fact that a dedicated struct type was there only to serve as a comparer (it is pretty unusual, I would expect it to be a class).

from clipper2.

AngusJohnson avatar AngusJohnson commented on August 21, 2024

Returning the ConvertHorzSegsToJoins function just before the sort appears to solve the issue.
Returning the ConvertHorzSegsToJoins function just after the sort and we still get the crash.

OK! Progress indeed!!
Evidently the List.Sort() routine that calls this function is implemented differently in the iOS C# compiler.
And yes, making the sort function a private method of ClipperBase and passing it as the parameter of _horzSegList List.Sort() seems to be the way to go. (I only implemented it the other way because of my relative inexperience in C# programming.)

private int HorzSegSort(HorzSegment hs1, HorzSegment hs2)
{
if (hs1 == null || hs2 == null) return 0;
if (hs1.rightOp == null)
{
return hs2.rightOp == null ? 0 : 1;
}
else if (hs2.rightOp == null)
return -1;
else
return hs1.leftOp!.pt.X.CompareTo(hs2.leftOp!.pt.X);
}

_horzSegList.Sort(HorzSegSort);

from clipper2.

AngusJohnson avatar AngusJohnson commented on August 21, 2024

It might be worth taking a look at whether some types within the library would be more efficient being structs; things like polygon edges or horizontal segments.

Unfortunately these have to be classes because they may be null referenced, and my understanding of C# is that structs must always point to assigned memory.

from clipper2.

divil5000 avatar divil5000 commented on August 21, 2024

That's kind of true, but Nullable<T> was invented to solve exactly this problem.

from clipper2.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.