287 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|     Copyright (c) 2016-2018 Chung, Hyung-Hwan. All rights reserved.
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
|     1. Redistributions of source code must retain the above copyright
 | |
|        notice, this list of conditions and the following disclaimer.
 | |
|     2. Redistributions in binary form must reproduce the above copyright
 | |
|        notice, this list of conditions and the following disclaimer in the
 | |
|        documentation and/or other materials provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 | |
|     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 | |
|     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | |
|     IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 | |
|     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | |
|     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | |
|     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
|     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | |
|     THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include <hak-tmr.h>
 | |
| #include "hak-prv.h"
 | |
| 
 | |
| #define HEAP_PARENT(x) (((x) - 1) / 2)
 | |
| #define HEAP_LEFT(x)   ((x) * 2 + 1)
 | |
| #define HEAP_RIGHT(x)  ((x) * 2 + 2)
 | |
| 
 | |
| #define YOUNGER_THAN(x,y) (HAK_CMP_NTIME(&(x)->when, &(y)->when) < 0)
 | |
| 
 | |
| hak_tmr_t* hak_tmr_open (hak_t* hak, hak_oow_t xtnsize, hak_oow_t capa)
 | |
| {
 | |
| 	hak_tmr_t* tmr;
 | |
| 
 | |
| 	tmr = (hak_tmr_t*)hak_allocmem(hak, HAK_SIZEOF(*tmr) + xtnsize);
 | |
| 	if (tmr)
 | |
| 	{
 | |
| 		if (hak_tmr_init(tmr, hak, capa) <= -1)
 | |
| 		{
 | |
| 			hak_freemem (tmr->hak, tmr);
 | |
| 			return HAK_NULL;
 | |
| 		}
 | |
| 		else HAK_MEMSET(tmr + 1, 0, xtnsize);
 | |
| 	}
 | |
| 
 | |
| 	return tmr;
 | |
| }
 | |
| 
 | |
| void hak_tmr_close (hak_tmr_t* tmr)
 | |
| {
 | |
| 	hak_tmr_fini (tmr);
 | |
| 	hak_freemem (tmr->hak, tmr);
 | |
| }
 | |
| 
 | |
| int hak_tmr_init (hak_tmr_t* tmr, hak_t* hak, hak_oow_t capa)
 | |
