// ----------------------------------------------------------------------------
//
//  Copyright (C) 2008-2023 Fons Adriaensen <fons@linuxaudio.org>
//    
//  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, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------


#ifndef _POSIXTHR_H
#define _POSIXTHR_H


#include <stdlib.h>
#include <pthread.h>


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


#ifdef P_SEMA_IS_IMPLEMENTED
#undef P_SEMA_IS_IMPLEMENTED
#endif


#if defined(__linux__)  || defined(__GNU__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)

#include <semaphore.h>

class P_sema
{
public:

    P_sema (void) { init (0, 0); }
    ~P_sema (void) { sem_destroy (&_sema); }

    P_sema (const P_sema&); // disabled
    P_sema& operator= (const P_sema&); // disabled

    int init (int s, int v) { return sem_init (&_sema, s, v); }
    int post (void) { return sem_post (&_sema); }
    int wait (void) { return sem_wait (&_sema); }
    int trywait (void) { return sem_trywait (&_sema); }
    int getvalue (void) { int n; sem_getvalue (&_sema, &n); return n; }

private:

    sem_t  _sema;
};

#define P_SEMA_IS_IMPLEMENTED
#endif


#ifdef __APPLE__

// NOTE:  ***** I DO NOT REPEAT NOT PROVIDE SUPPORT FOR OSX *****
// 
// The following code partially emulates the POSIX sem_t for which
// OSX has only a crippled implementation. It may or may not compile,
// and if it compiles it may or may not work correctly. Blame APPLE
// for not following POSIX standards.

class P_sema
{
public:

    P_sema (void) : _count (0)
    {
        init (0, 0);
    }

    ~P_sema (void)
    {
        pthread_mutex_destroy (&_mutex);
        pthread_cond_destroy (&_cond);
    }

    P_sema (const P_sema&); // disabled
    P_sema& operator= (const P_sema&); // disabled

    int init (int s, int v)
    {
	_count = v;
        return pthread_mutex_init (&_mutex, 0) || pthread_cond_init (&_cond, 0);
    }

    int post (void)
    {
	pthread_mutex_lock (&_mutex);
	_count++;
	if (_count == 1) pthread_cond_signal (&_cond);
	pthread_mutex_unlock (&_mutex);
	return 0;
    }

    int wait (void)
    {
	pthread_mutex_lock (&_mutex);
	while (_count < 1) pthread_cond_wait (&_cond, &_mutex);
	_count--;
	pthread_mutex_unlock (&_mutex);
	return 0;
    }

    int trywait (void)
    {
	if (pthread_mutex_trylock (&_mutex)) return -1;
	if (_count < 1)
	{
	    pthread_mutex_unlock (&_mutex);
	    return -1;
	}
        _count--;
        pthread_mutex_unlock (&_mutex);
        return 0;
    }

    int getvalue (void) { return _count; }
    
private:

    int              _count;
    pthread_mutex_t  _mutex;
    pthread_cond_t   _cond;
};

#define P_SEMA_IS_IMPLEMENTED
#endif


#ifndef P_SEMA_IS_IMPLEMENTED
#error "The P_sema class is not implemented."
#endif


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


class P_mutex
{
public:

    P_mutex (void) { if (pthread_mutex_init (&_mutex, 0)) abort (); }
    ~P_mutex (void) { pthread_mutex_destroy (&_mutex); }
    P_mutex (const P_mutex&);
    P_mutex& operator= (const P_mutex&);

    int lock (void) { return pthread_mutex_lock (&_mutex); }
    int unlock (void){ return pthread_mutex_unlock (&_mutex); }
    int trylock (void) { return pthread_mutex_trylock (&_mutex); }

private:

    pthread_mutex_t  _mutex;
};


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


class P_thread
{
public:

    P_thread (void);
    virtual ~P_thread (void);
    P_thread (const P_thread&);
    P_thread& operator=(const P_thread&);

    void sepuku (void) { pthread_cancel (_ident); }

    virtual void thr_main (void) = 0;
    virtual int  thr_start (int policy, int priority, size_t stacksize = 0);

private:
  
    pthread_t  _ident;
};


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


#endif //  _POSIXTHR_H

