This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[gomp4] Library side of cancellation support


Hi!

This patch implements the library side of
#pragma omp cancel{,llation point} {parallel,for,sections}.
The compiler adds GOMP_cancel calls for #pragma omp cancel
and GOMP_cancellation_point calls for #pragma omp cancellation point,
both returning bool whether it has been cancelled or not (and in that
case the compiler arranges to run any needed destructors and jump to the
final barrier in for/sections workshare (note, the standard requires them
not to be nowait) or to the end of parallel region.
The compiler also changes calls to GOMP_loop_end, GOMP_barrier and
GOMP_sections_end in teams that can be cancelled to their *_cancel
counterpart that again return bool whether the team has been cancelled.

Workshare (for, sections) cancellation is implemented by doing nothing for
orphaned workshares (they are run by just one thread, so all is needed is
return true from corresponding GOMP_cancel) and by setting
work_share_cancelled flag in the team structure.  This flag is cleared when
the last thread enters corresponding barrier at the end of the workshare,
there must be one, because nowait workshare can't be cancelled.  And, if the
whole parallel is cancelled after cancelling just the workshare, no further
workshares will be started, so the fact that the flag hasn't been cleared
doesn't matter.

Parallel is cancelled by setting BAR_CANCELLED bit in team barrier's
generation field and waking everybody waiting on the team barrier.  There
are three possible cases, either no thread is waiting on any barrier when
cancelled, or at least one thread is waiting on some cancellable team
barrier (GOMP_{barrier,sections_end,loop_end}_cancel), or at least one
thread is waiting on the final barrier at the end of parallel (which is
necessarily non-cancellable, we must wait for all threads to settle).
If nothing is waiting on either barrier, we just ensure that any new
thread that arrives on the cancellable barrier will immediately return true,
for the final non-cancellable barriers we need to ensure just that no
new explicit threads will be started and make sure BAR_CANCELLED is cleared
from generation at the end (as we are sometimes reusing the team barrier for
another (non-team) barrier wait afterwards).  For the cancellable barrier,
we have to ensure no new threads will be started, and that as soon as
possible the barrier will return true to tell it to jump straight to end of
parallel where it enters the final team barrier.

For the config/linux/bar* implementation, which doesn't use any mutex around
the barrier handling, just atomics and futexes, unfortunately the awaited
field can be damaged in the cancellable barrier (we don't know how many
threads will see that barrier and how many will bypass it because of #pragma
omp cancel and #pragma omp cancellation point), so the patch uses a
different counter for the final team barrier (new entry points with *_final*
in the function names) to reliably await bar->total threads (and set
bar->awaited (and bar->awaited_final) to the right value in the last thread
entering the final barrier).  As team->barrier.generation is changed
non-atomically during task handling (guarded by team->task_lock), the
addition of BAR_CANCELLED bit is also guarded by team->task_lock mutex.
In the config/posix/bar* implementation, we have bar->mutex1, so we can just
use a flag whether at least one thread is wating in a cancellable barrier
(rather than non-cancellable).

Another problem is that, as cancel-parallel-3.c testcase shows, with
cancellation we can have potentially hundreds of workshares that still
need gomp_fini_workshare to be called on, that matters primarily for
the config/posix/ variant where mutex, ptrlock destroy isn't a noop;
gomp_fini_workshare also frees ordered allocations, but cancellation of
a parallel that might be in a middle of executing some ordered region
is necessarily a deadlock (raised this on omp-lang, as a defect against
OpenMP 4.0).

Tested with both normal linux configuration and --disable-linux-futex
configuration on x86_64-linux.

Will commit tomorrow unless somebody raises some issues; Torvald, when
you have time, could you please double check whether it looks sane from
the memory model/synchronization POV?

2013-09-23  Richard Henderson  <rth@redhat.com>
	    Jakub Jelinek  <jakub@redhat.com>

	* libgomp.map (GOMP_4.0): Export GOMP_barrier_cancel,
	GOMP_loop_end_cancel, GOMP_sections_end_cancel.
	* parallel.c (GOMP_cancellation_point): Add WHICH argument, do nothing
	if gomp_cancel_var is false, handle workshare and parallel
	cancellation point, add ialias.
	(GOMP_cancel): Add WHICH and DO_CANCEL arguments, do nothing if
	gomp_cancel_var is false, call GOMP_cancellation_point if DO_CANCEL
	is false, handle workshare and parallel cancellation.
	* config/posix/bar.c (gomp_barrier_init): Clear cancellable field.
	(gomp_team_barrier_wait_end): Clear BAR_CANCELLED from state.
	Set work_share_cancelled to 0 on last thread, use __atomic_load_n.
	(gomp_team_barrier_wait_cancel_end, gomp_team_barrier_wait_cancel,
	gomp_team_barrier_cancel): New functions.
	* config/posix/bar.h (gomp_barrier_t): Add cancellable field.
	(BAR_CANCELLED): Define.
	(BAR_INCR): Set to 8.
	(gomp_team_barrier_wait_cancel, gomp_team_barrier_wait_cancel_end,
	gomp_team_barrier_cancel): New prototypes.
	(gomp_barrier_wait_start): Preserve BAR_CANCELLED bit.
	(gomp_barrier_wait_cancel_start, gomp_team_barrier_wait_final,
	gomp_team_barrier_cancelled): New inline functions.
	* config/linux/bar.c (gomp_team_barrier_wait_end): Clear BAR_CANCELLED
	from state where needed.  Set work_share_cancelled to 0 on last thread.
	(gomp_team_barrier_wait_final, gomp_team_barrier_wait_cancel_end,
	gomp_team_barrier_wait_cancel, gomp_team_barrier_cancel): New
	functions.
	* config/linux/bar.h (gomp_barrier_t): Add awaited_final field.
	(BAR_CANCELLED): Define.
	(BAR_INCR): Set to 8.
	(gomp_barrier_init): Initialize awaited_final field.
	(gomp_team_barrier_wait_final, gomp_team_barrier_wait_cancel,
	gomp_team_barrier_wait_cancel_end, gomp_team_barrier_cancel): New
	prototypes.
	(gomp_barrier_wait_start): Preserve BAR_CANCELLED bit.
	(gomp_barrier_wait_cancel_start, gomp_team_barrier_wait_final_start,
	gomp_team_barrier_cancelled): New inline functions.
	* work.c (gomp_work_share_end, gomp_work_share_end_nowait): Set
	team->work_shares_to_free to thr->ts.work_share before calling
	free_work_share.
	(gomp_work_share_end_cancel): New function.
	* team.c (gomp_thread_start): Use gomp_team_barrier_wait_final
	instead of gomp_team_barrier_wait.
	(gomp_new_team): Initialize work_shares_to_free, work_share_cancelled
	and team_cancelled fields.
	(gomp_team_end): Use gomp_team_barrier_wait_final instead of
	gomp_team_barrier_wait.  If team->team_cancelled, call
	gomp_fini_worshare on ws chain starting at team->work_shares_to_free
	rather than thr->ts.work_share.
	* env.c (gomp_global_icv): Initialize target_data field.
	(gomp_cancel_var): New global variable.
	(handle_omp_display_env): Print cancel-var ICV.
	(initialize_env): Parse OMP_CANCELLATION env var.
	(omp_get_cancellation): Return gomp_cancel_var.
	* barrier.c (GOMP_barrier_cancel): New function.
	* libgomp.h (gomp_cancel_var): New extern decl.
	(struct gomp_team): Add work_shares_to_free, work_share_cancelled
	and team_cancelled fields.
	(gomp_cancel_kind): New enum.
	(gomp_work_share_end_cancel): New prototype.
	* libgomp_g.h (GOMP_barrier_cancel, GOMP_loop_end_cancel,
	GOMP_sections_end_cancel): New prototypes.
	(GOMP_cancel, GOMP_cancellation_point): Adjust prototypes.
	* task.c (GOMP_task): If gomp_team_barrier_cancelled, don't
	queue or start new tasks.
	(gomp_barrier_handle_tasks): If gomp_team_barrier_cancelled,
	don't start any new tasks, just free all tasks.
	* sections.c (GOMP_sections_end_cancel): New function.
	* loop.c (GOMP_loop_end_cancel): New function.
	* testsuite/libgomp.c/cancel-parallel-1.c: New test.
	* testsuite/libgomp.c/cancel-parallel-2.c: New test.
	* testsuite/libgomp.c/cancel-parallel-3.c: New test.
	* testsuite/libgomp.c/cancel-for-1.c: New test.
	* testsuite/libgomp.c/cancel-for-2.c: New test.
	* testsuite/libgomp.c/cancel-sections-1.c: New test.

--- libgomp/libgomp.map.jj	2013-09-19 18:59:27.593751150 +0200
+++ libgomp/libgomp.map	2013-09-19 19:02:44.658757828 +0200
@@ -208,14 +208,17 @@ GOMP_3.0 {
 
 GOMP_4.0 {
   global:
+	GOMP_barrier_cancel;
 	GOMP_cancel;
 	GOMP_cancellation_point;
+	GOMP_loop_end_cancel;
 	GOMP_parallel_loop_dynamic;
 	GOMP_parallel_loop_guided;
 	GOMP_parallel_loop_runtime;
 	GOMP_parallel_loop_static;
 	GOMP_parallel_sections;
 	GOMP_parallel;
+	GOMP_sections_end_cancel;
 	GOMP_taskgroup_start;
 	GOMP_taskgroup_end;
 	GOMP_target;
--- libgomp/parallel.c.jj	2013-09-19 18:59:27.580751217 +0200
+++ libgomp/parallel.c	2013-09-23 09:36:00.012251705 +0200
@@ -141,15 +141,58 @@ GOMP_parallel (void (*fn) (void *), void
   ialias_call (GOMP_parallel_end) ();
 }
 
-void
-GOMP_cancel (void)
+bool
+GOMP_cancellation_point (int which)
 {
-  /* Nothing so far.  */
+  if (!gomp_cancel_var)
+    return false;
+
+  struct gomp_team *team = gomp_thread ()->ts.team;
+  if (which & (GOMP_CANCEL_LOOP | GOMP_CANCEL_SECTIONS))
+    {
+      if (team == NULL)
+	return false;
+      return team->work_share_cancelled != 0;
+    }
+  else if (which & GOMP_CANCEL_TASKGROUP)
+    {
+      /* FIXME: Check if current taskgroup has been cancelled,
+	 then fallthru into the GOMP_CANCEL_PARALLEL case,
+	 because if the current parallel has been cancelled,
+	 all tasks should be cancelled too.  */
+    }
+  if (team)
+    return gomp_team_barrier_cancelled (&team->barrier);
+  return false;
 }
+ialias (GOMP_cancellation_point)
 
-void
-GOMP_cancellation_point (void)
+bool
+GOMP_cancel (int which, bool do_cancel)
 {
+  if (!gomp_cancel_var)
+    return false;
+
+  if (!do_cancel)
+    return ialias_call (GOMP_cancellation_point) (which);
+
+  struct gomp_team *team = gomp_thread ()->ts.team;
+  if (which & (GOMP_CANCEL_LOOP | GOMP_CANCEL_SECTIONS))
+    {
+      /* In orphaned worksharing region, all we want to cancel
+	 is current thread.  */
+      if (team != NULL)
+	team->work_share_cancelled = 1;
+      return true;
+    }
+  else if (which & GOMP_CANCEL_TASKGROUP)
+    {
+      /* FIXME: Handle taskgroup cancellation.  */
+      return true;
+    }
+  team->team_cancelled = 1;
+  gomp_team_barrier_cancel (team);
+  return true;
 }
 
 /* The public OpenMP API for thread and team related inquiries.  */
--- libgomp/config/posix/bar.c.jj	2013-07-04 17:25:15.000000000 +0200
+++ libgomp/config/posix/bar.c	2013-09-20 19:39:13.050156157 +0200
@@ -42,6 +42,7 @@ gomp_barrier_init (gomp_barrier_t *bar,
   bar->total = count;
   bar->arrived = 0;
   bar->generation = 0;
+  bar->cancellable = false;
 }
 
 void
@@ -113,12 +114,14 @@ gomp_team_barrier_wait_end (gomp_barrier
 {
   unsigned int n;
 
+  state &= ~BAR_CANCELLED;
   if (state & BAR_WAS_LAST)
     {
       n = --bar->arrived;
       struct gomp_thread *thr = gomp_thread ();
       struct gomp_team *team = thr->ts.team;
 
+      team->work_share_cancelled = 0;
       if (team->task_count)
 	{
 	  gomp_barrier_handle_tasks (state);
@@ -141,13 +144,18 @@ gomp_team_barrier_wait_end (gomp_barrier
   else
     {
       gomp_mutex_unlock (&bar->mutex1);
+      int gen;
       do
 	{
 	  gomp_sem_wait (&bar->sem1);
-	  if (bar->generation & BAR_TASK_PENDING)
-	    gomp_barrier_handle_tasks (state);
+	  gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
+	  if (gen & BAR_TASK_PENDING)
+	    {
+	      gomp_barrier_handle_tasks (state);
+	      gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
+	    }
 	}
-      while (bar->generation != state + BAR_INCR);
+      while (gen != state + BAR_INCR);
 
 #ifdef HAVE_SYNC_BUILTINS
       n = __sync_add_and_fetch (&bar->arrived, -1);
@@ -162,6 +170,81 @@ gomp_team_barrier_wait_end (gomp_barrier
     }
 }
 
+bool
+gomp_team_barrier_wait_cancel_end (gomp_barrier_t *bar,
+				   gomp_barrier_state_t state)
+{
+  unsigned int n;
+
+  if (state & BAR_WAS_LAST)
+    {
+      bar->cancellable = false;
+      n = --bar->arrived;
+      struct gomp_thread *thr = gomp_thread ();
+      struct gomp_team *team = thr->ts.team;
+
+      team->work_share_cancelled = 0;
+      if (team->task_count)
+	{
+	  gomp_barrier_handle_tasks (state);
+	  if (n > 0)
+	    gomp_sem_wait (&bar->sem2);
+	  gomp_mutex_unlock (&bar->mutex1);
+	  return false;
+	}
+
+      bar->generation = state + BAR_INCR - BAR_WAS_LAST;
+      if (n > 0)
+	{
+	  do
+	    gomp_sem_post (&bar->sem1);
+	  while (--n != 0);
+	  gomp_sem_wait (&bar->sem2);
+	}
+      gomp_mutex_unlock (&bar->mutex1);
+    }
+  else
+    {
+      if (state & BAR_CANCELLED)
+	{
+	  gomp_mutex_unlock (&bar->mutex1);
+	  return true;
+	}
+      bar->cancellable = true;
+      gomp_mutex_unlock (&bar->mutex1);
+      int gen;
+      do
+	{
+	  gomp_sem_wait (&bar->sem1);
+	  gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
+	  if (gen & BAR_CANCELLED)
+	    break;
+	  if (gen & BAR_TASK_PENDING)
+	    {
+	      gomp_barrier_handle_tasks (state);
+	      gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
+	      if (gen & BAR_CANCELLED)
+		break;
+	    }
+	}
+      while (gen != state + BAR_INCR);
+
+#ifdef HAVE_SYNC_BUILTINS
+      n = __sync_add_and_fetch (&bar->arrived, -1);
+#else
+      gomp_mutex_lock (&bar->mutex2);
+      n = --bar->arrived;
+      gomp_mutex_unlock (&bar->mutex2);
+#endif
+
+      if (n == 0)
+	gomp_sem_post (&bar->sem2);
+      if (gen & BAR_CANCELLED)
+	return true;
+    }
+  return false;
+}
+
 void
 gomp_team_barrier_wait (gomp_barrier_t *barrier)
 {
@@ -176,3 +259,40 @@ gomp_team_barrier_wake (gomp_barrier_t *
   while (count-- > 0)
     gomp_sem_post (&bar->sem1);
 }
+
+bool
+gomp_team_barrier_wait_cancel (gomp_barrier_t *bar)
+{
+  gomp_barrier_state_t state = gomp_barrier_wait_cancel_start (bar);
+  return gomp_team_barrier_wait_cancel_end (bar, state);
+}
+
+void
+gomp_team_barrier_cancel (struct gomp_team *team)
+{
+  if (team->barrier.generation & BAR_CANCELLED)
+    return;
+  gomp_mutex_lock (&team->barrier.mutex1);
+  gomp_mutex_lock (&team->task_lock);
+  if (team->barrier.generation & BAR_CANCELLED)
+    {
+      gomp_mutex_unlock (&team->task_lock);
+      gomp_mutex_unlock (&team->barrier.mutex1);
+      return;
+    }
+  team->barrier.generation |= BAR_CANCELLED;
+  gomp_mutex_unlock (&team->task_lock);
+  if (team->barrier.cancellable)
+    {
+      int n = team->barrier.arrived;
+      if (n > 0)
+	{
+	  do
+	    gomp_sem_post (&team->barrier.sem1);
+	  while (--n != 0);
+	  gomp_sem_wait (&team->barrier.sem2);
+	}
+      team->barrier.cancellable = false;
+    }
+  gomp_mutex_unlock (&team->barrier.mutex1);
+}
--- libgomp/config/posix/bar.h.jj	2013-09-19 18:59:27.560751317 +0200
+++ libgomp/config/posix/bar.h	2013-09-20 19:24:33.566664418 +0200
@@ -43,6 +43,7 @@ typedef struct
   unsigned total;
   unsigned arrived;
   unsigned generation;
+  bool cancellable;
 } gomp_barrier_t;
 
 typedef unsigned int gomp_barrier_state_t;
@@ -53,7 +54,8 @@ typedef unsigned int gomp_barrier_state_
 #define BAR_TASK_PENDING	1
 #define BAR_WAS_LAST		1
 #define BAR_WAITING_FOR_TASK	2
-#define BAR_INCR		4
+#define BAR_CANCELLED		4
+#define BAR_INCR		8
 
 extern void gomp_barrier_init (gomp_barrier_t *, unsigned);
 extern void gomp_barrier_reinit (gomp_barrier_t *, unsigned);
@@ -64,19 +66,43 @@ extern void gomp_barrier_wait_end (gomp_
 extern void gomp_team_barrier_wait (gomp_barrier_t *);
 extern void gomp_team_barrier_wait_end (gomp_barrier_t *,
 					gomp_barrier_state_t);
+extern bool gomp_team_barrier_wait_cancel (gomp_barrier_t *);
+extern bool gomp_team_barrier_wait_cancel_end (gomp_barrier_t *,
+					       gomp_barrier_state_t);
 extern void gomp_team_barrier_wake (gomp_barrier_t *, int);
+struct gomp_team;
+extern void gomp_team_barrier_cancel (struct gomp_team *);
 
 static inline gomp_barrier_state_t
 gomp_barrier_wait_start (gomp_barrier_t *bar)
 {
   unsigned int ret;
   gomp_mutex_lock (&bar->mutex1);
-  ret = bar->generation & -BAR_INCR;
+  ret = bar->generation & (-BAR_INCR | BAR_CANCELLED);
   if (++bar->arrived == bar->total)
     ret |= BAR_WAS_LAST;
   return ret;
 }
 
+static inline gomp_barrier_state_t
+gomp_barrier_wait_cancel_start (gomp_barrier_t *bar)
+{
+  unsigned int ret;
+  gomp_mutex_lock (&bar->mutex1);
+  ret = bar->generation & (-BAR_INCR | BAR_CANCELLED);
+  if (ret & BAR_CANCELLED)
+    return ret;
+  if (++bar->arrived == bar->total)
+    ret |= BAR_WAS_LAST;
+  return ret;
+}
+
+static inline void
+gomp_team_barrier_wait_final (gomp_barrier_t *bar)
+{
+  gomp_team_barrier_wait (bar);
+}
+
 static inline bool
 gomp_barrier_last_thread (gomp_barrier_state_t state)
 {
@@ -116,6 +142,12 @@ gomp_team_barrier_waiting_for_tasks (gom
   return (bar->generation & BAR_WAITING_FOR_TASK) != 0;
 }
 
+static inline bool
+gomp_team_barrier_cancelled (gomp_barrier_t *bar)
+{
+  return __builtin_expect ((bar->generation & BAR_CANCELLED) != 0, 0);
+}
+
 static inline void
 gomp_team_barrier_done (gomp_barrier_t *bar, gomp_barrier_state_t state)
 {
--- libgomp/config/linux/bar.c.jj	2013-09-19 18:59:27.550751367 +0200
+++ libgomp/config/linux/bar.c	2013-09-20 19:41:04.816584999 +0200
@@ -88,6 +88,7 @@ gomp_team_barrier_wait_end (gomp_barrier
       struct gomp_team *team = thr->ts.team;
 
       bar->awaited = bar->total;
+      team->work_share_cancelled = 0;
       if (__builtin_expect (team->task_count, 0))
 	{
 	  gomp_barrier_handle_tasks (state);
@@ -95,6 +96,7 @@ gomp_team_barrier_wait_end (gomp_barrier
 	}
       else
 	{
+	  state &= ~BAR_CANCELLED;
 	  state += BAR_INCR - BAR_WAS_LAST;
 	  __atomic_store_n (&bar->generation, state, MEMMODEL_RELEASE);
 	  futex_wake ((int *) &bar->generation, INT_MAX);
@@ -103,6 +105,7 @@ gomp_team_barrier_wait_end (gomp_barrier
     }
 
   generation = state;
+  state &= ~BAR_CANCELLED;
   do
     {
       do_wait ((int *) &bar->generation, generation);
@@ -122,3 +125,86 @@ gomp_team_barrier_wait (gomp_barrier_t *
 {
   gomp_team_barrier_wait_end (bar, gomp_barrier_wait_start (bar));
 }
+
+void
+gomp_team_barrier_wait_final (gomp_barrier_t *bar)
+{
+  gomp_barrier_state_t state = gomp_barrier_wait_final_start (bar);
+  if (__builtin_expect (state & BAR_WAS_LAST, 0))
+    bar->awaited_final = bar->total;
+  gomp_team_barrier_wait_end (bar, state);
+}
+
+bool
+gomp_team_barrier_wait_cancel_end (gomp_barrier_t *bar,
+				   gomp_barrier_state_t state)
+{
+  unsigned int generation, gen;
+
+  if (__builtin_expect (state & BAR_WAS_LAST, 0))
+    {
+      /* Next time we'll be awaiting TOTAL threads again.  */
+      /* BAR_CANCELLED should never be set in state here, because
+	 cancellation means that at least one of the threads has been
+	 cancelled, thus on a cancellable barrier we should never see
+	 all threads to arrive.  */
+      struct gomp_thread *thr = gomp_thread ();
+      struct gomp_team *team = thr->ts.team;
+
+      bar->awaited = bar->total;
+      team->work_share_cancelled = 0;
+      if (__builtin_expect (team->task_count, 0))
+	{
+	  gomp_barrier_handle_tasks (state);
+	  state &= ~BAR_WAS_LAST;
+	}
+      else
+	{
+	  state += BAR_INCR - BAR_WAS_LAST;
+	  __atomic_store_n (&bar->generation, state, MEMMODEL_RELEASE);
+	  futex_wake ((int *) &bar->generation, INT_MAX);
+	  return false;
+	}
+    }
+
+  if (__builtin_expect (state & BAR_CANCELLED, 0))
+    return true;
+
+  generation = state;
+  do
+    {
+      do_wait ((int *) &bar->generation, generation);
+      gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
+      if (__builtin_expect (gen & BAR_CANCELLED, 0))
+	return true;
+      if (__builtin_expect (gen & BAR_TASK_PENDING, 0))
+	{
+	  gomp_barrier_handle_tasks (state);
+	  gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
+	}
+      generation |= gen & BAR_WAITING_FOR_TASK;
+    }
+  while (gen != state + BAR_INCR);
+
+  return false;
+}
+
+bool
+gomp_team_barrier_wait_cancel (gomp_barrier_t *bar)
+{
+  return gomp_team_barrier_wait_cancel_end (bar, gomp_barrier_wait_start (bar));
+}
+
+void
+gomp_team_barrier_cancel (struct gomp_team *team)
+{
+  gomp_mutex_lock (&team->task_lock);
+  if (team->barrier.generation & BAR_CANCELLED)
+    {
+      gomp_mutex_unlock (&team->task_lock);
+      return;
+    }
+  team->barrier.generation |= BAR_CANCELLED;
+  gomp_mutex_unlock (&team->task_lock);
+  futex_wake ((int *) &team->barrier.generation, INT_MAX);
+}
--- libgomp/config/linux/bar.h.jj	2013-09-19 18:59:27.560751317 +0200
+++ libgomp/config/linux/bar.h	2013-09-20 19:28:43.863391927 +0200
@@ -38,6 +38,7 @@ typedef struct
   unsigned total __attribute__((aligned (64)));
   unsigned generation;
   unsigned awaited __attribute__((aligned (64)));
+  unsigned awaited_final;
 } gomp_barrier_t;
 
 typedef unsigned int gomp_barrier_state_t;
@@ -48,12 +49,14 @@ typedef unsigned int gomp_barrier_state_
 #define BAR_TASK_PENDING	1
 #define BAR_WAS_LAST		1
 #define BAR_WAITING_FOR_TASK	2
-#define BAR_INCR		4
+#define BAR_CANCELLED		4
+#define BAR_INCR		8
 
 static inline void gomp_barrier_init (gomp_barrier_t *bar, unsigned count)
 {
   bar->total = count;
   bar->awaited = count;
+  bar->awaited_final = count;
   bar->generation = 0;
 }
 
@@ -71,15 +74,21 @@ extern void gomp_barrier_wait (gomp_barr
 extern void gomp_barrier_wait_last (gomp_barrier_t *);
 extern void gomp_barrier_wait_end (gomp_barrier_t *, gomp_barrier_state_t);
 extern void gomp_team_barrier_wait (gomp_barrier_t *);
+extern void gomp_team_barrier_wait_final (gomp_barrier_t *);
 extern void gomp_team_barrier_wait_end (gomp_barrier_t *,
 					gomp_barrier_state_t);
+extern bool gomp_team_barrier_wait_cancel (gomp_barrier_t *);
+extern bool gomp_team_barrier_wait_cancel_end (gomp_barrier_t *,
+					       gomp_barrier_state_t);
 extern void gomp_team_barrier_wake (gomp_barrier_t *, int);
+struct gomp_team;
+extern void gomp_team_barrier_cancel (struct gomp_team *);
 
 static inline gomp_barrier_state_t
 gomp_barrier_wait_start (gomp_barrier_t *bar)
 {
   unsigned int ret = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
-  ret &= -BAR_INCR;
+  ret &= -BAR_INCR | BAR_CANCELLED;
   /* A memory barrier is needed before exiting from the various forms
      of gomp_barrier_wait, to satisfy OpenMP API version 3.1 section
      2.8.6 flush Construct, which says there is an implicit flush during
@@ -90,6 +99,26 @@ gomp_barrier_wait_start (gomp_barrier_t
   return ret;
 }
 
+static inline gomp_barrier_state_t
+gomp_barrier_wait_cancel_start (gomp_barrier_t *bar)
+{
+  return gomp_barrier_wait_start (bar);
+}
+
+/* This is like gomp_barrier_wait_start, except it decrements
+   bar->awaited_final rather than bar->awaited and should be used
+   for the gomp_team_end barrier only.  */
+static inline gomp_barrier_state_t
+gomp_barrier_wait_final_start (gomp_barrier_t *bar)
+{
+  unsigned int ret = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
+  ret &= -BAR_INCR | BAR_CANCELLED;
+  /* See above gomp_barrier_wait_start comment.  */
+  if (__atomic_add_fetch (&bar->awaited_final, -1, MEMMODEL_ACQ_REL) == 0)
+    ret |= BAR_WAS_LAST;
+  return ret;
+}
+
 static inline bool
 gomp_barrier_last_thread (gomp_barrier_state_t state)
 {
@@ -123,6 +152,12 @@ gomp_team_barrier_waiting_for_tasks (gom
   return (bar->generation & BAR_WAITING_FOR_TASK) != 0;
 }
 
+static inline bool
+gomp_team_barrier_cancelled (gomp_barrier_t *bar)
+{
+  return __builtin_expect ((bar->generation & BAR_CANCELLED) != 0, 0);
+}
+
 static inline void
 gomp_team_barrier_done (gomp_barrier_t *bar, gomp_barrier_state_t state)
 {
--- libgomp/work.c.jj	2013-09-19 18:59:27.534751449 +0200
+++ libgomp/work.c	2013-09-23 09:40:04.008959881 +0200
@@ -221,7 +221,10 @@ gomp_work_share_end (void)
   if (gomp_barrier_last_thread (bstate))
     {
       if (__builtin_expect (thr->ts.last_work_share != NULL, 1))
-	free_work_share (team, thr->ts.last_work_share);
+	{
+	  team->work_shares_to_free = thr->ts.work_share;
+	  free_work_share (team, thr->ts.last_work_share);
+	}
     }
 
   gomp_team_barrier_wait_end (&team->barrier, bstate);
@@ -229,6 +232,32 @@ gomp_work_share_end (void)
 }
 
 /* The current thread is done with its current work sharing construct.
+   This version implies a cancellable barrier at the end of the work-share.  */
+
+bool
+gomp_work_share_end_cancel (void)
+{
+  struct gomp_thread *thr = gomp_thread ();
+  struct gomp_team *team = thr->ts.team;
+  gomp_barrier_state_t bstate;
+
+  /* Cancellable work sharing constructs cannot be orphaned.  */
+  bstate = gomp_barrier_wait_cancel_start (&team->barrier);
+
+  if (gomp_barrier_last_thread (bstate))
+    {
+      if (__builtin_expect (thr->ts.last_work_share != NULL, 1))
+	{
+	  team->work_shares_to_free = thr->ts.work_share;
+	  free_work_share (team, thr->ts.last_work_share);
+	}
+    }
+  thr->ts.last_work_share = NULL;
+
+  return gomp_team_barrier_wait_cancel_end (&team->barrier, bstate);
+}
+
+/* The current thread is done with its current work sharing construct.
    This version does NOT imply a barrier at the end of the work-share.  */
 
 void
@@ -259,6 +288,9 @@ gomp_work_share_end_nowait (void)
 #endif
 
   if (completed == team->nthreads)
-    free_work_share (team, thr->ts.last_work_share);
+    {
+      team->work_shares_to_free = thr->ts.work_share;
+      free_work_share (team, thr->ts.last_work_share);
+    }
   thr->ts.last_work_share = NULL;
 }
--- libgomp/team.c.jj	2013-09-19 18:59:27.597751130 +0200
+++ libgomp/team.c	2013-09-23 09:48:17.140429333 +0200
@@ -98,7 +98,7 @@ gomp_thread_start (void *xdata)
       gomp_barrier_wait (&team->barrier);
 
       local_fn (local_data);
-      gomp_team_barrier_wait (&team->barrier);
+      gomp_team_barrier_wait_final (&team->barrier);
       gomp_finish_task (task);
       gomp_barrier_wait_last (&team->barrier);
     }
@@ -113,7 +113,7 @@ gomp_thread_start (void *xdata)
 	  struct gomp_task *task = thr->task;
 
 	  local_fn (local_data);
-	  gomp_team_barrier_wait (&team->barrier);
+	  gomp_team_barrier_wait_final (&team->barrier);
 	  gomp_finish_task (task);
 
 	  gomp_barrier_wait (&pool->threads_dock);
@@ -149,6 +149,7 @@ gomp_new_team (unsigned nthreads)
 #else
   gomp_mutex_init (&team->work_share_list_free_lock);
 #endif
+  team->work_shares_to_free = &team->work_shares[0];
   gomp_init_work_share (&team->work_shares[0], false, nthreads);
   team->work_shares[0].next_alloc = NULL;
   team->work_share_list_free = NULL;
@@ -168,6 +169,8 @@ gomp_new_team (unsigned nthreads)
   team->task_queue = NULL;
   team->task_count = 0;
   team->task_running_count = 0;
+  team->work_share_cancelled = 0;
+  team->team_cancelled = 0;
 
   return team;
 }
@@ -477,9 +480,26 @@ gomp_team_end (void)
   struct gomp_thread *thr = gomp_thread ();
   struct gomp_team *team = thr->ts.team;
 
-  /* This barrier handles all pending explicit threads.  */
-  gomp_team_barrier_wait (&team->barrier);
-  gomp_fini_work_share (thr->ts.work_share);
+  /* This barrier handles all pending explicit threads.
+     As #pragma omp cancel parallel might get awaited count in
+     team->barrier in a inconsistent state, we need to use a different
+     counter here.  */
+  gomp_team_barrier_wait_final (&team->barrier);
+  if (__builtin_expect (team->team_cancelled, 0))
+    {
+      struct gomp_work_share *ws = team->work_shares_to_free;
+      do
+	{
+	  struct gomp_work_share *next_ws = gomp_ptrlock_get (&ws->next_ws);
+	  if (next_ws == NULL)
+	    gomp_ptrlock_set (&ws->next_ws, ws);
+	  gomp_fini_work_share (ws);
+	  ws = next_ws;
+	}
+      while (ws != NULL);
+    }
+  else
+    gomp_fini_work_share (thr->ts.work_share);
 
   gomp_end_task ();
   thr->ts = team->prev_ts;
--- libgomp/env.c.jj	2013-09-19 18:59:27.607751080 +0200
+++ libgomp/env.c	2013-09-19 19:02:44.661757595 +0200
@@ -58,13 +58,15 @@ struct gomp_task_icv gomp_global_icv = {
   .run_sched_modifier = 1,
   .default_device_var = 0,
   .dyn_var = false,
-  .nest_var = false
+  .nest_var = false,
+  .target_data = NULL
 };
 
 unsigned short *gomp_cpu_affinity;
 size_t gomp_cpu_affinity_len;
 unsigned long gomp_max_active_levels_var = INT_MAX;
 unsigned long gomp_thread_limit_var = ULONG_MAX;
+bool gomp_cancel_var = false;
 unsigned long gomp_remaining_threads_count;
 #ifndef HAVE_SYNC_BUILTINS
 gomp_mutex_t gomp_remaining_threads_lock;
@@ -676,8 +678,9 @@ handle_omp_display_env (bool proc_bind,
 	   gomp_max_active_levels_var);
 
 /* FIXME: Unimplemented OpenMP 4.0 environment variables.
-  fprintf (stderr, "  OMP_PLACES = ''\n");
-  fprintf (stderr, "  OMP_CANCELLATION = ''\n"); */
+  fprintf (stderr, "  OMP_PLACES = ''\n"); */
+  fprintf (stderr, "  OMP_CANCELLATION = '%s'\n",
+	   gomp_cancel_var ? "TRUE" : "FALSE");
   fprintf (stderr, "  OMP_DEFAULT_DEVICE = '%d'\n",
 	   gomp_global_icv.default_device_var);
 
@@ -719,6 +722,7 @@ initialize_env (void)
   parse_boolean ("OMP_DYNAMIC", &gomp_global_icv.dyn_var);
   parse_boolean ("OMP_NESTED", &gomp_global_icv.nest_var);
   parse_boolean ("OMP_PROC_BIND", &bind_var);
+  parse_boolean ("OMP_CANCELLATION", &gomp_cancel_var);
   parse_int ("OMP_DEFAULT_DEVICE", &gomp_global_icv.default_device_var, true);
   parse_unsigned_long ("OMP_MAX_ACTIVE_LEVELS", &gomp_max_active_levels_var,
 		       true);
@@ -890,7 +894,7 @@ omp_get_max_active_levels (void)
 int
 omp_get_cancellation (void)
 {
-  return 0;
+  return gomp_cancel_var;
 }
 
 omp_proc_bind_t
--- libgomp/barrier.c.jj	2013-09-19 18:59:27.570751266 +0200
+++ libgomp/barrier.c	2013-09-19 19:02:44.661757595 +0200
@@ -39,3 +39,15 @@ GOMP_barrier (void)
 
   gomp_team_barrier_wait (&team->barrier);
 }
+
+bool
+GOMP_barrier_cancel (void)
+{
+  struct gomp_thread *thr = gomp_thread ();
+  struct gomp_team *team = thr->ts.team;
+
+  /* The compiler transforms to barrier_cancel when it sees that the
+     barrier is within a construct that can cancel.  Thus we should
+     never have an orphaned cancellable barrier.  */
+  return gomp_team_barrier_wait_cancel (&team->barrier);
+}
--- libgomp/libgomp.h.jj	2013-09-19 18:59:27.608751075 +0200
+++ libgomp/libgomp.h	2013-09-23 09:34:34.563734948 +0200
@@ -240,6 +240,7 @@ extern unsigned long gomp_remaining_thre
 extern gomp_mutex_t gomp_remaining_threads_lock;
 #endif
 extern unsigned long gomp_max_active_levels_var;
+extern bool gomp_cancel_var;
 extern unsigned long long gomp_spin_count_var, gomp_throttled_spin_count_var;
 extern unsigned long gomp_available_cpus, gomp_managed_threads;
 extern unsigned long *gomp_nthreads_var_list, gomp_nthreads_var_list_len;
@@ -298,6 +299,12 @@ struct gomp_team
      of the threads in the team.  */
   gomp_sem_t **ordered_release;
 
+  /* List of work shares on which gomp_fini_work_share hasn't been
+     called yet.  If the team hasn't been cancelled, this should be
+     equal to each thr->ts.work_share, but otherwise it can be a possibly
+     long list of workshares.  */
+  struct gomp_work_share *work_shares_to_free;
+
   /* List of gomp_work_share structs chained through next_free fields.
      This is populated and taken off only by the first thread in the
      team encountering a new work sharing construct, in a critical
@@ -331,6 +338,8 @@ struct gomp_team
   struct gomp_task *task_queue;
   int task_count;
   int task_running_count;
+  int work_share_cancelled;
+  int team_cancelled;
 
   /* This array contains structures for implicit tasks.  */
   struct gomp_task implicit_task[];
@@ -373,6 +382,16 @@ struct gomp_thread_pool
   gomp_barrier_t threads_dock;
 };
 
+enum gomp_cancel_kind
+{
+  GOMP_CANCEL_PARALLEL = 1,
+  GOMP_CANCEL_LOOP = 2,
+  GOMP_CANCEL_FOR = GOMP_CANCEL_LOOP,
+  GOMP_CANCEL_DO = GOMP_CANCEL_LOOP,
+  GOMP_CANCEL_SECTIONS = 4,
+  GOMP_CANCEL_TASKGROUP = 8
+};
+
 /* ... and here is that TLS data.  */
 
 #ifdef HAVE_TLS
@@ -511,6 +530,7 @@ extern void gomp_init_work_share (struct
 extern void gomp_fini_work_share (struct gomp_work_share *);
 extern bool gomp_work_share_start (bool);
 extern void gomp_work_share_end (void);
+extern bool gomp_work_share_end_cancel (void);
 extern void gomp_work_share_end_nowait (void);
 
 static inline void
--- libgomp/libgomp_g.h.jj	2013-09-19 18:59:27.580751217 +0200
+++ libgomp/libgomp_g.h	2013-09-19 19:02:44.661757595 +0200
@@ -33,6 +33,7 @@
 /* barrier.c */
 
 extern void GOMP_barrier (void);
+extern bool GOMP_barrier_cancel (void);
 
 /* critical.c */
 
@@ -91,6 +92,7 @@ extern void GOMP_parallel_loop_runtime (
 
 extern void GOMP_loop_end (void);
 extern void GOMP_loop_end_nowait (void);
+extern bool GOMP_loop_end_cancel (void);
 
 /* loop_ull.c */
 
@@ -170,8 +172,8 @@ extern void GOMP_ordered_end (void);
 extern void GOMP_parallel_start (void (*) (void *), void *, unsigned);
 extern void GOMP_parallel_end (void);
 extern void GOMP_parallel (void (*) (void *), void *, unsigned, unsigned);
-extern void GOMP_cancel (void);
-extern void GOMP_cancellation_point (void);
+extern bool GOMP_cancel (int, bool);
+extern bool GOMP_cancellation_point (int);
 
 /* task.c */
 
@@ -192,6 +194,7 @@ extern void GOMP_parallel_sections (void
 				    unsigned, unsigned, unsigned);
 extern void GOMP_sections_end (void);
 extern void GOMP_sections_end_nowait (void);
+extern bool GOMP_sections_end_cancel (void);
 
 /* single.c */
 
--- libgomp/task.c.jj	2013-09-19 18:59:27.616751035 +0200
+++ libgomp/task.c	2013-09-20 17:10:54.554520414 +0200
@@ -94,6 +94,10 @@ GOMP_task (void (*fn) (void *), void *da
     flags &= ~1;
 #endif
 
+  /* If parallel has been cancelled, don't start new tasks.  */
+  if (team && gomp_team_barrier_cancelled (&team->barrier))
+    return;
+
   if (!if_clause || team == NULL
       || (thr->task && thr->task->final_task)
       || team->task_count > 64 * team->nthreads)
@@ -158,6 +162,14 @@ GOMP_task (void (*fn) (void *), void *da
       task->in_tied_task = true;
       task->final_task = (flags & 2) >> 1;
       gomp_mutex_lock (&team->task_lock);
+      /* If parallel has been cancelled, don't start new tasks.  */
+      if (gomp_team_barrier_cancelled (&team->barrier))
+	{
+	  gomp_mutex_unlock (&team->task_lock);
+	  gomp_finish_task (task);
+	  free (task);
+	  return;
+	}
       if (parent->children)
 	{
 	  task->next_child = parent->children;
@@ -202,6 +214,7 @@ gomp_barrier_handle_tasks (gomp_barrier_
   struct gomp_task *task = thr->task;
   struct gomp_task *child_task = NULL;
   struct gomp_task *to_free = NULL;
+  bool cancelled = false;
 
   gomp_mutex_lock (&team->task_lock);
   if (gomp_barrier_last_thread (state))
@@ -233,6 +246,17 @@ gomp_barrier_handle_tasks (gomp_barrier_
 	  else
 	    team->task_queue = NULL;
 	  child_task->kind = GOMP_TASK_TIED;
+	  cancelled |= gomp_team_barrier_cancelled (&team->barrier);
+	  if (__builtin_expect (cancelled, 0))
+	    {
+	      if (to_free)
+		{
+		  gomp_finish_task (to_free);
+		  free (to_free);
+		  to_free = NULL;
+		}
+	      goto finish_cancelled;
+	    }
 	  team->task_running_count++;
 	  if (team->task_count == team->task_running_count)
 	    gomp_team_barrier_clear_task_pending (&team->barrier);
@@ -253,6 +277,7 @@ gomp_barrier_handle_tasks (gomp_barrier_
       else
 	return;
       gomp_mutex_lock (&team->task_lock);
+     finish_cancelled:
       if (child_task)
 	{
 	  struct gomp_task *parent = child_task->parent;
@@ -281,7 +306,8 @@ gomp_barrier_handle_tasks (gomp_barrier_
 	  gomp_clear_parent (child_task->children);
 	  to_free = child_task;
 	  child_task = NULL;
-	  team->task_running_count--;
+	  if (!cancelled)
+	    team->task_running_count--;
 	  if (--team->task_count == 0
 	      && gomp_team_barrier_waiting_for_tasks (&team->barrier))
 	    {
--- libgomp/sections.c.jj	2013-09-19 18:59:27.602751105 +0200
+++ libgomp/sections.c	2013-09-19 19:02:44.663757450 +0200
@@ -160,7 +160,7 @@ GOMP_parallel_sections (void (*fn) (void
 }
 
 /* The GOMP_section_end* routines are called after the thread is told
-   that all sections are complete.  This first version synchronizes
+   that all sections are complete.  The first two versions synchronize
    all threads; the nowait version does not.  */
 
 void
@@ -169,6 +169,12 @@ GOMP_sections_end (void)
   gomp_work_share_end ();
 }
 
+bool
+GOMP_sections_end_cancel (void)
+{
+  return gomp_work_share_end_cancel ();
+}
+
 void
 GOMP_sections_end_nowait (void)
 {
--- libgomp/loop.c.jj	2013-09-19 18:59:27.620751014 +0200
+++ libgomp/loop.c	2013-09-19 19:02:44.663757450 +0200
@@ -538,7 +538,7 @@ GOMP_parallel_loop_runtime (void (*fn) (
 }
 
 /* The GOMP_loop_end* routines are called after the thread is told that
-   all loop iterations are complete.  This first version synchronizes
+   all loop iterations are complete.  The first two versions synchronize
    all threads; the nowait version does not.  */
 
 void
@@ -547,6 +547,12 @@ GOMP_loop_end (void)
   gomp_work_share_end ();
 }
 
+bool
+GOMP_loop_end_cancel (void)
+{
+  return gomp_work_share_end_cancel ();
+}
+
 void
 GOMP_loop_end_nowait (void)
 {
--- libgomp/testsuite/libgomp.c/cancel-parallel-2.c.jj	2013-09-19 19:02:44.662757522 +0200
+++ libgomp/testsuite/libgomp.c/cancel-parallel-2.c	2013-09-20 17:34:11.914360059 +0200
@@ -0,0 +1,53 @@
+/* { dg-do run } */
+/* { dg-set-target-env-var OMP_CANCELLATION "true" } */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <omp.h>
+
+static void
+foo (int *x)
+{
+  #pragma omp parallel firstprivate(x) num_threads (32)
+  {
+    int thr = omp_get_thread_num ();
+    switch (x[thr])
+      {
+      case 4:
+	#pragma omp cancel parallel
+	break;
+      case 3:
+	#pragma omp task
+	usleep (1000);
+	#pragma omp task
+	usleep (2000);
+	#pragma omp task
+	usleep (4000);
+	break;
+      case 2:
+	usleep (1000);
+	/* FALLTHRU */
+      case 1:
+	#pragma omp cancellation point parallel
+	break;
+      }
+    #pragma omp barrier
+    if (omp_get_cancellation ())
+      abort ();
+  }
+}
+
+int
+main ()
+{
+  int i, j, x[32] = { 0, 1, 2, 4, 2, 2, 1, 0 };
+  foo (x);
+  for (i = 0; i < 32; i++)
+    {
+      for (j = 0; j < 32; j++)
+	x[j] = rand () & 3;
+      x[rand () & 31] = 4;
+      foo (x);
+    }
+  return 0;
+}
--- libgomp/testsuite/libgomp.c/cancel-parallel-1.c.jj	2013-09-19 19:02:44.662757522 +0200
+++ libgomp/testsuite/libgomp.c/cancel-parallel-1.c	2013-09-20 17:33:49.050466691 +0200
@@ -0,0 +1,17 @@
+/* { dg-do run } */
+/* { dg-set-target-env-var OMP_CANCELLATION "true" } */
+
+#include <stdlib.h>
+#include <omp.h>
+
+int
+main ()
+{
+  #pragma omp parallel num_threads (32)
+  {
+    #pragma omp cancel parallel
+    if (omp_get_cancellation ())
+      abort ();
+  }
+  return 0;
+}
--- libgomp/testsuite/libgomp.c/cancel-parallel-3.c.jj	2013-09-23 10:09:42.315062774 +0200
+++ libgomp/testsuite/libgomp.c/cancel-parallel-3.c	2013-09-23 10:10:42.587757472 +0200
@@ -0,0 +1,39 @@
+/* { dg-do run } */
+/* { dg-set-target-env-var OMP_CANCELLATION "true" } */
+
+#include <omp.h>
+#include <unistd.h>
+
+static inline
+do_some_work (void)
+{
+  asm volatile ("" : : : "memory");
+}
+
+int
+main ()
+{
+  omp_set_dynamic (0);
+  omp_set_schedule (omp_sched_static, 1);
+  #pragma omp parallel num_threads (16)
+  {
+    int i, j;
+    do_some_work ();
+    #pragma omp barrier
+    if (omp_get_thread_num () == 1)
+      {
+	sleep (2);
+	#pragma omp cancellation point parallel
+      }
+    for (j = 3; j <= 16; j++)
+      #pragma omp for schedule (runtime) nowait
+      for (i = 0; i < j; i++)
+	do_some_work ();
+    if (omp_get_thread_num () == 0)
+      {
+	sleep (1);
+	#pragma omp cancel parallel
+      }
+  }
+  return 0;
+}
--- libgomp/testsuite/libgomp.c/cancel-for-2.c.jj	2013-09-20 16:45:45.594139418 +0200
+++ libgomp/testsuite/libgomp.c/cancel-for-2.c	2013-09-20 17:33:24.300593675 +0200
@@ -0,0 +1,95 @@
+/* { dg-do run } */
+/* { dg-set-target-env-var OMP_CANCELLATION "true" } */
+
+#include <stdlib.h>
+#include <omp.h>
+
+__attribute__((noinline, noclone)) int
+foo (int *x)
+{
+  int v = 0, w = 0;
+  #pragma omp parallel num_threads (32) shared (v, w)
+  {
+    int i;
+    #pragma omp for
+    for (i = 0; i < 1000; ++i)
+      {
+	#pragma omp cancel for if (x[0])
+	abort ();
+      }
+    #pragma omp for
+    for (i = 0; i < 1000; ++i)
+      {
+	#pragma omp cancel for if (x[1])
+	#pragma omp atomic
+	v++;
+      }
+    #pragma omp for
+    for (i = 0; i < 1000; ++i)
+      {
+	#pragma omp cancel for if (x[2])
+	#pragma omp atomic
+	w += 8;
+      }
+    #pragma omp for
+    for (i = 0; i < 1000; ++i)
+      {
+	#pragma omp cancel for if (x[3])
+	#pragma omp atomic
+	v += 2;
+      }
+  }
+  if (v != 3000 || w != 0)
+    abort ();
+  #pragma omp parallel num_threads (32) shared (v, w)
+  {
+    int i;
+    /* None of these cancel directives should actually cancel anything,
+       but the compiler shouldn't know that and thus should use cancellable
+       barriers at the end of all the workshares.  */
+    #pragma omp cancel parallel if (omp_get_thread_num () == 1 && x[4])
+    #pragma omp for
+    for (i = 0; i < 1000; ++i)
+      {
+	#pragma omp cancel for if (x[0])
+	abort ();
+      }
+    #pragma omp cancel parallel if (omp_get_thread_num () == 2 && x[4])
+    #pragma omp for
+    for (i = 0; i < 1000; ++i)
+      {
+	#pragma omp cancel for if (x[1])
+	#pragma omp atomic
+	v++;
+      }
+    #pragma omp cancel parallel if (omp_get_thread_num () == 3 && x[4])
+    #pragma omp for
+    for (i = 0; i < 1000; ++i)
+      {
+	#pragma omp cancel for if (x[2])
+	#pragma omp atomic
+	w += 8;
+      }
+    #pragma omp cancel parallel if (omp_get_thread_num () == 4 && x[4])
+    #pragma omp for
+    for (i = 0; i < 1000; ++i)
+      {
+	#pragma omp cancel for if (x[3])
+	#pragma omp atomic
+	v += 2;
+      }
+    #pragma omp cancel parallel if (omp_get_thread_num () == 5 && x[4])
+  }
+  if (v != 6000 || w != 0)
+    abort ();
+  return 0;
+}
+
+int
+main ()
+{
+  int x[] = { 1, 0, 1, 0, 0 };
+  if (omp_get_cancellation ())
+    foo (x);
+  return 0;
+}
--- libgomp/testsuite/libgomp.c/cancel-sections-1.c.jj	2013-09-19 19:02:44.663757450 +0200
+++ libgomp/testsuite/libgomp.c/cancel-sections-1.c	2013-09-20 17:34:37.391220734 +0200
@@ -0,0 +1,38 @@
+/* { dg-do run } */
+/* { dg-set-target-env-var OMP_CANCELLATION "true" } */
+
+#include <stdlib.h>
+#include <omp.h>
+
+int
+main ()
+{
+  if (!omp_get_cancellation ())
+    return 0;
+  #pragma omp parallel num_threads (32)
+  {
+    #pragma omp sections
+      {
+	{
+	  #pragma omp cancel sections
+	  abort ();
+	}
+      #pragma omp section
+	{
+	  #pragma omp cancel sections
+	  abort ();
+	}
+      #pragma omp section
+	{
+	  #pragma omp cancel sections
+	  abort ();
+	}
+      #pragma omp section
+	{
+	  #pragma omp cancel sections
+	  abort ();
+	}
+      }
+  }
+  return 0;
+}
--- libgomp/testsuite/libgomp.c/cancel-for-1.c.jj	2013-09-19 19:02:44.663757450 +0200
+++ libgomp/testsuite/libgomp.c/cancel-for-1.c	2013-09-20 17:33:12.894649895 +0200
@@ -0,0 +1,22 @@
+/* { dg-do run } */
+/* { dg-set-target-env-var OMP_CANCELLATION "true" } */
+
+#include <stdlib.h>
+#include <omp.h>
+
+int
+main ()
+{
+  #pragma omp parallel num_threads (32)
+  {
+    int i;
+    #pragma omp for
+    for (i = 0; i < 1000; ++i)
+      {
+	#pragma omp cancel for
+	if (omp_get_cancellation ())
+	  abort ();
+      }
+  }
+  return 0;
+}

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]