| {
 | |
| 	hak_tmr_event_t* tmp;
 | |
| 
 | |
| 	HAK_MEMSET(tmr, 0, HAK_SIZEOF(*tmr));
 | |
| 
 | |
| 	if (capa <= 0) capa = 1;
 | |
| 
 | |
| 	tmp = (hak_tmr_event_t*)hak_allocmem(hak, capa * HAK_SIZEOF(*tmp));
 | |
| 	if (!tmp) return -1;
 | |
| 
 | |
| 	tmr->hak = hak;
 | |
| 	tmr->capa = capa;
 | |
| 	tmr->event = tmp;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void hak_tmr_fini (hak_tmr_t* tmr)
 | |
| {
 | |
| 	hak_tmr_clear (tmr);
 | |
| 	if (tmr->event)
 | |
| 	{
 | |
| 		hak_freemem (tmr->hak, tmr->event);
 | |
| 		tmr->event = HAK_NULL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
| hak_mmgr_t* hak_tmr_getmmgr (hak_tmr_t* tmr)
 | |
| {
 | |
| 	return tmr->hak->mmgr;
 | |
| }*/
 | |
| 
 | |
| void* hak_tmr_getxtn (hak_tmr_t* tmr)
 | |
| {
 | |
| 	return (void*)(tmr + 1);
 | |
| }
 | |
| 
 | |
| void hak_tmr_clear (hak_tmr_t* tmr)
 | |
| {
 | |
| 	while (tmr->size > 0) hak_tmr_delete (tmr, 0);
 | |
| }
 | |
| 
 | |
| static hak_tmr_index_t sift_up (hak_tmr_t* tmr, hak_tmr_index_t index, int notify)
 | |
| {
 | |
| 	hak_oow_t parent;
 | |
| 
 | |
| 	parent = HEAP_PARENT(index);
 | |
| 	if (index > 0 && YOUNGER_THAN(&tmr->event[index], &tmr->event[parent]))
 | |
| 	{
 | |
| 		hak_tmr_event_t item;
 | |
| 		hak_oow_t old_index;
 | |
| 
 | |
| 		item = tmr->event[index];
 | |
| 		old_index = index;
 | |
| 
 | |
| 		do
 | |
| 		{
 | |
| 			/* move down the parent to my current position */
 | |
| 			tmr->event[index] = tmr->event[parent];
 | |
| 			tmr->event[index].updater (tmr, parent, index, &tmr->event[index]);
 | |
| 
 | |
| 			/* traverse up */
 | |
| 			index = parent;
 | |
| 			parent = HEAP_PARENT(parent);
 | |
| 		}
 | |
| 		while (index > 0 && YOUNGER_THAN(&item, &tmr->event[parent]));
 | |
| 
 | |
| 		/* we send no notification if the item is added with hak_tmr_insert()
 | |
| 		 * or updated with hak_tmr_update(). the caller of these functions
 | |
| 		 * must rely on the return value. */
 | |
| 		tmr->event[index] = item;
 | |
| 		if (notify && index != old_index)
 | |
| 			tmr->event[index].updater (tmr, old_index, index, &tmr->event[index]);
 | |
| 	}
 | |
| 
 | |
| 	return index;
 | |
| }
 | |
| 
 | |
| static hak_tmr_index_t sift_down (hak_tmr_t* tmr, hak_tmr_index_t index, int notify)
 | |
| {
 | |
| 	hak_oow_t base = tmr->size / 2;
 | |
| 
 | |
| 	if (index < base) /* at least 1 child is under the 'index' position */
 | |
| 	{
 | |
| 		hak_tmr_event_t item;
 | |
| 		hak_oow_t old_index;
 | |
| 
 | |
| 		item = tmr->event[index];
 | |
| 		old_index = index;
 | |
| 
 | |
| 		do
 | |
| 		{
 | |
| 			hak_oow_t left, right, younger;
 | |
| 
 | |
| 			left = HEAP_LEFT(index);
 | |
| 			right = HEAP_RIGHT(index);
 | |
| 
 | |
| 			if (right < tmr->size && YOUNGER_THAN(&tmr->event[right], &tmr->event[left]))
 | |
| 			{
 | |
| 				younger = right;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				younger = left;
 | |
| 			}
 | |
| 
 | |
| 			if (YOUNGER_THAN(&item, &tmr->event[younger])) break;
 | |
| 
 | |
| 			tmr->event[index] = tmr->event[younger];
 | |
| 			tmr->event[index].updater (tmr, younger, index, &tmr->event[index]);
 | |
| 
 | |
| 			index = younger;
 | |
| 		}
 | |
| 		while (index < base);
 | |
| 
 | |
| 		tmr->event[index] = item;
 | |
| 		if (notify && index != old_index)
 | |
| 			tmr->event[index].updater (tmr, old_index, index, &tmr->event[index]);
 | |
| 	}
 | |
| 
 | |
| 	return index;
 | |
| }
 | |
| 
 | |
| void hak_tmr_delete (hak_tmr_t* tmr, hak_tmr_index_t index)
 | |
| {
 | |
| 	hak_tmr_event_t item;
 | |
| 
 | |
| 	HAK_ASSERT(tmr->hak, index < tmr->size);
 | |
| 
 | |
| 	item = tmr->event[index];
 | |
| 	tmr->event[index].updater (tmr, index, HAK_TMR_INVALID_INDEX, &tmr->event[index]);
 | |
| 
 | |
| 	tmr->size = tmr->size - 1;
 | |
| 	if (tmr->size > 0 && index != tmr->size)
 | |
| 	{
 | |
| 		tmr->event[index] = tmr->event[tmr->size];
 | |
| 		tmr->event[index].updater (tmr, tmr->size, index, &tmr->event[index]);
 | |
| 		YOUNGER_THAN(&tmr->event[index], &item)? sift_up(tmr, index, 1): sift_down(tmr, index, 1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| hak_tmr_index_t hak_tmr_insert (hak_tmr_t* tmr, const hak_tmr_event_t* event)
 | |
| {
 | |
| 	hak_tmr_index_t index = tmr->size;
 | |
| 
 | |
| 	if (index >= tmr->capa)
 | |
| 	{
 | |
| 		hak_tmr_event_t* tmp;
 | |
| 		hak_oow_t new_capa;
 | |
| 
 | |
| 		HAK_ASSERT(tmr->hak, tmr->capa >= 1);
 | |
| 		new_capa = tmr->capa * 2;
 | |
| 		tmp = (hak_tmr_event_t*)hak_reallocmem(tmr->hak, tmr->event, new_capa * HAK_SIZEOF(*tmp));
 | |
| 		if (!tmp) return HAK_TMR_INVALID_INDEX;
 | |
| 
 | |
| 		tmr->event = tmp;
 | |
| 		tmr->capa = new_capa;
 | |
| 	}
 | |
| 
 | |
| 	HAK_ASSERT(tmr->hak, event->handler != HAK_NULL);
 | |
| 	HAK_ASSERT(tmr->hak, event->updater != HAK_NULL);
 | |
| 
 | |
| 	tmr->size = tmr->size + 1;
 | |
| 	tmr->event[index] = *event;
 | |
| 	return sift_up(tmr, index, 0);
 | |
| }
 | |
| 
 | |
| hak_tmr_index_t hak_tmr_update (hak_tmr_t* tmr, hak_oow_t index, const hak_tmr_event_t* event)
 | |
| {
 | |
| 	hak_tmr_event_t item;
 | |
| 
 | |
| 	HAK_ASSERT(tmr->hak, event->handler != HAK_NULL);
 | |
| 	HAK_ASSERT(tmr->hak, event->updater != HAK_NULL);
 | |
| 
 | |
| 	item = tmr->event[index];
 | |
| 	tmr->event[index] = *event;
 | |
| 	return YOUNGER_THAN(event, &item)? sift_up(tmr, index, 0): sift_down(tmr, index, 0);
 | |
| }
 | |
| 
 | |
| int hak_tmr_fire (hak_tmr_t* tmr, const hak_ntime_t* tm, hak_oow_t* firecnt)
 | |
| {
 | |
| 	hak_ntime_t now;
 | |
| 	hak_tmr_event_t event;
 | |
| 	hak_oow_t fire_count = 0;
 | |
| 
 | |
| 	/* if the current time is not specified, get it from the system */
 | |
| 	if (tm) now = *tm;
 | |
| 	/*else if (hak_gettime(&now) <= -1) return -1;*/
 | |
| 	tmr->hak->vmprim.vm_gettime (tmr->hak, &now);
 | |
| 
 | |
| 	while (tmr->size > 0)
 | |
| 	{
 | |
| 		if (HAK_CMP_NTIME(&tmr->event[0].when, &now) > 0) break;
 | |
| 
 | |
| 		event = tmr->event[0];
 | |
| 		hak_tmr_delete (tmr, 0); /* remove the registered event structure */
 | |
| 
 | |
| 		fire_count++;
 | |
| 		event.handler (tmr, &now, &event); /* then fire the event */
 | |
| 	}
 | |
| 
 | |
| 	if (firecnt) *firecnt = fire_count;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int hak_tmr_gettmout (hak_tmr_t* tmr, const hak_ntime_t* tm, hak_ntime_t* tmout)
 | |
| {
 | |
| 	hak_ntime_t now;
 | |
| 
 | |
| 	/* time-out can't be calculated when there's no event scheduled */
 | |
| 	if (tmr->size <= 0) return -1;
 | |
| 
 | |
| 	/* if the current time is not specified, get it from the system */
 | |
| 	if (tm) now = *tm;
 | |
| 	/*else if (hak_gettime(&now) <= -1) return -1;*/
 | |
| 	tmr->hak->vmprim.vm_gettime (tmr->hak, &now);
 | |
| 
 | |
| 	HAK_SUB_NTIME (tmout, &tmr->event[0].when, &now);
 | |
| 	if (tmout->sec < 0) HAK_CLEAR_NTIME (tmout);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| hak_tmr_event_t* hak_tmr_getevent (hak_tmr_t* tmr, hak_tmr_index_t index)
 | |
| {
 | |
| 	return (index < 0 || index >= tmr->size)? HAK_NULL: &tmr->event[index];
 | |
| }
 |