/*
 * (C) Copyright 1996- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#ifndef eckit_Once_h
#define eckit_Once_h

#include <pthread.h>

#include "eckit/memory/NonCopyable.h"


namespace eckit {

//----------------------------------------------------------------------------------------------------------------------

template <class T>
class Once : private NonCopyable {
public:
    // -- Contructors

    Once();

    // -- Destructor

    ~Once();

    // -- Class methods

    operator T&();

private:
    // -- Members
    T* value_;

    // -- Class members

    static pthread_once_t once_;
    static pthread_mutex_t mutex_;

    // -- Class methods

    static void init(void);
};


//----------------------------------------------------------------------------------------------------------------------

template <class T>
pthread_once_t Once<T>::once_ = PTHREAD_ONCE_INIT;
template <class T>
pthread_mutex_t Once<T>::mutex_;

template <class T>
Once<T>::Once() :
    value_(0) {}

template <class T>
Once<T>::~Once() {}

template <class T>
Once<T>::operator T&() {
    ::pthread_once(&once_, init);

    ::pthread_mutex_lock(&mutex_);

    if (!value_) {
        value_ = new T();
    }

    ::pthread_mutex_unlock(&mutex_);

    return *value_;
}


template <class T>
void Once<T>::init() {
    pthread_mutexattr_t attr;

    ::pthread_mutexattr_init(&attr);

    //::pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);

    pthread_mutex_init(&mutex_, &attr);
}

//----------------------------------------------------------------------------------------------------------------------

}  // namespace eckit

#endif
