Files
libreoffice/cppu/source/threadpool/jobqueue.cxx
Stephan Bergmann a0bc388b38 Use the saner std::condition_variable semantics for JobQueue::m_cndWait
This avoids the need for the tricky osl::Condition::reset calls.  For example,
the first one (in the "disposed !" case) had been added with
2a3ed89284 "sb132: #i112448# proper clean up in
JobQueue::enter (patch by olistraub)" as

>                  if( 0 == m_lstCallstack.front() )
>                  {
>                      // disposed !
> +                    if( m_lstJob.empty() )
> +                    {
> +                        osl_resetCondition( m_cndWait );
> +                    }
>                      break;
>                  }
>

and then change to

>                  if( 0 == m_lstCallstack.front() )
>                  {
>                      // disposed !
> -                    if( m_lstJob.empty() )
> +                    if( m_lstJob.empty()
> +                        && (m_lstCallstack.empty()
> +                            || m_lstCallstack.front() != 0) )
>                      {
>                          osl_resetCondition( m_cndWait );
>                      }

with cba3ac1eab "Avoid deadlocks when disposing
recursive JobQueue::enter", which prevented the reset from ever hapening
(because m_lstCallstack.front() cannot both be zero and non-zero here at the
same time).  The std::condition_variable semantics nicely avoid any reasoning
whether or not a reset would be necessary here.

Change-Id: Ic9b57b836bb6679829f4aa3b68679256726acf60
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96406
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-06-16 10:02:24 +02:00

178 lines
4.9 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <cassert>
#include "jobqueue.hxx"
#include "threadpool.hxx"
namespace cppu_threadpool {
JobQueue::JobQueue() :
m_nToDo( 0 ),
m_bSuspended( false ),
m_DisposedCallerAdmin( DisposedCallerAdmin::getInstance() )
{
}
void JobQueue::add( void *pThreadSpecificData, RequestFun * doRequest )
{
std::scoped_lock guard( m_mutex );
Job job = { pThreadSpecificData , doRequest };
m_lstJob.push_back( job );
if( ! m_bSuspended )
{
m_cndWait.notify_all();
}
m_nToDo ++;
}
void *JobQueue::enter( void const * nDisposeId , bool bReturnWhenNoJob )
{
void *pReturn = nullptr;
{
// synchronize with the dispose calls
std::scoped_lock guard( m_mutex );
if( m_DisposedCallerAdmin->isDisposed( nDisposeId ) )
{
return nullptr;
}
m_lstCallstack.push_front( nDisposeId );
}
while( true )
{
struct Job job={nullptr,nullptr};
{
std::unique_lock guard( m_mutex );
while (m_bSuspended
|| (m_lstCallstack.front() != nullptr && !bReturnWhenNoJob
&& m_lstJob.empty()))
{
m_cndWait.wait(guard);
}
if( nullptr == m_lstCallstack.front() )
{
// disposed !
if (!m_lstJob.empty() && m_lstJob.front().doRequest == nullptr) {
// If this thread was waiting for a remote response, that response may or
// may not have been enqueued; if it has not been enqueued, there cannot be
// another enqueued response, so it is always correct to remove any enqueued
// response here:
m_lstJob.pop_front();
}
break;
}
if( m_lstJob.empty() )
{
assert(bReturnWhenNoJob);
break;
}
job = m_lstJob.front();
m_lstJob.pop_front();
}
if( job.doRequest )
{
job.doRequest( job.pThreadSpecificData );
std::scoped_lock guard( m_mutex );
m_nToDo --;
}
else
{
pReturn = job.pThreadSpecificData;
std::scoped_lock guard( m_mutex );
m_nToDo --;
break;
}
}
{
// synchronize with the dispose calls
std::scoped_lock guard( m_mutex );
m_lstCallstack.pop_front();
}
return pReturn;
}
void JobQueue::dispose( void const * nDisposeId )
{
std::scoped_lock guard( m_mutex );
for( auto& rId : m_lstCallstack )
{
if( rId == nDisposeId )
{
rId = nullptr;
}
}
if( !m_lstCallstack.empty() && ! m_lstCallstack.front() )
{
// The thread is waiting for a disposed pCallerId, let it go
m_cndWait.notify_all();
}
}
void JobQueue::suspend()
{
std::scoped_lock guard( m_mutex );
m_bSuspended = true;
}
void JobQueue::resume()
{
std::scoped_lock guard( m_mutex );
m_bSuspended = false;
if( ! m_lstJob.empty() )
{
m_cndWait.notify_all();
}
}
bool JobQueue::isEmpty() const
{
std::scoped_lock guard( m_mutex );
return m_lstJob.empty();
}
bool JobQueue::isCallstackEmpty() const
{
std::scoped_lock guard( m_mutex );
return m_lstCallstack.empty();
}
bool JobQueue::isBusy() const
{
std::scoped_lock guard( m_mutex );
return m_nToDo > 0;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */