gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
}
}
- while (gen != state + BAR_INCR);
+ while (!gomp_barrier_state_is_incremented (gen, state));
}
void
gen = __atomic_load_n (&bar->generation, MEMMODEL_RELAXED);
}
}
- while (gen != state + BAR_INCR);
+ while (!gomp_barrier_state_is_incremented (gen, state));
return false;
}
bar->generation = (state & -BAR_INCR) + BAR_INCR;
}
+static inline bool
+gomp_barrier_state_is_incremented (gomp_barrier_state_t gen,
+ gomp_barrier_state_t state)
+{
+ unsigned next_state = (state & -BAR_INCR) + BAR_INCR;
+ return next_state > state ? gen >= next_state : gen < state;
+}
+
+static inline bool
+gomp_barrier_has_completed (gomp_barrier_state_t state, gomp_barrier_t *bar)
+{
+ /* Handling overflow in the generation. The "next" state could be less than
+ or greater than the current one. */
+ return gomp_barrier_state_is_incremented (bar->generation, state);
+}
+
#endif /* GOMP_BARRIER_H */
}
generation |= gen & BAR_WAITING_FOR_TASK;
}
- while (gen != state + BAR_INCR);
+ while (!gomp_barrier_state_is_incremented (gen, state));
}
void
}
generation |= gen & BAR_WAITING_FOR_TASK;
}
- while (gen != state + BAR_INCR);
+ while (!gomp_barrier_state_is_incremented (gen, state));
return false;
}
bar->generation = (state & -BAR_INCR) + BAR_INCR;
}
+static inline bool
+gomp_barrier_state_is_incremented (gomp_barrier_state_t gen,
+ gomp_barrier_state_t state)
+{
+ unsigned next_state = (state & -BAR_INCR) + BAR_INCR;
+ return next_state > state ? gen >= next_state : gen < state;
+}
+
+static inline bool
+gomp_barrier_has_completed (gomp_barrier_state_t state, gomp_barrier_t *bar)
+{
+ /* Handling overflow in the generation. The "next" state could be less than
+ or greater than the current one. */
+ return gomp_barrier_state_is_incremented (bar->generation, state);
+}
+
#endif /* GOMP_BARRIER_H */
bar->generation = (state & -BAR_INCR) + BAR_INCR;
}
+static inline bool
+gomp_barrier_state_is_incremented (gomp_barrier_state_t gen,
+ gomp_barrier_state_t state)
+{
+ unsigned next_state = (state & -BAR_INCR) + BAR_INCR;
+ return next_state > state ? gen >= next_state : gen < state;
+}
+
+static inline bool
+gomp_barrier_has_completed (gomp_barrier_state_t state, gomp_barrier_t *bar)
+{
+ /* Handling overflow in the generation. The "next" state could be less than
+ or greater than the current one. */
+ return gomp_barrier_state_is_incremented (bar->generation, state);
+}
+
#endif /* GOMP_BARRIER_H */
gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
}
}
- while (gen != state + BAR_INCR);
+ while (!gomp_barrier_state_is_incremented (gen, state));
#ifdef HAVE_SYNC_BUILTINS
n = __sync_add_and_fetch (&bar->arrived, -1);
break;
}
}
- while (gen != state + BAR_INCR);
+ while (!gomp_barrier_state_is_incremented (gen, state));
#ifdef HAVE_SYNC_BUILTINS
n = __sync_add_and_fetch (&bar->arrived, -1);
bar->generation = (state & -BAR_INCR) + BAR_INCR;
}
+static inline bool
+gomp_barrier_state_is_incremented (gomp_barrier_state_t gen,
+ gomp_barrier_state_t state)
+{
+ unsigned next_state = (state & -BAR_INCR) + BAR_INCR;
+ return next_state > state ? gen >= next_state : gen < state;
+}
+
+static inline bool
+gomp_barrier_has_completed (gomp_barrier_state_t state, gomp_barrier_t *bar)
+{
+ /* Handling overflow in the generation. The "next" state could be less than
+ or greater than the current one. */
+ return gomp_barrier_state_is_incremented (bar->generation, state);
+}
+
#endif /* GOMP_BARRIER_H */
bar->generation = (state & -BAR_INCR) + BAR_INCR;
}
+static inline bool
+gomp_barrier_state_is_incremented (gomp_barrier_state_t gen,
+ gomp_barrier_state_t state)
+{
+ unsigned next_state = (state & -BAR_INCR) + BAR_INCR;
+ return next_state > state ? gen >= next_state : gen < state;
+}
+
+static inline bool
+gomp_barrier_has_completed (gomp_barrier_state_t state, gomp_barrier_t *bar)
+{
+ /* Handling overflow in the generation. The "next" state could be less than
+ or greater than the current one. */
+ return gomp_barrier_state_is_incremented (bar->generation, state);
+}
+
#endif /* GOMP_BARRIER_H */
int do_wake = 0;
gomp_mutex_lock (&team->task_lock);
+ /* Avoid running tasks from next task scheduling region (PR122314).
+ N.b. we check that `team->task_count != 0` in order to avoid the
+ non-atomic read of `bar->generation` "conflicting" (in the C standard
+ sense) with the atomic write of `bar->generation` in
+ `gomp_team_barrier_wait_end`. That conflict would otherwise be a
+ data-race and hence UB. One alternate approach could have been to
+ atomically load `bar->generation` in `gomp_barrier_has_completed`.
+
+ When `task_count == 0` we're not going to perform tasks anyway, so the
+ problem of PR122314 is naturally avoided. */
+ if (team->task_count != 0
+ && gomp_barrier_has_completed (state, &team->barrier))
+ {
+ gomp_mutex_unlock (&team->task_lock);
+ return;
+ }
+
if (gomp_barrier_last_thread (state))
{
if (team->task_count == 0)
--- /dev/null
+#include <omp.h>
+
+void abort ();
+
+#define NUM_THREADS 8
+unsigned full_data[NUM_THREADS] = {0};
+#pragma omp declare target enter(full_data)
+
+void
+test ()
+{
+#pragma omp parallel num_threads(8)
+ {
+#pragma omp barrier
+ /* Initialise so that if tasks are performed on the previous barrier their
+ updates get overridden. This is a key behaviour of this test. */
+ full_data[omp_get_thread_num ()] = 0;
+#pragma omp for
+ for (int i = 0; i < 10; i++)
+#pragma omp task
+ {
+ full_data[omp_get_thread_num ()] += 1;
+ }
+ }
+
+ unsigned total = 0;
+ for (int i = 0; i < NUM_THREADS; i++)
+ total += full_data[i];
+
+ if (total != 10)
+ abort ();
+}
+#pragma omp declare target enter(test)
+
+int
+main ()
+{
+ test ();
+
+#pragma omp target
+ test ();
+}