jsemaphore.cpp 5.86 KB
/***************************************************************************
 *   Copyright (C) 2005 by Jeff Ferr                                       *
 *   root@sat                                                              *
 *                                                                         *
 *   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 2 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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "Stdafx.h"
#include "jsemaphore.h"
#include "jsemaphoreexception.h"
#include "jsemaphoretimeoutexception.h"

namespace jthread {

Semaphore::Semaphore(int value_):
	jcommon::Object()
{
	jcommon::Object::SetClassName("jthread::Semaphore");
	
#ifdef _WIN32
	BOOL inherit = TRUE;

	_sa = NULL;
	_semaphore = NULL;

	_sa = (LPSECURITY_ATTRIBUTES)HeapAlloc(GetProcessHeap(), 0, sizeof(SECURITY_ATTRIBUTES));
	
	_sa->nLength = sizeof(SECURITY_ATTRIBUTES);
	_sa->lpSecurityDescriptor = NULL;
	_sa->bInheritHandle = TRUE;

	if (value_ < 0) {
		value_ = 0;
	}

	// if ((_semaphore = CreateSemaphore(_sa, _count, _max_count, "NTcourse.semaphore.empty")) == NULL) {
	if ((_semaphore = CreateSemaphore(_sa, value_, 65535, NULL)) == NULL) {
		// if ((_semaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, inherit, "NTcourse.semaphore.empty")) == NULL) {
		DWORD code = GetLastError();
		char *msg = NULL;

		FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER |
				FORMAT_MESSAGE_FROM_SYSTEM,
				0,			        	// no source buffer needed
				code,					// error code for this message
				0,						// default language ID
				(LPTSTR)msg,			// allocated by fcn
				0,						// minimum size of buffer
				(va_list *)NULL);		// no inserts

		throw SemaphoreException(msg);
		// }
	}
#else
	if (sem_init(&_semaphore, 0, value_) < 0) {
		if (errno == EINVAL) {
			throw SemaphoreException("Operation would increase the semaphore count !");
		} else {
			throw SemaphoreException("Unknown semaphore error !");
		}
	}
#endif
}

Semaphore::~Semaphore()
{
	Release();
}

void Semaphore::Wait()
{
#ifdef _WIN32
	switch (WaitForSingleObject(_semaphore, INFINITE)) {
		case WAIT_OBJECT_0:
			break;
		default:
			throw SemaphoreException("Waiting semaphore failed"); 
	}
#else
	if (sem_wait(&_semaphore) != 0) {
		throw SemaphoreException("Semaphore waiting failed");
	}
#endif
}

void Semaphore::Wait(uint64_t time_)
{
#ifdef _WIN32
	time_ /= 100000L;
	
	switch (WaitForSingleObject(_semaphore, (unsigned int)time_)) {
		case WAIT_OBJECT_0:
			break;
		case WAIT_TIMEOUT:
			throw SemaphoreTimeoutException("Waiting time out exception");
		default:
			throw SemaphoreException("Waiting semaphore failed"); 
	}
#else
	struct timespec t;
	int result = 0;

	clock_gettime(CLOCK_REALTIME, &t);

	t.tv_sec += (int64_t)(time_/1000000LL);
	t.tv_nsec += (int64_t)((time_%1000000LL)*1000LL);

	while ((result = sem_timedwait(&_semaphore, &t)) == -1 && errno == EINTR) {
		continue; 
	}

	if (result == -1) {
		if (errno == ETIMEDOUT) {
			throw SemaphoreTimeoutException("Semaphore wait timeout");
		} else {
			throw SemaphoreException("Semaphore wait failed");
		}
	}
#endif
}

void Semaphore::Notify()
{
	AutoLock lock(&mutex);

#ifdef _WIN32
		if (ReleaseSemaphore(_semaphore, 1, NULL) == 0) {
			throw SemaphoreException("Notify semaphore failed");
		}
#else
	if (sem_post(&_semaphore) < 0) {
		if (errno == ERANGE) {
			throw SemaphoreException("Operation would increase the semaphore count !");
		} else {
			throw SemaphoreException("Notify semaphore failed");
		}
	}
#endif
}

void Semaphore::NotifyAll()
{
	AutoLock lock(&mutex);

#ifdef _WIN32
	LONG value;

	ReleaseSemaphore(_semaphore, 0, &value);

	if (value > 0) {
		ReleaseSemaphore(_semaphore, value, NULL);
	}
#else
	int r;
    
	sem_getvalue(&_semaphore, &r);

	while (r-- > 0) {
		sem_post(&_semaphore);
	}
#endif
}

bool Semaphore::TryWait()
{
	AutoLock lock(&mutex);

#ifdef _WIN32
	switch (WaitForSingleObject(_semaphore, 0L)) {
		case WAIT_OBJECT_0:
			return true;
		default:
			return false; 
	}

	return true;
#else
	if (sem_trywait(&_semaphore) < 0) {
		if (errno == EAGAIN) {
			return false;
		}

		throw SemaphoreException("Unknown semaphore error !");
	}

	return true;
#endif
}

int Semaphore::GetValue()
{
#ifdef _WIN32
	LONG value;

	ReleaseSemaphore(_semaphore, 0, &value);
	
	return value;
#else
	int r;
    
	sem_getvalue(&_semaphore, &r);
    
	return r;
#endif
}

void Semaphore::Release()
{
	try {
		NotifyAll();
	} catch (SemaphoreException &) {
	}

#ifdef _WIN32
	if ((void *)_sa != NULL) {
		HeapFree(GetProcessHeap(), 0, _sa);
	}

	if ((void *)_semaphore != NULL) {
		if (CloseHandle(_semaphore) == 0) {
			throw SemaphoreException("Close semaphore failed");
		}

		_semaphore = NULL;
	}
#else
	sem_destroy(&_semaphore);
#endif
}

}