// gold-threads.h -- thread support for gold  -*- C++ -*-

// Copyright (C) 2006-2024 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <[email protected]>.

// This file is part of gold.

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.

// This program 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 General Public License for more details.

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

// gold can be configured to support threads.  If threads are
// supported, the user can specify at runtime whether or not to
// support them.  This provides an interface to manage locking
// accordingly.

// Lock
//   A simple lock class.

#ifndef GOLD_THREADS_H
#define GOLD_THREADS_H

namespace gold
{

class Condvar;
class Once_initialize;
class Initialize_lock_once;

// The interface for the implementation of a Lock.

class Lock_impl
{
public:
 Lock_impl()
 { }

 virtual
 ~Lock_impl()
 { }

 virtual void
 acquire() = 0;

 virtual void
 release() = 0;
};

// A simple lock class.

class Lock
{
public:
 Lock();

 ~Lock();

 // Acquire the lock.
 void
 acquire()
 { this->lock_->acquire(); }

 // Release the lock.
 void
 release()
 { this->lock_->release(); }

private:
 // This class can not be copied.
 Lock(const Lock&);
 Lock& operator=(const Lock&);

 friend class Condvar;
 Lock_impl*
 get_impl() const
 { return this->lock_; }

 Lock_impl* lock_;
};

// RAII for Lock.

class Hold_lock
{
public:
 Hold_lock(Lock& lock)
   : lock_(lock)
 { this->lock_.acquire(); }

 ~Hold_lock()
 { this->lock_.release(); }

private:
 // This class can not be copied.
 Hold_lock(const Hold_lock&);
 Hold_lock& operator=(const Hold_lock&);

 Lock& lock_;
};

class Hold_optional_lock
{
public:
 Hold_optional_lock(Lock* lock)
   : lock_(lock)
 {
   if (this->lock_ != NULL)
     this->lock_->acquire();
 }

 ~Hold_optional_lock()
 {
   if (this->lock_ != NULL)
     this->lock_->release();
 }

private:
 Hold_optional_lock(const Hold_optional_lock&);
 Hold_optional_lock& operator=(const Hold_optional_lock&);

 Lock* lock_;
};

// The interface for the implementation of a condition variable.

class Condvar_impl
{
public:
 Condvar_impl()
 { }

 virtual
 ~Condvar_impl()
 { }

 virtual void
 wait(Lock_impl*) = 0;

 virtual void
 signal() = 0;

 virtual void
 broadcast() = 0;
};

// A simple condition variable class.  It is always associated with a
// specific lock.

class Condvar
{
public:
 Condvar(Lock& lock);
 ~Condvar();

 // Wait for the condition variable to be signalled.  This should
 // only be called when the lock is held.
 void
 wait()
 { this->condvar_->wait(this->lock_.get_impl()); }

 // Signal the condition variable--wake up at least one thread
 // waiting on the condition variable.  This should only be called
 // when the lock is held.
 void
 signal()
 { this->condvar_->signal(); }

 // Broadcast the condition variable--wake up all threads waiting on
 // the condition variable.  This should only be called when the lock
 // is held.
 void
 broadcast()
 { this->condvar_->broadcast(); }

private:
 // This class can not be copied.
 Condvar(const Condvar&);
 Condvar& operator=(const Condvar&);

 Lock& lock_;
 Condvar_impl* condvar_;
};

// A class used to do something once.  This is an abstract parent
// class; any actual use will involve a child of this.

class Once
{
public:
 Once();

 virtual
 ~Once()
 { }

 // Call this function to do whatever it is.  We pass an argument
 // even though you have to use a child class because in some uses
 // setting the argument would itself require a Once class.
 void
 run_once(void* arg);

 // This is an internal function, which must be public because it is
 // run by an extern "C" function called via pthread_once.
 void
 internal_run(void* arg);

protected:
 // This must be implemented by the child class.
 virtual void
 do_run_once(void* arg) = 0;

private:
 // True if we have already run the function.
 bool was_run_;
#if defined(ENABLE_THREADS) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
 // Internal compare-and-swap lock on was_run_;
 uint32_t was_run_lock_;
#endif
 // The lock to run the function only once.
 Once_initialize* once_;
};

// A class used to initialize a lock exactly once, after the options
// have been read.  This is needed because the implementation of locks
// depends on whether we've seen the --threads option.  Before the
// options have been read, we know we are single-threaded, so we can
// get by without using a lock.  This class should be an instance
// variable of the class which has a lock which needs to be
// initialized.

class Initialize_lock : public Once
{
public:
 // The class which uses this will have a pointer to a lock.  This
 // must be constructed with a pointer to that pointer.
 Initialize_lock(Lock** pplock)
   : pplock_(pplock)
 { }

 // Initialize the lock.  Return true if the lock is now initialized,
 // false if it is not (because the options have not yet been read).
 bool
 initialize();

protected:
 void
 do_run_once(void*);

private:
 // A pointer to the lock pointer which must be initialized.
 Lock** const pplock_;
};

} // End namespace gold.

#endif // !defined(GOLD_THREADS_H)