// thread.c
//
/****************************************************************************
   liblscp - LinuxSampler Control Protocol API
   Copyright (C) 2004-2021, rncbc aka Rui Nuno Capela. All rights reserved.

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

   This library 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
   Lesser 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.

*****************************************************************************/

#include "lscp/thread.h"


//-------------------------------------------------------------------------
// Threads.

struct _lscp_thread_t {
#if defined(WIN32)
	HANDLE              hThread;
	DWORD               dwThreadID;
#else
	pthread_t           pthread;
#endif
	lscp_thread_proc_t  pfnProc;
	void               *pvData;
	int                 iDetach;
};


#if defined(WIN32)
static DWORD WINAPI _lscp_thread_start ( LPVOID pvThread )
#else
static void *_lscp_thread_start ( void *pvThread )
#endif
{
	lscp_thread_t *pThread = (lscp_thread_t *) pvThread;
	if (pThread) {
	//  fprintf(stderr, "_lscp_thread_start: pThread=%p started.\n", pThread);
		pThread->pfnProc(pThread->pvData);
	//  fprintf(stderr, "_lscp_thread_start: pThread=%p terminated.\n", pThread);
		if (pThread->iDetach)
			free(pThread);
	}
#if defined(WIN32)
	return 0;
#else
	return NULL;
#endif
}

lscp_thread_t *lscp_thread_create ( lscp_thread_proc_t pfnProc, void *pvData, int iDetach )
{
	lscp_thread_t *pThread;
#if !defined(WIN32)
	pthread_attr_t attr;
#endif

	if (pfnProc == NULL) {
		fprintf(stderr, "lcsp_thread_create: Invalid thread function.\n");
		return NULL;
	}

	pThread = (lscp_thread_t *) malloc(sizeof(lscp_thread_t));
	if (pThread == NULL) {
		fprintf(stderr, "lcsp_thread_create: Out of memory.\n");
		return NULL;
	}
	memset(pThread, 0, sizeof(lscp_thread_t));

	pThread->pvData  = pvData;
	pThread->pfnProc = pfnProc;
	pThread->iDetach = iDetach;

//  fprintf(stderr, "lscp_thread_create: pThread=%p.\n", pThread);

#if defined(WIN32)
	pThread->hThread = CreateThread(NULL, 0, _lscp_thread_start, (LPVOID) pThread, 0, &(pThread->dwThreadID));
	if (pThread->hThread == NULL) {
		fprintf(stderr, "lcsp_thread_create: Failed to create thread.\n");
		free(pThread);
		return NULL;
	}
#else
	pthread_attr_init(&attr);
	if (iDetach)
		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
	if (pthread_create(&pThread->pthread, &attr, _lscp_thread_start, pThread)) {
		fprintf(stderr, "lcsp_thread_create: Failed to create thread.\n");
		free(pThread);
		return NULL;
	}
#endif

return pThread;
}


lscp_status_t lscp_thread_join( lscp_thread_t *pThread )
{
	lscp_status_t ret = LSCP_FAILED;

	if (pThread == NULL)
		return ret;

//  fprintf(stderr, "lscp_thread_join: pThread=%p.\n", pThread);

#if defined(WIN32)
	if (pThread->hThread && WaitForSingleObject(pThread->hThread, INFINITE) == WAIT_OBJECT_0) {
		pThread->hThread = NULL;
		ret = LSCP_OK;
	}
#else
	if (pThread->pthread && pthread_join(pThread->pthread, NULL) == 0) {
		pThread->pthread = 0;
		ret = LSCP_OK;
	}
#endif

	return ret;
}


lscp_status_t lscp_thread_cancel ( lscp_thread_t *pThread )
{
	lscp_status_t ret = LSCP_FAILED;

	if (pThread == NULL)
		return ret;

//  fprintf(stderr, "lscp_thread_cancel: pThread=%p.\n", pThread);

#if defined(WIN32)
	if (pThread->hThread) { // Should we TerminateThread(pThread->hThread, 0) ?
	/*  pThread->hThread = NULL; */
		ret = LSCP_OK;
	}
#else
	if (pThread->pthread && pthread_cancel(pThread->pthread) == 0) {
		pThread->pthread = 0;
		ret = LSCP_OK;
	}
#endif

	return ret;
}


lscp_status_t lscp_thread_destroy ( lscp_thread_t *pThread )
{
	lscp_status_t ret = lscp_thread_cancel(pThread);

	if (ret == LSCP_OK)
		ret = lscp_thread_join(pThread);
		
//  fprintf(stderr, "lscp_thread_destroy: pThread=%p.\n", pThread);

	if (ret == LSCP_OK)
		free(pThread);

	return ret;
}


// end of thread.c
