//
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) Contributors to the OpenEXR Project.
//

#ifndef INCLUDED_ILM_THREAD_SEMAPHORE_H
#define INCLUDED_ILM_THREAD_SEMAPHORE_H

//-----------------------------------------------------------------------------
//
//	class Semaphore -- a wrapper class for
//	system-dependent counting semaphores
//
//-----------------------------------------------------------------------------

#include "IlmThreadExport.h"

#include "IlmThreadConfig.h"
#include "IlmThreadNamespace.h"

#if defined(__APPLE__)
#   include <AvailabilityMacros.h>
#endif

#if ILMTHREAD_THREADING_ENABLED
#    if ILMTHREAD_HAVE_POSIX_SEMAPHORES
#        include <semaphore.h>
#    elif defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED > 1050 && !defined(__ppc__)
#        include <dispatch/dispatch.h>
#    elif (defined(_WIN32) || defined(_WIN64))
#        ifdef NOMINMAX
#            undef NOMINMAX
#        endif
#        define NOMINMAX
#        include <windows.h>
#    else
#        include <condition_variable>
#        include <mutex>
#    endif
#endif

ILMTHREAD_INTERNAL_NAMESPACE_HEADER_ENTER


class ILMTHREAD_EXPORT_TYPE Semaphore
{
  public:

    ILMTHREAD_EXPORT Semaphore (unsigned int value = 0);
    ILMTHREAD_EXPORT virtual ~Semaphore();

    ILMTHREAD_EXPORT void wait();
    ILMTHREAD_EXPORT bool tryWait();
    ILMTHREAD_EXPORT void post();
    ILMTHREAD_EXPORT int  value() const;

  private:

#if ILMTHREAD_HAVE_POSIX_SEMAPHORES

	mutable sem_t _semaphore;

#elif defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED > 1050 && !defined(__ppc__)
    mutable dispatch_semaphore_t _semaphore;

#elif (defined (_WIN32) || defined (_WIN64))

	mutable HANDLE _semaphore;

#elif ILMTHREAD_THREADING_ENABLED
    //
    // If the platform has threads but no semaphores,
    // then we implement them ourselves using condition variables
    //

	struct sema_t
	{
	    unsigned int count;
	    unsigned long numWaiting;
        std::mutex mutex;
        std::condition_variable nonZero;
	};

	mutable sema_t _semaphore;
  
#endif

    void operator = (const Semaphore& s) = delete;
    Semaphore (const Semaphore& s) = delete;
    void operator = (Semaphore&& s) = delete;
    Semaphore (Semaphore&& s) = delete;
};


ILMTHREAD_INTERNAL_NAMESPACE_HEADER_EXIT

#endif // INCLUDED_ILM_THREAD_SEMAPHORE_H
