/*
 * Copyright (C) 2013-21 Robert Boisvert
 *
 * This file is part of the mohqueue module for Kamailio, a free SIP server.
 *
 * The mohqueue module 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
 *
 * The mohqueue module 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "mohqueue_mod.h"
#include "mohq_locks.h"

/**********
* external functions
**********/

/**********
* Change Lock
*
* INPUT:
*   Arg (1) = lock pointer
*   Arg (2) = exclusive flag
* OUTPUT: 0 if failed
**********/

int mohq_lock_change(mohq_lock *plock, int bexcl)

{
	/**********
* o lock memory
* o check set type
* o unlock memory
**********/

	int nret = 0;
	lock_get(plock->plock);
	if(bexcl) {
		if(plock->lock_cnt == 1) {
			plock->lock_cnt = -1;
			nret = 1;
		}
	} else {
		if(plock->lock_cnt == -1) {
			plock->lock_cnt = 1;
			nret = 1;
		}
	}
	lock_release(plock->plock);
	return nret;
}

/**********
* Destroy Lock Record
*
* INPUT:
*   Arg (1) = lock pointer
* OUTPUT: none
**********/

void mohq_lock_destroy(mohq_lock *plock)

{
	lock_destroy(plock->plock);
	lock_dealloc(plock->plock);
	return;
}

/**********
* Init Lock Record
*
* INPUT:
*   Arg (1) = lock pointer
* OUTPUT: 0 if failed
**********/

int mohq_lock_init(mohq_lock *plock)

{
	/**********
* alloc memory and initialize
**********/

	char *pfncname = "mohq_lock_init: ";
	plock->plock = lock_alloc();
	if(!plock->plock) {
		LM_ERR("%sUnable to allocate lock memory!\n", pfncname);
		return 0;
	}
	if(!lock_init(plock->plock)) {
		LM_ERR("%sUnable to init lock!\n", pfncname);
		lock_dealloc(plock->plock);
		return 0;
	}
	plock->lock_cnt = 0;
	return -1;
}

/**********
* Release Lock
*
* INPUT:
*   Arg (1) = lock pointer
* OUTPUT: none
**********/

void mohq_lock_release(mohq_lock *plock)

{
	/**********
* o lock memory
* o reduce count
* o unlock memory
**********/

	lock_get(plock->plock);
	switch(plock->lock_cnt) {
		case -1:
			plock->lock_cnt = 0;
			break;
		case 0:
			LM_WARN("mohq_lock_release: Lock was not set.\n");
			break;
		default:
			plock->lock_cnt--;
			break;
	}
	lock_release(plock->plock);
	return;
}

/**********
* Set Lock
*
* INPUT:
*   Arg (1) = lock pointer
*   Arg (2) = exclusive flag
*   Arg (3) = milliseconds to try; 0 = try once
* OUTPUT: 0 if failed
**********/

int mohq_lock_set(mohq_lock *plock, int bexcl, int nms_cnt)

{
	int nret = 0;
	do {
		/**********
  * o lock memory
  * o check set type
  * o unlock memory
  * o sleep if failed
  **********/

		lock_get(plock->plock);
		if(bexcl) {
			if(!plock->lock_cnt) {
				plock->lock_cnt = -1;
				nret = 1;
			}
		} else {
			if(plock->lock_cnt != -1) {
				plock->lock_cnt++;
				nret = 1;
			}
		}
		lock_release(plock->plock);
		if(!nret) {
			usleep(1);
		}
	} while(!nret && --nms_cnt >= 0);
	return nret;
}