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

Add error checking to ForCollapse #120

Merged
merged 3 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions DotMP-Tests/ParallelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1451,6 +1451,18 @@ public void Absent_params_shouldnt_except()
Assert.Null(exception);
}

/// <summary>
/// Verifies that for loops which overflow internal indices should throw an exception.
/// </summary>
[Fact]
public void Overflow_for_should_except()
{
Assert.Throws<DotMP.Exceptions.TooManyIterationsException>(() =>
{
DotMP.Parallel.ParallelForCollapse((0, 256), (0, 256), (0, 256), (0, 256), (i, j, k, l) => { });
});
}

/// <summary>
/// Verifies that invalid parameters throw exceptions.
/// </summary>
Expand All @@ -1467,6 +1479,16 @@ public void Invalid_params_should_except()
DotMP.Parallel.ParallelFor(-1, 10, i => { });
});

Assert.Throws<DotMP.Exceptions.InvalidArgumentsException>(() =>
{
DotMP.Parallel.ParallelForCollapse((10, 20), (-1, 10), (i, j) => { });
});

Assert.Throws<DotMP.Exceptions.InvalidArgumentsException>(() =>
{
DotMP.Parallel.ParallelForCollapse((10, 5), (0, 20), (i, j) => { });
});

Assert.Throws<DotMP.Exceptions.InvalidArgumentsException>(() =>
{
DotMP.Parallel.ParallelFor(10, -5, i => { });
Expand Down
43 changes: 38 additions & 5 deletions DotMP/Wrappers.cs → DotMP/Chunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.

*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.

*
* You should have received a copy of the GNU Lesser General Public License along with this library; if not,
* write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

using System;
using System.Linq;
using System.Runtime.CompilerServices;
using DotMP.Exceptions;

/* jscpd:ignore-start */
namespace DotMP
Expand Down Expand Up @@ -179,7 +179,41 @@ private enum ActionSelector
/// <summary>
/// Represents the ranges of collapsed loops for future unflattening.
/// </summary>
private ValueTuple<int, int>[] ranges;
private ValueTuple<int, int>[] ranges_prv;

/// <summary>
/// Represents the ranges of collapsed loops for unflattening, ensuring that loops do not have too many iterations.
/// </summary>
private ValueTuple<int, int>[] ranges
{
get => ranges_prv;
set
{
int total_iters = 1;

try
{
foreach ((int start, int end) in value)
{
if (end < start)
throw new InvalidArgumentsException(string.Format("Start of loop ({0}) must be less than end of loop ({1}).", start, end));

if (start < 0 || end < 0)
throw new InvalidArgumentsException(string.Format("Start ({0}) and end ({1}) of loop must both be positive integers.", start, end));

total_iters = checked(total_iters * (end - start));
}
}
catch (OverflowException)
{
throw new TooManyIterationsException(string.Format("Parallel-for loop has too many iterations (exceeds {0}).", int.MaxValue));
}
finally
{
ranges_prv = value;
}
}
}

/// <summary>
/// Array representing the starting indices.
Expand Down Expand Up @@ -420,7 +454,6 @@ private void ComputeIndicesN(int curr_iter, int[] indices)

/// <summary>
/// Executes a chunk using the action selected by ForAction.selector
/// TODO: Optimize this whole function.
/// </summary>
/// <param name="curr_iter">A reference to the current iteration.</param>
/// <param name="start">The start of the chunk, inclusive.</param>
Expand Down
12 changes: 12 additions & 0 deletions DotMP/Exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,16 @@ public class InvalidArgumentsException : Exception
/// <param name="msg">The message to associate with the exception.</param>
public InvalidArgumentsException(string msg) : base(msg) { }
}

/// <summary>
/// Exception thrown if a for loop has too many iterations and would cause the schedulers to fail.
/// </summary>
public class TooManyIterationsException : Exception
{
/// <summary>
/// Constructor with a message.
/// </summary>
/// <param name="msg">The message to associate with the exception.</param>
public TooManyIterationsException(string msg) : base(msg) { }
}
}
8 changes: 8 additions & 0 deletions DotMP/Parallel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ public static void For(int start, int end, Action<int> action, IScheduler schedu
/// <exception cref="NotInParallelRegionException">Thrown when not in a parallel region.</exception>
/// <exception cref="CannotPerformNestedWorksharingException">Thrown when nested inside another worksharing region.</exception>
/// <exception cref="InvalidArgumentsException">Thrown if any provided arguments are invalid.</exception>
/// <exception cref="TooManyIterationsException">Thrown if there are too many iterations to handle.</exception>
public static void ForCollapse((int, int) firstRange, (int, int) secondRange, Action<int, int> action, IScheduler schedule = null, uint? chunk_size = null)
{
ForAction<object> forAction = new ForAction<object>(action, new (int, int)[] { firstRange, secondRange });
Expand Down Expand Up @@ -231,6 +232,7 @@ public static void ForCollapse((int, int) firstRange, (int, int) secondRange, Ac
/// <exception cref="NotInParallelRegionException">Thrown when not in a parallel region.</exception>
/// <exception cref="CannotPerformNestedWorksharingException">Thrown when nested inside another worksharing region.</exception>
/// <exception cref="InvalidArgumentsException">Thrown if any provided arguments are invalid.</exception>
/// <exception cref="TooManyIterationsException">Thrown if there are too many iterations to handle.</exception>
public static void ForCollapse((int, int) firstRange, (int, int) secondRange, (int, int) thirdRange, Action<int, int, int> action, IScheduler schedule = null, uint? chunk_size = null)
{
ForAction<object> forAction = new ForAction<object>(action, new (int, int)[] { firstRange, secondRange, thirdRange });
Expand Down Expand Up @@ -260,6 +262,7 @@ public static void ForCollapse((int, int) firstRange, (int, int) secondRange, (i
/// <exception cref="NotInParallelRegionException">Thrown when not in a parallel region.</exception>
/// <exception cref="CannotPerformNestedWorksharingException">Thrown when nested inside another worksharing region.</exception>
/// <exception cref="InvalidArgumentsException">Thrown if any provided arguments are invalid.</exception>
/// <exception cref="TooManyIterationsException">Thrown if there are too many iterations to handle.</exception>
public static void ForCollapse((int, int) firstRange, (int, int) secondRange, (int, int) thirdRange, (int, int) fourthRange, Action<int, int, int, int> action, IScheduler schedule = null, uint? chunk_size = null)
{
ForAction<object> forAction = new ForAction<object>(action, new (int, int)[] { firstRange, secondRange, thirdRange, fourthRange });
Expand Down Expand Up @@ -287,6 +290,7 @@ public static void ForCollapse((int, int) firstRange, (int, int) secondRange, (i
/// <exception cref="NotInParallelRegionException">Thrown when not in a parallel region.</exception>
/// <exception cref="CannotPerformNestedWorksharingException">Thrown when nested inside another worksharing region.</exception>
/// <exception cref="InvalidArgumentsException">Thrown if any provided arguments are invalid.</exception>
/// <exception cref="TooManyIterationsException">Thrown if there are too many iterations to handle.</exception>
public static void ForCollapse((int, int)[] ranges, Action<int[]> action, IScheduler schedule = null, uint? chunk_size = null)
{
ForAction<object> forAction = new ForAction<object>(action, ranges);
Expand Down Expand Up @@ -394,6 +398,7 @@ public static void ForReduction<T>(int start, int end, Operations op, ref T redu
/// <exception cref="NotInParallelRegionException">Thrown when not in a parallel region.</exception>
/// <exception cref="CannotPerformNestedWorksharingException">Thrown when nested inside another worksharing region.</exception>
/// <exception cref="InvalidArgumentsException">Thrown if any provided arguments are invalid.</exception>
/// <exception cref="TooManyIterationsException">Thrown if there are too many iterations to handle.</exception>
public static void ForReductionCollapse<T>((int, int) firstRange, (int, int) secondRange, Operations op, ref T reduce_to, ActionRef2<T> action, IScheduler schedule = null, uint? chunk_size = null)
{
ForAction<T> forAction = new ForAction<T>(action, new (int, int)[] { firstRange, secondRange });
Expand Down Expand Up @@ -426,6 +431,7 @@ public static void ForReductionCollapse<T>((int, int) firstRange, (int, int) sec
/// <exception cref="NotInParallelRegionException">Thrown when not in a parallel region.</exception>
/// <exception cref="CannotPerformNestedWorksharingException">Thrown when nested inside another worksharing region.</exception>
/// <exception cref="InvalidArgumentsException">Thrown if any provided arguments are invalid.</exception>
/// <exception cref="TooManyIterationsException">Thrown if there are too many iterations to handle.</exception>
public static void ForReductionCollapse<T>((int, int) firstRange, (int, int) secondRange, (int, int) thirdRange, Operations op, ref T reduce_to, ActionRef3<T> action, IScheduler schedule = null, uint? chunk_size = null)
{
ForAction<T> forAction = new ForAction<T>(action, new (int, int)[] { firstRange, secondRange, thirdRange });
Expand Down Expand Up @@ -460,6 +466,7 @@ public static void ForReductionCollapse<T>((int, int) firstRange, (int, int) sec
/// <exception cref="NotInParallelRegionException">Thrown when not in a parallel region.</exception>
/// <exception cref="CannotPerformNestedWorksharingException">Thrown when nested inside another worksharing region.</exception>
/// <exception cref="InvalidArgumentsException">Thrown if any provided arguments are invalid.</exception>
/// <exception cref="TooManyIterationsException">Thrown if there are too many iterations to handle.</exception>
public static void ForReductionCollapse<T>((int, int) firstRange, (int, int) secondRange, (int, int) thirdRange, (int, int) fourthRange, Operations op, ref T reduce_to, ActionRef4<T> action, IScheduler schedule = null, uint? chunk_size = null)
{
ForAction<T> forAction = new ForAction<T>(action, new (int, int)[] { firstRange, secondRange, thirdRange, fourthRange });
Expand Down Expand Up @@ -492,6 +499,7 @@ public static void ForReductionCollapse<T>((int, int) firstRange, (int, int) sec
/// <exception cref="NotInParallelRegionException">Thrown when not in a parallel region.</exception>
/// <exception cref="CannotPerformNestedWorksharingException">Thrown when nested inside another worksharing region.</exception>
/// <exception cref="InvalidArgumentsException">Thrown if any provided arguments are invalid.</exception>
/// <exception cref="TooManyIterationsException">Thrown if there are too many iterations to handle.</exception>
public static void ForReductionCollapse<T>((int, int)[] ranges, Operations op, ref T reduce_to, ActionRefN<T> action, IScheduler schedule = null, uint? chunk_size = null)
{
ForAction<T> forAction = new ForAction<T>(action, ranges);
Expand Down