This is the mail archive of the gcc@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]

Re: Re: [GSoC'19, libgomp work-stealing] Task parallelism runtime


On Sat, Aug 03, 2019 at 06:11:58PM +0900, 김규래 wrote:
> I'm currently having trouble implementing the thread sleeping mechanism when the queue is out of tasks.
> Problem is, it's hard to maintain consistency between the thread sleeping routine and the queues.
> See the pseudocode below,
>  
> 1. check queue is empty
> 2. go to sleep
>  
> if we go lock-free, the consistency between 1 and 2 cannot be maintained.

I thought we don't want to go lock-free, the queue operations aren't easily
implementable lock-free, but instead with a lock for each of the queues,
so in the multi-queue setting having locks on the implicit tasks that hold
those queues.  What can and should be done without lock is perhaps some
preliminary check if a queue is empty, that can be done through
__atomic_load.
And, generally go to sleep is done outside of the critical section, inside
of the critical section we decide if we go to sleep or not, and then
go to sleep either (on Linux) using futexes, or otherwise using semaphores,
both have the properties that one can already post to them before some other
thread sleeps on it, and in that case the other thread doesn't actually go
to sleep.  The wake up (post on the semaphore or updating the memory + later
futex wake) is sometimes done inside of a critical section, the updating of
memory if it is not atomic increase/decrease and the latter depending on
whether we remember from the atomic operation whether the wake up is needed
or not and defer it until after the critical section.

Given say:
      ++team->task_count;
      ++team->task_queued_count;
      gomp_team_barrier_set_task_pending (&team->barrier);
      do_wake = team->task_running_count + !parent->in_tied_task
                < team->nthreads;
      gomp_mutex_unlock (&team->task_lock);
      if (do_wake)
        gomp_team_barrier_wake (&team->barrier, 1);
you can see the wake up is done outside of the critical section.
If team->task_lock isn't used, there will be of course problems, say
team->task_count and team->task_queued_count need to be bumped atomically,
ditto operations on team->barrier, and the question is what to do with the
team->task_running_count check, if that one is updated atomically too,
maybe __atomic_load might be good enough, though perhaps worst case it might
mean we don't in some cases wake anybody, so there will be threads idling
instead of doing useful work, but at least one thread probably should handle
it later.

	Jakub


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