[Openais] [PATCH]: openais/trunk: Initial patch for OpenAIS TMR service

Ryan O'Hara rohara at redhat.com
Wed Feb 11 10:19:30 PST 2009


This patch contains the initial implementation of the OpenAIS TMR
service.

Most of the service API calls are implemented. The exception is
saTmrTimerRemainingTimeGet. This call should return the time until the
next expiration of a periodic timer. To implement this in a sane
manner will require changes to the corosync API, which are currently
being designed and will be included in a separate patch.

Another missing bit of code is the code to check is an absolute timer
has an initial expire time in the past. This is invalid, and this
check should be done in the corosync timer API code. Until that code
is completed, you should be careful when adding absolute timers.

This patch contains many debugging printf's and log_printf's. These
will be removed in the future.

A test program is included (testtmr.c).



-------------- next part --------------
Index: test/Makefile
===================================================================
--- test/Makefile	(revision 1694)
+++ test/Makefile	(working copy)
@@ -40,9 +40,9 @@
 	override LDFLAGS += -lnsl -lsocket -lrt
 endif
 
-LIBRARIES= ../lib/libSaCkpt.a ../lib/libSaLck.a ../lib/libSaMsg.a ../lib/libSaEvt.a sa_error.o
+LIBRARIES= ../lib/libSaCkpt.a ../lib/libSaLck.a ../lib/libSaMsg.a ../lib/libSaEvt.a ../lib/libSaTmr.a sa_error.o
 LIBS = $(LIBRARIES) 
-BINARIES= testckpt testlck testlck2 testmsg testmsg2 testmsg3 testevt
+BINARIES= testckpt testlck testlck2 testmsg testmsg2 testmsg3 testevt testtmr
 
 override CFLAGS += -I../include
 override LDFLAGS += -L../lib
@@ -72,6 +72,10 @@
 
 testevt: testevt.o $(LIBRARIES)
 	$(CC) $(LDFLAGS) -o testevt testevt.o $(LIBS)
+
+testtmr: testtmr.o $(LIBRARIES)
+	$(CC) $(LDFLAGS) -o testtmr testtmr.o $(LIBS)
+
 clean:
 	rm -f *.o $(LIBRARIES) $(BINARIES)
 
Index: test/testtmr.c
===================================================================
--- test/testtmr.c	(revision 0)
+++ test/testtmr.c	(revision 0)
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2008 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Ryan O'Hara (rohara at redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the MontaVista Software, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/un.h>
+
+#include "saAis.h"
+#include "saTmr.h"
+
+#define TMR_10_SECONDS (SaTimeT)(10000000000)
+#define TMR_20_SECONDS (SaTimeT)(20000000000)
+#define TMR_30_SECONDS (SaTimeT)(30000000000)
+
+void TimerExpiredCallback (
+	SaTmrTimerIdT timerId,
+	const void *timerData,
+	SaUint64T expirationCount)
+{
+	/* DEBUG */
+	printf ("[DEBUG]: TimerExpiredCallback\n");
+	printf ("[DEBUG]:\t timerId = %u\n", timerId);
+	printf ("[DEBUG]:\t timerData = %p\n", timerData);
+	printf ("[DEBUG]:\t expirationCount = %u\n", expirationCount);
+
+	return;
+}
+
+SaTmrCallbacksT callbacks = {
+	.saTmrTimerExpiredCallback = TimerExpiredCallback
+};
+
+SaVersionT version = { 'A', 1, 1 };
+
+void *tmr_dispatch (void *data)
+{
+	SaTmrHandleT *handle = (SaTmrHandleT *)data;
+
+	saTmrDispatch (*handle, SA_DISPATCH_BLOCKING);
+
+	return (0);
+}
+
+int main (void)
+{
+	SaTmrHandleT handle;
+	SaSelectionObjectT select_obj;
+	SaTmrTimerAttributesT my_attrs;
+	SaTmrTimerAttributesT attrs_a = { SA_TIME_DURATION, TMR_10_SECONDS, TMR_10_SECONDS };
+	SaTmrTimerAttributesT attrs_b = { SA_TIME_DURATION, TMR_20_SECONDS, TMR_20_SECONDS };
+	SaTmrTimerIdT id_a;
+	SaTmrTimerIdT id_b;
+	SaTimeT time;
+	SaTimeT current_time_a;
+	SaTimeT current_time_b;
+	SaTimeT delta_time;
+	SaTimeT clock_tick;
+
+	pthread_t dispatch_thread;
+	int result;
+	int i;
+
+	printf ("[DEBUG]: TMR_10_SECONDS = %"PRId64"\n", TMR_10_SECONDS);
+
+	result = saTmrInitialize (&handle, &callbacks, &version);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrInitialize\n", result);
+		exit (result);
+	}
+
+	pthread_create (&dispatch_thread, NULL, &tmr_dispatch, &handle);
+
+	saTmrSelectionObjectGet (handle, &select_obj);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrSelectionObjectGet\n", result);
+		exit (result);
+	}
+
+	result = saTmrTimeGet (handle, &current_time_a);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimeGet\n", result);
+	}
+	else {
+		printf ("[DEBUG]: current_time = %"PRIu64"\n", current_time_a);
+	}
+
+	result = saTmrClockTickGet (handle, &clock_tick);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrClockTickGet\n", result);
+	}
+	else {
+		printf ("[DEBUG]: clock_tick = %"PRIu64"\n", clock_tick);
+	}
+
+	sleep (10);
+
+	result = saTmrTimeGet (handle, &current_time_b);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimeGet\n", result);
+	}
+	else {
+		printf ("[DEBUG]: current_time = %"PRIu64"\n", current_time_b);
+	}
+
+	delta_time = current_time_b - current_time_a;
+
+	printf ("[DEBUG]: delta_time = %"PRIu64"\n", delta_time);
+
+	result = saTmrTimerStart (handle, &attrs_a, NULL, &id_a, &time);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerStart\n", result);
+	}
+	else {
+		printf ("[DEBUG]: saTmrTimerStart { id=%u }\n", id_a);
+		printf ("[DEBUG]:\t callTime = %"PRIu64"\n", time);
+	}
+
+	result = saTmrTimerAttributesGet (handle, id_a, &my_attrs);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerAttributesGet\n", result);
+	}
+	else {
+		printf ("[DEBUG]: id=%u attributes:\n", id_a);
+		printf ("[DEBUG]:\t type=%"PRId64"\n", my_attrs.type);
+		printf ("[DEBUG]:\t expire=%"PRIi64"\n", my_attrs.initialExpirationTime);
+		printf ("[DEBUG]:\t duration=%"PRIi64"\n", my_attrs.timerPeriodDuration);
+	}
+
+	result = saTmrTimerStart (handle, &attrs_b, NULL, &id_b, &time);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerStart\n", result);
+	}
+	else {
+		printf ("[DEBUG]: saTmrTimerStart { id=%u }\n", id_b);
+		printf ("[DEBUG]:\t callTime = %"PRIu64"\n", time);
+	}
+
+	result = saTmrTimerAttributesGet (handle, id_a, &my_attrs);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerAttributesGet\n", result);
+	}
+	else {
+		printf ("[DEBUG]: id=%u attributes:\n", id_a);
+		printf ("[DEBUG]:\t type=%"PRId64"\n", my_attrs.type);
+		printf ("[DEBUG]:\t expire=%"PRId64"\n", my_attrs.initialExpirationTime);
+		printf ("[DEBUG]:\t duration=%"PRId64"\n", my_attrs.timerPeriodDuration);
+	}
+
+	result = saTmrTimerReschedule (handle, id_a, &attrs_a, &time);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerReschedule\n", result);
+	}
+
+	result = saTmrTimerAttributesGet (handle, id_a, &my_attrs);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerAttributesGet\n", result);
+	}
+	else {
+		printf ("[DEBUG]: id=%u attributes:\n", id_a);
+		printf ("[DEBUG]:\t type=%"PRId64"\n", my_attrs.type);
+		printf ("[DEBUG]:\t expire=%"PRId64"\n", my_attrs.initialExpirationTime);
+		printf ("[DEBUG]:\t duration=%"PRId64"\n", my_attrs.timerPeriodDuration);
+	}
+
+	result = saTmrTimerReschedule (handle, id_b, &attrs_b, &time);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerReschedule\n", result);
+	}
+
+	result = saTmrTimerAttributesGet (handle, id_a, &my_attrs);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerAttributesGet\n", result);
+	}
+	else {
+		printf ("[DEBUG]: id=%u attributes:\n", id_a);
+		printf ("[DEBUG]:\t type=%"PRId64"\n", my_attrs.type);
+		printf ("[DEBUG]:\t expire=%"PRId64"\n", my_attrs.initialExpirationTime);
+		printf ("[DEBUG]:\t duration=%"PRId64"\n", my_attrs.timerPeriodDuration);
+	}
+
+	sleep (30);
+
+	result = saTmrPeriodicTimerSkip (handle, id_a);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrPeriodicTimerSkip\n", result);
+	}
+
+	sleep (60);
+
+	for (i = 0; i < 4; i++) {
+		result = saTmrPeriodicTimerSkip (handle, id_a);
+		if (result != SA_AIS_OK) {
+			printf ("[ERROR]: (%d) saTmrPeriodicTimerSkip\n", result);
+		}
+	}
+
+	sleep (120);
+
+	result = saTmrTimerCancel (handle, id_a, NULL);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerCancel\n", result);
+	}
+
+	result = saTmrTimerReschedule (handle, id_a, &attrs_a, &time);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerReschedule\n", result);
+	}
+
+	result = saTmrTimerCancel (handle, id_b, NULL);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerCancel\n", result);
+	}
+
+	result = saTmrTimerReschedule (handle, id_b, &attrs_b, &time);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrTimerReschedule\n", result);
+	}
+
+	result = saTmrFinalize (handle);
+	if (result != SA_AIS_OK) {
+		printf ("[ERROR]: (%d) saTmrFinalize\n", result);
+		exit (1);
+	}
+
+	return (0);
+}
Index: include/ipc_tmr.h
===================================================================
--- include/ipc_tmr.h	(revision 0)
+++ include/ipc_tmr.h	(revision 0)
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2008 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Ryan O'Hara (rohara at redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the MontaVista Software, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ */
+
+#ifndef IPC_TMR_H_DEFINED
+#define IPC_TMR_H_DEFINED
+
+#include "saAis.h"
+#include "saTmr.h"
+
+#include <corosync/ipc_gen.h>
+
+enum req_lib_tmr_timer_types {
+	MESSAGE_REQ_TMR_TIMERSTART = 0,
+	MESSAGE_REQ_TMR_TIMERRESCHEDULE = 1,
+	MESSAGE_REQ_TMR_TIMERCANCEL = 2,
+	MESSAGE_REQ_TMR_PERIODICTIMERSKIP = 3,
+	MESSAGE_REQ_TMR_TIMERREMAININGTIMEGET = 4,
+	MESSAGE_REQ_TMR_TIMERATTRIBUTESGET = 5,
+	MESSAGE_REQ_TMR_TIMEGET = 6,
+	MESSAGE_REQ_TMR_CLOCKTICKGET = 7,
+	MESSAGE_REQ_TMR_TIMEREXPIREDCALLBACK = 8,
+};
+
+enum res_lib_tmr_timer_types {
+	MESSAGE_RES_TMR_TIMERSTART = 0,
+	MESSAGE_RES_TMR_TIMERRESCHEDULE = 1,
+	MESSAGE_RES_TMR_TIMERCANCEL = 2,
+	MESSAGE_RES_TMR_PERIODICTIMERSKIP = 3,
+	MESSAGE_RES_TMR_TIMERREMAININGTIMEGET = 4,
+	MESSAGE_RES_TMR_TIMERATTRIBUTESGET = 5,
+	MESSAGE_RES_TMR_TIMEGET = 6,
+	MESSAGE_RES_TMR_CLOCKTICKGET = 7,
+	MESSAGE_RES_TMR_TIMEREXPIREDCALLBACK = 8,
+};
+
+struct req_lib_tmr_timerstart {
+	mar_req_header_t header;
+	SaTmrTimerIdT timer_id;
+	SaTmrTimerAttributesT timer_attributes;
+	SaTimeT call_time;
+};
+
+struct res_lib_tmr_timerstart {
+	mar_res_header_t header;
+	SaTmrTimerIdT timer_id;
+};
+
+struct req_lib_tmr_timerreschedule {
+	mar_req_header_t header;
+	SaTmrTimerIdT timer_id;
+	SaTmrTimerAttributesT timer_attributes;
+};
+
+struct res_lib_tmr_timerreschedule {
+	mar_res_header_t header;
+	SaTimeT call_time;
+};
+
+struct req_lib_tmr_timercancel {
+	mar_req_header_t header;
+	SaTmrTimerIdT timer_id;
+};
+
+struct res_lib_tmr_timercancel {
+	mar_res_header_t header;
+};
+
+struct req_lib_tmr_periodictimerskip {
+	mar_req_header_t header;
+	SaTmrTimerIdT timer_id;
+};
+
+struct res_lib_tmr_periodictimerskip {
+	mar_res_header_t header;
+};
+
+struct req_lib_tmr_timerremainingtimeget {
+	mar_req_header_t header;
+	SaTmrTimerIdT timer_id;
+};
+
+struct res_lib_tmr_timerremainingtimeget {
+	mar_res_header_t header;
+	SaTimeT remaining_time;
+};
+
+struct req_lib_tmr_timerattributesget {
+	mar_req_header_t header;
+	SaTmrTimerIdT timer_id;
+};
+
+struct res_lib_tmr_timerattributesget {
+	mar_res_header_t header;
+	SaTmrTimerAttributesT timer_attributes;
+};
+
+struct req_lib_tmr_timeget {
+	mar_req_header_t header;
+};
+
+struct res_lib_tmr_timeget {
+	mar_res_header_t header;
+	SaTimeT current_time;
+};
+
+struct req_lib_tmr_clocktickget {
+	mar_req_header_t header;
+};
+
+struct res_lib_tmr_clocktickget {
+	mar_res_header_t header;
+	SaTimeT clock_tick;
+};
+
+struct req_lib_tmr_timerexpiredcallback {
+	mar_res_header_t header;
+};
+
+struct res_lib_tmr_timerexpiredcallback {
+	mar_res_header_t header;
+	SaTmrTimerIdT timer_id;
+	void *timer_data;
+	SaUint64T expiration_count;
+};
+
+#endif /* IPC_TMR_H_DEFINED  */
Index: include/saTmr.h
===================================================================
--- include/saTmr.h	(revision 0)
+++ include/saTmr.h	(revision 0)
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2008 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Ryan O'Hara (rohara at redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the MontaVista Software, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ */
+
+#ifndef SATMR_H_DEFINED
+#define SATMR_H_DEFINED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef SaUint64T SaTmrHandleT;
+
+typedef SaUint64T SaTmrTimerIdT;
+
+typedef enum {
+	SA_TIME_ABSOLUTE = 1,
+	SA_TIME_DURATION = 2,
+} SaTmrTimeTypeT;
+
+typedef struct {
+	SaTmrTimeTypeT type;
+	SaTimeT initialExpirationTime;
+	SaTimeT timerPeriodDuration;
+} SaTmrTimerAttributesT;
+
+typedef void (*SaTmrTimerExpiredCallbackT) (
+	SaTmrTimerIdT timerId,
+	const void *timerData,
+	SaUint64T expirationCount);
+
+typedef struct {
+	SaTmrTimerExpiredCallbackT saTmrTimerExpiredCallback;
+} SaTmrCallbacksT;
+
+SaAisErrorT
+saTmrInitialize (
+	SaTmrHandleT *tmrHandle,
+	const SaTmrCallbacksT *timerCallbacks,
+	SaVersionT *version);
+
+SaAisErrorT
+saTmrSelectionObjectGet (
+	SaTmrHandleT tmrHandle,
+	SaSelectionObjectT *selectionObject);
+
+SaAisErrorT
+saTmrDispatch (
+	SaTmrHandleT tmrHandle,
+	SaDispatchFlagsT dispatchFlags);
+
+SaAisErrorT
+saTmrFinalize (
+	SaTmrHandleT tmrHandle);
+
+SaAisErrorT
+saTmrTimerStart (
+	SaTmrHandleT tmrHandle,
+	const SaTmrTimerAttributesT *timerAttributes,
+	const void *timerData,
+	SaTmrTimerIdT *timerId,
+	SaTimeT *callTime);
+
+SaAisErrorT
+saTmrTimerReschedule (
+	SaTmrHandleT tmrHandle,
+	SaTmrTimerIdT timerId,
+	const SaTmrTimerAttributesT *timerAttributes,
+	SaTimeT *callTime);
+
+SaAisErrorT
+saTmrTimerCancel (
+	SaTmrHandleT tmrHandle,
+	SaTmrTimerIdT timerId,
+	void **timerDataP);
+
+SaAisErrorT
+saTmrPeriodicTimerSkip (
+	SaTmrHandleT tmrHandle,
+	SaTmrTimerIdT timerId);
+
+SaAisErrorT
+saTmrTimerRemainingTimeGet (
+	SaTmrHandleT tmrHandle,
+	SaTmrTimerIdT timerId,
+	SaTimeT *remainingTime);
+
+SaAisErrorT
+saTmrTimerAttributesGet (
+	SaTmrHandleT tmrHandle,
+	SaTmrTimerIdT timerId,
+	SaTmrTimerAttributesT *timerAttributes);
+
+SaAisErrorT
+saTmrTimeGet (
+	SaTmrHandleT tmrHandle,
+	SaTimeT *currentTime);
+
+SaAisErrorT
+saTmrClockTickGet (
+	SaTmrHandleT tmrHandle,
+	SaTimeT *clockTick);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SATMR_H_DEFINED */
Index: services/tmr.c
===================================================================
--- services/tmr.c	(revision 0)
+++ services/tmr.c	(revision 0)
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2008 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Ryan O'Hara (rohara at redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the MontaVista Software, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 <sys/types.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include <inttypes.h> /* for development only */
+
+#include <corosync/ipc_gen.h>
+#include <corosync/mar_gen.h>
+#include <corosync/swab.h>
+#include <corosync/list.h>
+#include <corosync/engine/coroapi.h>
+#include <corosync/engine/logsys.h>
+#include <corosync/lcr/lcr_comp.h>
+
+#include "../include/saAis.h"
+#include "../include/saTmr.h"
+#include "../include/ipc_tmr.h"
+
+LOGSYS_DECLARE_SUBSYS ("TMR", LOG_INFO);
+
+struct timer {
+	SaTmrTimerIdT timer_id;
+	SaTmrTimerAttributesT timer_attributes;
+	corosync_timer_handle_t timer_handle;
+	mar_message_source_t source;
+	SaUint64T expiration_count;
+	SaUint64T timer_skip;
+	SaTimeT call_time;
+	void *timer_data;
+	struct list_head list;
+};
+
+DECLARE_LIST_INIT(timer_list_head);
+
+static struct corosync_api_v1 *api;
+
+static int tmr_exec_init_fn (struct corosync_api_v1 *);
+static int tmr_lib_init_fn (void *conn);
+static int tmr_lib_exit_fn (void *conn);
+
+static void message_handler_req_lib_tmr_timerstart (
+	void *conn,
+	void *msg);
+
+static void message_handler_req_lib_tmr_timerreschedule (
+	void *conn,
+	void *msg);
+
+static void message_handler_req_lib_tmr_timercancel (
+	void *conn,
+	void *msg);
+
+static void message_handler_req_lib_tmr_periodictimerskip (
+	void *conn,
+	void *msg);
+
+static void message_handler_req_lib_tmr_timerremainingtimeget (
+	void *conn,
+	void *msg);
+
+static void message_handler_req_lib_tmr_timerattributesget (
+	void *conn,
+	void *msg);
+
+static void message_handler_req_lib_tmr_timeget (
+	void *conn,
+	void *msg);
+
+static void message_handler_req_lib_tmr_clocktickget (
+	void *conn,
+	void *msg);
+
+static struct corosync_api_v1 *api;
+
+struct tmr_pd {
+	struct list_head timer_list;
+	struct list_head timer_cleanup_list;
+};
+
+static struct corosync_lib_handler tmr_lib_engine[] =
+{
+	{
+		.lib_handler_fn		= message_handler_req_lib_tmr_timerstart,
+		.response_size		= sizeof (struct req_lib_tmr_timerstart),
+		.response_id		= MESSAGE_RES_TMR_TIMERSTART,
+		.flow_control		= COROSYNC_LIB_FLOW_CONTROL_REQUIRED
+	},
+	{
+		.lib_handler_fn		= message_handler_req_lib_tmr_timerreschedule,
+		.response_size		= sizeof (struct req_lib_tmr_timerreschedule),
+		.response_id		= MESSAGE_RES_TMR_TIMERRESCHEDULE,
+		.flow_control		= COROSYNC_LIB_FLOW_CONTROL_REQUIRED
+	},
+	{
+		.lib_handler_fn		= message_handler_req_lib_tmr_timercancel,
+		.response_size		= sizeof (struct req_lib_tmr_timercancel),
+		.response_id		= MESSAGE_RES_TMR_TIMERCANCEL,
+		.flow_control		= COROSYNC_LIB_FLOW_CONTROL_REQUIRED
+	},
+	{
+		.lib_handler_fn		= message_handler_req_lib_tmr_periodictimerskip,
+		.response_size		= sizeof (struct req_lib_tmr_periodictimerskip),
+		.response_id		= MESSAGE_RES_TMR_PERIODICTIMERSKIP,
+		.flow_control		= COROSYNC_LIB_FLOW_CONTROL_REQUIRED
+	},
+	{
+		.lib_handler_fn		= message_handler_req_lib_tmr_timerremainingtimeget,
+		.response_size		= sizeof (struct req_lib_tmr_timerremainingtimeget),
+		.response_id		= MESSAGE_RES_TMR_TIMERREMAININGTIMEGET,
+		.flow_control		= COROSYNC_LIB_FLOW_CONTROL_REQUIRED
+	},
+	{
+		.lib_handler_fn		= message_handler_req_lib_tmr_timerattributesget,
+		.response_size		= sizeof (struct req_lib_tmr_timerattributesget),
+		.response_id		= MESSAGE_RES_TMR_TIMERATTRIBUTESGET,
+		.flow_control		= COROSYNC_LIB_FLOW_CONTROL_REQUIRED
+	},
+	{
+		.lib_handler_fn		= message_handler_req_lib_tmr_timeget,
+		.response_size		= sizeof (struct req_lib_tmr_timeget),
+		.response_id		= MESSAGE_RES_TMR_TIMEGET,
+		.flow_control		= COROSYNC_LIB_FLOW_CONTROL_REQUIRED
+	},
+	{
+		.lib_handler_fn		= message_handler_req_lib_tmr_clocktickget,
+		.response_size		= sizeof (struct req_lib_tmr_clocktickget),
+		.response_id		= MESSAGE_RES_TMR_CLOCKTICKGET,
+		.flow_control		= COROSYNC_LIB_FLOW_CONTROL_REQUIRED
+	},
+};
+
+struct corosync_service_engine tmr_service_engine = {
+	.name			= "openais timer service A.01.01",
+	.id			= TMR_SERVICE,
+	.private_data_size	= sizeof (struct tmr_pd),
+	.flow_control		= COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED,
+	.lib_init_fn		= tmr_lib_init_fn,
+	.lib_exit_fn		= tmr_lib_exit_fn,
+	.lib_engine		= tmr_lib_engine,
+	.lib_engine_count	= sizeof (tmr_lib_engine) / sizeof (struct corosync_lib_handler),
+	.exec_init_fn		= tmr_exec_init_fn,
+	.exec_dump_fn		= NULL,
+	.exec_engine		= NULL,
+	.exec_engine_count	= 0,
+	.confchg_fn		= NULL,
+	.sync_init		= NULL,
+	.sync_process		= NULL,
+	.sync_activate		= NULL,
+	.sync_abort		= NULL,
+};
+
+static struct corosync_service_engine *tmr_get_engine_ver0 (void);
+
+static struct corosync_service_engine_iface_ver0 tmr_service_engine_iface = {
+	.corosync_get_service_engine_ver0 = tmr_get_engine_ver0
+};
+
+static struct lcr_iface openais_tmr_ver0[1] = {
+	{
+		.name			= "openais_tmr",
+		.version		= 0,
+		.versions_replace	= 0,
+		.versions_replace_count	= 0,
+		.dependencies		= 0,
+		.dependency_count	= 0,
+		.constructor		= NULL,
+		.destructor		= NULL,
+		.interfaces		= NULL,
+	}
+};
+
+static struct lcr_comp tmr_comp_ver0 = {
+	.iface_count	= 1,
+	.ifaces		= openais_tmr_ver0
+};
+
+static struct corosync_service_engine *tmr_get_engine_ver0 (void)
+{
+	return (&tmr_service_engine);
+}
+
+__attribute__ ((constructor)) static void register_this_component (void) {
+	lcr_interfaces_set (&openais_tmr_ver0[0], &tmr_service_engine_iface);
+	lcr_component_register (&tmr_comp_ver0);
+}
+
+static int tmr_exec_init_fn (struct corosync_api_v1 *corosync_api)
+{
+	api = corosync_api;
+
+	return (0);
+}
+
+static int tmr_lib_init_fn (void *conn)
+{
+	return (0);
+}
+
+static int tmr_lib_exit_fn (void *conn)
+{
+	struct tmr_pd *tmr_pd = (struct tmr_pd *) api->ipc_private_data_get (conn);
+
+	list_init (&tmr_pd->timer_list);
+	list_init (&tmr_pd->timer_cleanup_list);
+
+	return (0);
+}
+
+SaTimeT tmr_time_now (void)
+{
+	struct timeval tv;
+	SaTimeT time;
+
+	if (gettimeofday (&tv, 0)) {
+		return (0ULL);
+	}
+
+	time = (SaTimeT)(tv.tv_sec) * 1000000000ULL;
+	time += (SaTimeT)(tv.tv_usec) * 1000ULL;
+
+	/* DEBUG */
+	log_printf (LOG_LEVEL_NOTICE, "[DEBUG]: tmr_time_now %"PRIu64"\n", time);
+
+	return (time);
+}
+
+static void tmr_timer_create (struct timer *timer)
+{
+	return;
+}
+
+static void tmr_timer_expired (void *data)
+{
+	struct timer *timer = (struct timer *)data;
+	struct res_lib_tmr_timerexpiredcallback res_lib_tmr_timerexpiredcallback;
+
+	timer->expiration_count += 1;
+
+	/* DEBUG */
+	log_printf (LOG_LEVEL_NOTICE, "[DEBUG]: tmr_timer_expired { id=%u }\n",
+		    (unsigned int)(timer->timer_id));
+
+	res_lib_tmr_timerexpiredcallback.header.size =
+		sizeof (struct res_lib_tmr_timerexpiredcallback);
+	res_lib_tmr_timerexpiredcallback.header.id =
+		MESSAGE_RES_TMR_TIMEREXPIREDCALLBACK;
+	res_lib_tmr_timerexpiredcallback.header.error = SA_AIS_OK; /* FIXME */
+
+	res_lib_tmr_timerexpiredcallback.timer_id = timer->timer_id;
+	res_lib_tmr_timerexpiredcallback.timer_data = timer->timer_data;
+	res_lib_tmr_timerexpiredcallback.expiration_count = timer->expiration_count;
+
+	if (timer->timer_skip == 0) {
+		api->ipc_conn_send_response (
+			api->ipc_conn_partner_get (timer->source.conn),
+			&res_lib_tmr_timerexpiredcallback,
+			sizeof (struct res_lib_tmr_timerexpiredcallback));
+	}
+	else {
+		/* DEBUG */
+		log_printf (LOG_LEVEL_NOTICE, "[DEBUG]: skipping timer {  id=%u }\n",
+			    (unsigned int)(timer->timer_id));
+
+		timer->timer_skip -= 1;
+	}
+
+	if (timer->timer_attributes.timerPeriodDuration > 0) {
+		switch (timer->timer_attributes.type) {
+		case SA_TIME_ABSOLUTE:
+			api->timer_add_absolute (
+				timer->timer_attributes.timerPeriodDuration,
+				(void *)(timer), tmr_timer_expired,
+				&timer->timer_handle);
+			break;
+		case SA_TIME_DURATION:
+			api->timer_add_duration (
+				timer->timer_attributes.timerPeriodDuration,
+				(void *)(timer), tmr_timer_expired,
+				&timer->timer_handle);
+			break;
+		default:
+			break;
+		}
+	}
+
+	return;
+}
+
+static struct timer *tmr_find_timer (
+	struct list_head *head,
+	SaTmrTimerIdT timer_id)
+{
+	struct list_head *list;
+	struct timer *timer;
+
+	for (list = head->next; list != head; list = list->next)
+	{
+		timer = list_entry (list, struct timer, list);
+
+		if (timer->timer_id == timer_id) {
+			return (timer);
+		}
+	}
+	return (0);
+}
+
+static void message_handler_req_lib_tmr_timerstart (
+	void *conn,
+	void *msg)
+{
+	struct req_lib_tmr_timerstart *req_lib_tmr_timerstart =
+		(struct req_lib_tmr_timerstart *)msg;
+	struct res_lib_tmr_timerstart res_lib_tmr_timerstart;
+	SaAisErrorT error = SA_AIS_OK;
+	struct timer *timer = NULL;
+
+	/* DEBUG */
+	log_printf (LOG_LEVEL_NOTICE, "LIB request: saTmrTimerStart { id=%u }\n",
+		    (unsigned int)(req_lib_tmr_timerstart->timer_id));
+
+	timer = tmr_find_timer (&timer_list_head,
+		req_lib_tmr_timerstart->timer_id);
+	if (timer == NULL) {
+		timer = malloc (sizeof (struct timer));
+		if (timer == NULL) {
+			error = SA_AIS_ERR_NO_MEMORY;
+			goto error_exit;
+		}
+		memset (timer, 0, sizeof (struct timer));
+
+		timer->timer_id = req_lib_tmr_timerstart->timer_id;
+		timer->call_time = req_lib_tmr_timerstart->call_time;
+
+		memcpy (&timer->timer_attributes,
+			&req_lib_tmr_timerstart->timer_attributes,
+			sizeof (SaTmrTimerAttributesT));
+
+		api->ipc_source_set (&timer->source, conn);
+
+		list_init (&timer->list);
+		list_add (&timer->list, &timer_list_head);
+
+		switch (timer->timer_attributes.type)
+		{
+		case SA_TIME_ABSOLUTE:
+			api->timer_add_absolute (
+				timer->timer_attributes.initialExpirationTime,
+				(void *)(timer), tmr_timer_expired,
+				&timer->timer_handle);
+			break;
+		case SA_TIME_DURATION:
+			api->timer_add_duration (
+				timer->timer_attributes.initialExpirationTime,
+				(void *)(timer), tmr_timer_expired,
+				&timer->timer_handle);
+			break;
+		default:
+			/*
+			 * This case is handled in the library.
+			 */
+			break;
+		}
+
+		/* DEBUG */
+		log_printf (LOG_LEVEL_NOTICE, "[DEBUG]:\t timer_handle = %u\n",
+			    (unsigned int)(timer->timer_handle));
+	}
+
+error_exit:
+
+	res_lib_tmr_timerstart.header.size =
+		sizeof (struct res_lib_tmr_timerstart);
+	res_lib_tmr_timerstart.header.id =
+		MESSAGE_RES_TMR_TIMERSTART;
+	res_lib_tmr_timerstart.header.error = error;
+
+	api->ipc_conn_send_response (conn,
+		&res_lib_tmr_timerstart,
+		sizeof (struct res_lib_tmr_timerstart));
+}
+
+static void message_handler_req_lib_tmr_timerreschedule (
+	void *conn,
+	void *msg)
+{
+	struct req_lib_tmr_timerreschedule *req_lib_tmr_timerreschedule =
+		(struct req_lib_tmr_timerreschedule *)msg;
+	struct res_lib_tmr_timerreschedule res_lib_tmr_timerreschedule;
+	SaAisErrorT error = SA_AIS_OK;
+	struct timer *timer = NULL;
+
+	/* DEBUG */
+	log_printf (LOG_LEVEL_NOTICE, "LIB request: saTmrTimerReschedule { id=%u }\n",
+		    (unsigned int)(req_lib_tmr_timerreschedule->timer_id));
+
+	timer = tmr_find_timer (&timer_list_head,
+		req_lib_tmr_timerreschedule->timer_id);
+	if (timer == NULL) {
+		error = SA_AIS_ERR_NOT_EXIST;
+		goto error_exit;
+	}
+
+	memcpy (&timer->timer_attributes,
+		&req_lib_tmr_timerreschedule->timer_attributes,
+		sizeof (SaTmrTimerAttributesT));
+
+error_exit:
+
+	res_lib_tmr_timerreschedule.header.size =
+		sizeof (struct res_lib_tmr_timerreschedule);
+	res_lib_tmr_timerreschedule.header.id =
+		MESSAGE_RES_TMR_TIMERRESCHEDULE;
+	res_lib_tmr_timerreschedule.header.error = error;
+
+	api->ipc_conn_send_response (conn,
+		&res_lib_tmr_timerreschedule,
+		sizeof (struct res_lib_tmr_timerreschedule));
+}
+
+static void message_handler_req_lib_tmr_timercancel (
+	void *conn,
+	void *msg)
+{
+	struct req_lib_tmr_timercancel *req_lib_tmr_timercancel =
+		(struct req_lib_tmr_timercancel *)msg;
+	struct res_lib_tmr_timercancel res_lib_tmr_timercancel;
+	SaAisErrorT error = SA_AIS_OK;
+	struct timer *timer = NULL;
+
+	/* DEBUG */
+	log_printf (LOG_LEVEL_NOTICE, "LIB request: saTmrTimerCancel\n");
+
+	timer = tmr_find_timer (&timer_list_head,
+		req_lib_tmr_timercancel->timer_id);
+	if (timer == NULL) {
+		error = SA_AIS_ERR_NOT_EXIST;
+		goto error_exit;
+	}
+
+	api->timer_delete (timer->timer_handle);
+	list_del (&timer->list);
+	free (timer);
+
+error_exit:
+
+	res_lib_tmr_timercancel.header.size =
+		sizeof (struct res_lib_tmr_timercancel);
+	res_lib_tmr_timercancel.header.id =
+		MESSAGE_RES_TMR_TIMERCANCEL;
+	res_lib_tmr_timercancel.header.error = error;
+
+	api->ipc_conn_send_response (conn,
+		&res_lib_tmr_timercancel,
+		sizeof (struct res_lib_tmr_timercancel));
+}
+
+static void message_handler_req_lib_tmr_periodictimerskip (
+	void *conn,
+	void *msg)
+{
+	struct req_lib_tmr_periodictimerskip *req_lib_tmr_periodictimerskip =
+		(struct req_lib_tmr_periodictimerskip *)msg;
+	struct res_lib_tmr_periodictimerskip res_lib_tmr_periodictimerskip;
+	SaAisErrorT error = SA_AIS_OK;
+	struct timer *timer = NULL;
+
+	/* DEBUG */
+	log_printf (LOG_LEVEL_NOTICE, "LIB request: saTmrPeriodicTimerSkip { id=%u }\n",
+		    (unsigned int)(req_lib_tmr_periodictimerskip->timer_id));
+
+	timer = tmr_find_timer (&timer_list_head,
+		req_lib_tmr_periodictimerskip->timer_id);
+	if (timer == NULL) {
+		error = SA_AIS_ERR_NOT_EXIST;
+		goto error_exit;
+	}
+
+	if (timer->timer_attributes.timerPeriodDuration == 0) {
+		error = SA_AIS_ERR_NOT_EXIST;
+		goto error_exit;
+	}
+
+	timer->timer_skip += 1;
+
+error_exit:
+
+	res_lib_tmr_periodictimerskip.header.size =
+		sizeof (struct res_lib_tmr_periodictimerskip);
+	res_lib_tmr_periodictimerskip.header.id =
+		MESSAGE_RES_TMR_PERIODICTIMERSKIP;
+	res_lib_tmr_periodictimerskip.header.error = error;
+
+	api->ipc_conn_send_response (conn,
+		&res_lib_tmr_periodictimerskip,
+		sizeof (struct res_lib_tmr_periodictimerskip));
+}
+
+static void message_handler_req_lib_tmr_timerremainingtimeget (
+	void *conn,
+	void *msg)
+{
+	struct req_lib_tmr_timerremainingtimeget *req_lib_tmr_timerremainingtimeget =
+		(struct req_lib_tmr_timerremainingtimeget *)msg;
+	struct res_lib_tmr_timerremainingtimeget res_lib_tmr_timerremainingtimeget;
+	SaAisErrorT error = SA_AIS_OK;
+	struct timer *timer = NULL;
+
+	/* DEBUG */
+	log_printf (LOG_LEVEL_NOTICE, "LIB request: saTmrTimerRemainingTimeGet { id=%u }\n",
+		    (unsigned int)(req_lib_tmr_timerremainingtimeget->timer_id));
+
+	timer = tmr_find_timer (&timer_list_head,
+		req_lib_tmr_timerremainingtimeget->timer_id);
+	if (timer == NULL) {
+		error = SA_AIS_ERR_NOT_EXIST;
+		goto error_exit;
+	}
+
+error_exit:
+
+	res_lib_tmr_timerremainingtimeget.header.size =
+		sizeof (struct res_lib_tmr_timerremainingtimeget);
+	res_lib_tmr_timerremainingtimeget.header.id =
+		MESSAGE_RES_TMR_TIMERREMAININGTIMEGET;
+	res_lib_tmr_timerremainingtimeget.header.error = error;
+
+	api->ipc_conn_send_response (conn,
+		&res_lib_tmr_timerremainingtimeget,
+		sizeof (struct res_lib_tmr_timerremainingtimeget));
+}
+
+static void message_handler_req_lib_tmr_timerattributesget (
+	void *conn,
+	void *msg)
+{
+	struct req_lib_tmr_timerattributesget *req_lib_tmr_timerattributesget =
+		(struct req_lib_tmr_timerattributesget *)msg;
+	struct res_lib_tmr_timerattributesget res_lib_tmr_timerattributesget;
+	SaAisErrorT error = SA_AIS_OK;
+	struct timer *timer = NULL;
+
+	/* DEBUG */
+	log_printf (LOG_LEVEL_NOTICE, "LIB request: saTmrTimerAttributesGet { id=%u }\n",
+		    (unsigned int)(req_lib_tmr_timerattributesget->timer_id));
+
+	timer = tmr_find_timer (&timer_list_head,
+		req_lib_tmr_timerattributesget->timer_id);
+	if (timer == NULL) {
+		error = SA_AIS_ERR_NOT_EXIST;
+		goto error_exit;
+	}
+
+	memcpy (&res_lib_tmr_timerattributesget.timer_attributes,
+		&timer->timer_attributes, sizeof (SaTmrTimerAttributesT));
+
+error_exit:
+
+	res_lib_tmr_timerattributesget.header.size =
+		sizeof (struct res_lib_tmr_timerattributesget);
+	res_lib_tmr_timerattributesget.header.id =
+		MESSAGE_RES_TMR_TIMERATTRIBUTESGET;
+	res_lib_tmr_timerattributesget.header.error = error;
+
+	api->ipc_conn_send_response (conn,
+		&res_lib_tmr_timerattributesget,
+		sizeof (struct res_lib_tmr_timerattributesget));
+}
+
+static void message_handler_req_lib_tmr_timeget (
+	void *conn,
+	void *msg)
+{
+	struct req_lib_tmr_timeget *req_lib_tmr_timeget =
+		(struct req_lib_tmr_timeget *)msg;
+	struct res_lib_tmr_timeget res_lib_tmr_timeget;
+	SaAisErrorT error = SA_AIS_OK;
+	SaTimeT current_time;
+
+	/* DEBUG */
+	log_printf (LOG_LEVEL_NOTICE, "LIB request: saTmrTimeGet\n");
+
+	current_time = tmr_time_now();
+
+	memcpy (&res_lib_tmr_timeget.current_time,
+		&current_time, sizeof (SaTimeT));
+
+error_exit:
+
+	res_lib_tmr_timeget.header.size =
+		sizeof (struct res_lib_tmr_timeget);
+	res_lib_tmr_timeget.header.id =
+		MESSAGE_RES_TMR_TIMEGET;
+	res_lib_tmr_timeget.header.error = error;
+
+	api->ipc_conn_send_response (conn,
+		&res_lib_tmr_timeget,
+		sizeof (struct res_lib_tmr_timeget));
+}
+
+static void message_handler_req_lib_tmr_clocktickget (
+	void *conn,
+	void *msg)
+{
+	struct req_lib_tmr_clocktickget *req_lib_tmr_clocktickget =
+		(struct req_lib_tmr_clocktickget *)msg;
+	struct res_lib_tmr_clocktickget res_lib_tmr_clocktickget;
+	SaAisErrorT error = SA_AIS_OK;
+	SaTimeT clock_tick;
+
+	/* DEBUG */
+	log_printf (LOG_LEVEL_NOTICE, "LIB request: saTmrClockTickGet\n");
+
+	clock_tick = (SaTimeT)((1.0 / CLOCKS_PER_SEC) * 1000000000ULL);
+
+	memcpy (&res_lib_tmr_clocktickget.clock_tick,
+		&clock_tick, sizeof (SaTimeT));
+
+error_exit:
+
+	res_lib_tmr_clocktickget.header.size =
+		sizeof (struct res_lib_tmr_clocktickget);
+	res_lib_tmr_clocktickget.header.id =
+		MESSAGE_RES_TMR_CLOCKTICKGET;
+	res_lib_tmr_clocktickget.header.error = error;
+
+	api->ipc_conn_send_response (conn,
+		&res_lib_tmr_clocktickget,
+		sizeof (struct res_lib_tmr_clocktickget));
+}
Index: services/openaisserviceenable.c
===================================================================
--- services/openaisserviceenable.c	(revision 1694)
+++ services/openaisserviceenable.c	(working copy)
@@ -57,12 +57,13 @@
 };
 
 static struct service_engine service_engines[] = {
-	{ "openais_clm", "0" },
-	{ "openais_evt", "0" },
+	{ "openais_clm",  "0" },
+	{ "openais_evt",  "0" },
 	{ "openais_ckpt", "0" },
-	{ "openais_amf", "0" },
-	{ "openais_msg", "0" },
-	{ "openais_lck", "0" }
+	{ "openais_amf",  "0" },
+	{ "openais_msg",  "0" },
+	{ "openais_lck",  "0" },
+	{ "openais_tmr",  "0" },
 };
 
 static int openais_service_enable (
Index: services/Makefile
===================================================================
--- services/Makefile	(revision 1694)
+++ services/Makefile	(working copy)
@@ -52,14 +52,14 @@
 AMF_OBJS = amf.o amfutil.o amfnode.o amfcluster.o amfapp.o amfsg.o amfsu.o amfcomp.o amfsi.o
 
 # LCR objects
-LCR_SRC = openaisparser.c openaisserviceenable.c clm.c ckpt.c evt.c lck.c msg.c cfg.c $(AMF_SRC)
-LCR_OBJS = openaisparser.o openaisserviceenable.o clm.o ckpt.o evt.o lck.o msg.o cfg.o o $(AMF_OBJS)
+LCR_SRC = openaisparser.c openaisserviceenable.c clm.c ckpt.c evt.c lck.c msg.c cfg.c tmr.c $(AMF_SRC)
+LCR_OBJS = openaisparser.o openaisserviceenable.o clm.o ckpt.o evt.o lck.o msg.o cfg.o tmr.o $(AMF_OBJS)
 
 override CFLAGS += -fPIC
 
 all: openaisparser.lcrso openaisserviceenable.lcrso service_amf.lcrso \
 	service_clm.lcrso service_ckpt.lcrso service_evt.lcrso service_lck.lcrso \
-	service_msg.lcrso openais-instantiate
+	service_msg.lcrso service_tmr.lcrso openais-instantiate
 
 ifeq (${OPENAIS_COMPAT}, DARWIN)
 
@@ -89,6 +89,9 @@
 service_cfg.lcrso: cfg.o
 	$(CC) $(LDFLAGS) -bundle $(LDFLAGS) -bundle_loader $(COROSYNC_BIN) -bind_at_load cfg.o -o $@
 
+service_tmr.lcrso: tmr.o
+	$(CC) $(LDFLAGS) -bundle $(LDFALGS) -bundle_loader $(COROSYNC_BIN) -bind_at_load tmr.o -o $@
+
 openaisparser.lcrso: openaisparser.o
 	$(CC) $(LDFLAGS) -bundle $(LDFLAGS) -bundle_loader $(COROSYNC_BIN) -bind_at_load openaisparser.o -o $@
 
@@ -122,6 +125,9 @@
 service_cfg.lcrso: cfg.o
 	$(CC) -shared -Wl,-soname,service_cfg.lcrso cfg.o -o $@
 
+service_tmr.lcrso: tmr.o
+	$(CC) -shared -Wl,-soname,service_tmr.lcrso tmr.o -o $@
+
 openaisparser.lcrso: openaisparser.o
 	$(CC) -shared -Wl,-soname,openaisparser.lcrso openaisparser.o -o $@
 
@@ -168,5 +174,8 @@
 msg.o: msg.c
 	$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
 
+tmr.o: tmr.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
+
 openaisparser.o: openaisparser.c
 	$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
Index: lib/tmr.c
===================================================================
--- lib/tmr.c	(revision 0)
+++ lib/tmr.c	(revision 0)
@@ -0,0 +1,851 @@
+/*
+ * Copyright (c) 2008 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Ryan O'Hara (rohara at redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the MontaVista Software, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/un.h>
+
+#include <saAis.h>
+#include <saTmr.h>
+
+#include <corosync/list.h>
+#include <corosync/ipc_gen.h>
+
+#include "../include/ipc_tmr.h"
+#include "util.h"
+
+struct message_overlay {
+	mar_res_header_t header __attribute__((aligned(8)));
+	char data[4096];
+};
+
+struct tmrInstance {
+	int response_fd;
+	int dispatch_fd;
+	SaTmrCallbacksT callbacks;
+	int finalize;
+	SaTmrHandleT tmrHandle;
+	pthread_mutex_t response_mutex;
+	pthread_mutex_t dispatch_mutex;
+};
+
+struct tmrTimerIdInstance {
+	int response_fd;
+	int dispatch_fd;
+	SaTmrHandleT tmrHandle;
+	struct list_head list;
+	void *timer_lock;
+	pthread_mutex_t *response_mutex;
+	pthread_mutex_t *dispatch_mutex;
+};
+
+void tmrHandleInstanceDestructor (void *instance);
+void tmrTimerIdHandleInstanceDestructor (void *instance);
+
+static SaTimeT tmrGetTimeNow (void);
+
+static struct saHandleDatabase tmrHandleDatabase = {
+	.handleCount			= 0,
+	.handles			= 0,
+	.mutex				= PTHREAD_MUTEX_INITIALIZER,
+	.handleInstanceDestructor	= tmrHandleInstanceDestructor
+};
+
+static struct saHandleDatabase tmrTimerIdHandleDatabase = {
+	.handleCount			= 0,
+	.handles			= 0,
+	.mutex				= PTHREAD_MUTEX_INITIALIZER,
+	.handleInstanceDestructor	= tmrTimerIdHandleInstanceDestructor
+};
+
+static SaVersionT tmrVersionsSupported[] = {
+	{ 'A', 1, 1 }
+};
+
+static struct saVersionDatabase tmrVersionDatabase = {
+	sizeof (tmrVersionsSupported) / sizeof (SaVersionT),
+	tmrVersionsSupported
+};
+
+void tmrHandleInstanceDestructor (void *instance)
+{
+	struct tmrInstance *tmrInstance = instance;
+
+	pthread_mutex_destroy (&tmrInstance->response_mutex);
+	pthread_mutex_destroy (&tmrInstance->dispatch_mutex);
+}
+
+void tmrTimerIdHandleInstanceDestructor (void *instance)
+{
+	return;
+}
+
+#ifdef COMPILE_OUT
+static void tmrInstanceFinalize (struct tmrInstance *tmrInstance)
+{
+	return;
+}
+#endif /* COMPILE_OUT */
+
+SaAisErrorT
+saTmrInitialize (
+	SaTmrHandleT *tmrHandle,
+	const SaTmrCallbacksT *timerCallbacks,
+	SaVersionT *version)
+{
+	struct tmrInstance *tmrInstance;
+	SaAisErrorT error = SA_AIS_OK;
+
+	/* DEBUG */
+	printf ("[DEBUG]: saTmrInitialize\n");
+
+	if (tmrHandle == NULL) {
+		return (SA_AIS_ERR_INVALID_PARAM);
+	}
+
+	error = saVersionVerify (&tmrVersionDatabase, version);
+	if (error != SA_AIS_OK) {
+		goto error_no_destroy;
+	}
+
+	error = saHandleCreate (&tmrHandleDatabase, sizeof (struct tmrInstance), tmrHandle);
+	if (error != SA_AIS_OK) {
+		goto error_no_destroy;
+	}
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, *tmrHandle, (void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		goto error_destroy;
+	}
+
+	tmrInstance->response_fd = -1;
+
+	error = saServiceConnect (
+		&tmrInstance->response_fd,
+		&tmrInstance->dispatch_fd,
+		TMR_SERVICE);
+	if (error != SA_AIS_OK) {
+		goto error_put_destroy;
+	}
+
+	if (timerCallbacks != NULL) {
+		memcpy (&tmrInstance->callbacks, timerCallbacks, sizeof (SaTmrCallbacksT));
+	} else {
+		memset (&tmrInstance->callbacks, 0, sizeof (SaTmrCallbacksT));
+	}
+
+	tmrInstance->tmrHandle = *tmrHandle;
+
+	pthread_mutex_init (&tmrInstance->response_mutex, NULL);
+
+	saHandleInstancePut (&tmrHandleDatabase, *tmrHandle);
+
+	return (SA_AIS_OK);
+
+error_put_destroy:
+	saHandleInstancePut (&tmrHandleDatabase, *tmrHandle);
+error_destroy:
+	saHandleDestroy (&tmrHandleDatabase, *tmrHandle);
+error_no_destroy:
+	return (error);
+}
+
+SaAisErrorT
+saTmrSelectionObjectGet (
+	SaTmrHandleT tmrHandle,
+	SaSelectionObjectT *selectionObject)
+{
+	struct tmrInstance *tmrInstance;
+	SaAisErrorT error = SA_AIS_OK;
+
+	/* DEBUG */
+	printf ("[DEBUG]: saTmrSelectionObjectGet\n");
+
+	if (selectionObject == NULL) {
+		return (SA_AIS_ERR_INVALID_PARAM);
+	}
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, tmrHandle, (void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		return (error);
+	}
+
+	*selectionObject = tmrInstance->dispatch_fd;
+
+	saHandleInstancePut (&tmrHandleDatabase, tmrHandle);
+
+	return (SA_AIS_OK);
+}
+
+SaAisErrorT
+saTmrDispatch (
+	SaTmrHandleT tmrHandle,
+	SaDispatchFlagsT dispatchFlags)
+{
+	SaTmrCallbacksT callbacks;
+	SaAisErrorT error = SA_AIS_OK;
+	struct tmrInstance *tmrInstance;
+	struct message_overlay dispatch_data;
+	struct pollfd ufds;
+	int dispatch_avail;
+	int poll_fd;
+	int timeout = 1;
+	int cont = 1;
+
+	struct res_lib_tmr_timerexpiredcallback *res_lib_tmr_timerexpiredcallback;
+
+	if (dispatchFlags != SA_DISPATCH_ONE &&
+	    dispatchFlags != SA_DISPATCH_ALL &&
+	    dispatchFlags != SA_DISPATCH_BLOCKING)
+	{
+		return (SA_AIS_ERR_INVALID_PARAM);
+	}
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, tmrHandle,
+		(void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		goto error_exit;
+	}
+
+	if (dispatchFlags == SA_DISPATCH_ALL) {
+		timeout = 0;
+	}
+
+	do {
+		poll_fd = tmrInstance->dispatch_fd;
+		ufds.fd = poll_fd;
+		ufds.events = POLLIN;
+		ufds.revents = 0;
+
+		error = saPollRetry (&ufds, 1, timeout);
+		if (error != SA_AIS_OK) {
+			goto error_put;
+		}
+		pthread_mutex_lock (&tmrInstance->dispatch_mutex);
+
+		if (tmrInstance->finalize == 1) {
+			error = SA_AIS_OK;
+			goto error_unlock;
+		}
+
+		if ((ufds.revents & (POLLERR|POLLHUP|POLLNVAL)) != 0) {
+			error = SA_AIS_ERR_BAD_HANDLE;
+			goto error_unlock;
+		}
+
+		dispatch_avail = (ufds.revents & POLLIN);
+
+		if (dispatch_avail == 0 && dispatchFlags == SA_DISPATCH_ALL) {
+			pthread_mutex_unlock (&tmrInstance->dispatch_mutex);
+			break;
+		}
+		else if (dispatch_avail == 0) {
+			pthread_mutex_unlock (&tmrInstance->dispatch_mutex);
+			continue;
+		}
+
+		memset (&dispatch_data, 0, sizeof (struct message_overlay));
+
+		error = saRecvRetry (tmrInstance->dispatch_fd,
+			&dispatch_data.header, sizeof (mar_res_header_t));
+		if (error != SA_AIS_OK) {
+			goto error_unlock;
+		}
+
+		if (dispatch_data.header.size > sizeof (mar_res_header_t)) {
+			error = saRecvRetry (tmrInstance->dispatch_fd,
+				&dispatch_data.data,
+				(dispatch_data.header.size - sizeof (mar_res_header_t)));
+			if (error != SA_AIS_OK) {
+				goto error_unlock;
+			}
+		}
+
+		memcpy (&callbacks, &tmrInstance->callbacks,
+			sizeof (tmrInstance->callbacks));
+
+		pthread_mutex_unlock (&tmrInstance->dispatch_mutex);
+
+		/* DEBUG */
+		printf ("[DEBUG]: saTmrDispatch { id = %d }\n",
+			dispatch_data.header.id);
+
+		switch (dispatch_data.header.id)
+		{
+		case MESSAGE_RES_TMR_TIMEREXPIREDCALLBACK:
+			if (callbacks.saTmrTimerExpiredCallback == NULL) {
+				continue;
+			}
+
+			res_lib_tmr_timerexpiredcallback =
+				(struct res_lib_tmr_timerexpiredcallback *) &dispatch_data;
+
+			callbacks.saTmrTimerExpiredCallback (
+				res_lib_tmr_timerexpiredcallback->timer_id,
+				res_lib_tmr_timerexpiredcallback->timer_data,
+				res_lib_tmr_timerexpiredcallback->expiration_count);
+
+			break;
+		default:
+			break;
+		}
+
+		switch (dispatchFlags)
+		{
+		case SA_DISPATCH_ONE:
+			cont = 0;
+			break;
+		case SA_DISPATCH_ALL:
+			break;
+		case SA_DISPATCH_BLOCKING:
+			break;
+		}
+	} while (cont);
+
+error_unlock:
+	pthread_mutex_unlock (&tmrInstance->dispatch_mutex);
+error_put:
+	saHandleInstancePut (&tmrHandleDatabase, tmrHandle);
+error_exit:
+	return (error);
+}
+
+SaAisErrorT
+saTmrFinalize (
+	SaTmrHandleT tmrHandle)
+{
+	struct tmrInstance *tmrInstance;
+	SaAisErrorT error = SA_AIS_OK;
+
+	/* DEBUG */
+	printf ("[DEBUG]: saTmrFinalize\n");
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, tmrHandle, (void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		return (error);
+	}
+
+	pthread_mutex_lock (&tmrInstance->response_mutex);
+
+	if (tmrInstance->finalize) {
+		pthread_mutex_unlock (&tmrInstance->response_mutex);
+		saHandleInstancePut (&tmrHandleDatabase, tmrHandle);
+		return (SA_AIS_ERR_BAD_HANDLE);
+	}
+
+	tmrInstance->finalize = 1;
+
+	pthread_mutex_unlock (&tmrInstance->response_mutex);
+
+	/* tmrInstanceFinalize (tmrInstance); */
+
+	if (tmrInstance->response_fd != -1) {
+		shutdown (tmrInstance->response_fd, 0);
+		close (tmrInstance->response_fd);
+	}
+
+	if (tmrInstance->dispatch_fd != -1) {
+		shutdown (tmrInstance->dispatch_fd, 0);
+		close (tmrInstance->dispatch_fd);
+	}
+
+	saHandleInstancePut (&tmrHandleDatabase, tmrHandle);
+
+	return (SA_AIS_OK);
+}
+
+SaAisErrorT
+saTmrTimerStart (
+	SaTmrHandleT tmrHandle,
+	const SaTmrTimerAttributesT *timerAttributes,
+	const void *timerData,
+	SaTmrTimerIdT *timerId,
+	SaTimeT *callTime)
+{
+	struct tmrInstance *tmrInstance;
+	struct tmrTimerIdInstance *tmrTimerIdInstance;
+	SaAisErrorT error = SA_AIS_OK;
+
+	struct req_lib_tmr_timerstart req_lib_tmr_timerstart;
+	struct res_lib_tmr_timerstart res_lib_tmr_timerstart;
+
+	/* DEBUG */
+	printf ("[DEBUG]: saTmrTimerStart\n");
+
+	if (timerAttributes == NULL) {
+		return (SA_AIS_ERR_INVALID_PARAM);
+	}
+
+	if ((timerAttributes->type != SA_TIME_ABSOLUTE) &&
+	    (timerAttributes->type != SA_TIME_DURATION)) {
+		return (SA_AIS_ERR_INVALID_PARAM);
+	}
+
+	/* DEBUG */
+	printf ("[DEBUG]:\t type=%d expire=%"PRId64" duration=%"PRId64"\n",
+		timerAttributes->type,
+		timerAttributes->initialExpirationTime,
+		timerAttributes->timerPeriodDuration);
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, tmrHandle, (void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		return (error);
+	}
+
+	error = saHandleCreate (&tmrTimerIdHandleDatabase,
+		sizeof (struct tmrTimerIdInstance), timerId);
+	if (error != SA_AIS_OK) {
+		goto error_exit;
+	}
+
+	error = saHandleInstanceGet (&tmrTimerIdHandleDatabase, *timerId,
+		(void *)&tmrTimerIdInstance);
+	if (error != SA_AIS_OK) {
+		goto error_destroy;
+	}
+
+	*callTime = tmrGetTimeNow ();
+
+	tmrTimerIdInstance->response_fd = tmrInstance->response_fd;
+	tmrTimerIdInstance->response_mutex = &tmrInstance->response_mutex;
+	tmrTimerIdInstance->tmrHandle = tmrHandle;
+
+	req_lib_tmr_timerstart.header.size =
+		sizeof (struct req_lib_tmr_timerstart);
+	req_lib_tmr_timerstart.header.id =
+		MESSAGE_REQ_TMR_TIMERSTART;
+
+	req_lib_tmr_timerstart.timer_id = *timerId;
+	req_lib_tmr_timerstart.call_time = *callTime;
+
+	memcpy (&req_lib_tmr_timerstart.timer_attributes,
+		timerAttributes, sizeof (SaTmrTimerAttributesT));
+
+	pthread_mutex_lock (&tmrInstance->response_mutex);
+
+	error = saSendReceiveReply (tmrInstance->response_fd,
+		&req_lib_tmr_timerstart,
+		sizeof (struct req_lib_tmr_timerstart),
+		&res_lib_tmr_timerstart,
+		sizeof (struct res_lib_tmr_timerstart));
+
+	pthread_mutex_unlock (&tmrInstance->response_mutex);
+
+	if (res_lib_tmr_timerstart.header.error != SA_AIS_OK) {
+		error = res_lib_tmr_timerstart.header.error;
+	}
+
+error_destroy:
+	saHandleDestroy (&tmrTimerIdHandleDatabase, *timerId);
+
+error_exit:
+	saHandleInstancePut (&tmrHandleDatabase, tmrHandle);
+	return (error);;
+}
+
+SaAisErrorT
+saTmrTimerReschedule (
+	SaTmrHandleT tmrHandle,
+	SaTmrTimerIdT timerId,
+	const SaTmrTimerAttributesT *timerAttributes,
+	SaTimeT *callTime)
+{
+	struct tmrInstance *tmrInstance;
+	SaAisErrorT error = SA_AIS_OK;
+
+	struct req_lib_tmr_timerreschedule req_lib_tmr_timerreschedule;
+	struct res_lib_tmr_timerreschedule res_lib_tmr_timerreschedule;
+
+	/* DEBUG */
+	printf ("[DEBUG]: saTmrTimerReschedule\n");
+
+	if (timerAttributes == NULL) {
+		return (SA_AIS_ERR_INVALID_PARAM);
+	}
+
+	/* DEBUG */
+	printf ("[DEBUG]:\t type=%d expire=%"PRId64" duration=%"PRId64"\n",
+		timerAttributes->type,
+		timerAttributes->initialExpirationTime,
+		timerAttributes->timerPeriodDuration);
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, tmrHandle, (void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		return (error);
+	}
+
+	req_lib_tmr_timerreschedule.header.size =
+		sizeof (struct req_lib_tmr_timerreschedule);
+	req_lib_tmr_timerreschedule.header.id =
+		MESSAGE_REQ_TMR_TIMERRESCHEDULE;
+
+	req_lib_tmr_timerreschedule.timer_id = timerId;
+
+	memcpy (&req_lib_tmr_timerreschedule.timer_attributes,
+		timerAttributes, sizeof (SaTmrTimerAttributesT));
+
+	pthread_mutex_lock (&tmrInstance->response_mutex);
+
+	error = saSendReceiveReply (tmrInstance->response_fd,
+		&req_lib_tmr_timerreschedule,
+		sizeof (struct req_lib_tmr_timerreschedule),
+		&res_lib_tmr_timerreschedule,
+		sizeof (struct res_lib_tmr_timerreschedule));
+
+	pthread_mutex_unlock (&tmrInstance->response_mutex);
+
+	if (res_lib_tmr_timerreschedule.header.error != SA_AIS_OK) {
+		error = res_lib_tmr_timerreschedule.header.error;
+	}
+
+	saHandleInstancePut (&tmrHandleDatabase, tmrHandle);
+
+	return (error);
+}
+
+SaAisErrorT
+saTmrTimerCancel (
+	SaTmrHandleT tmrHandle,
+	SaTmrTimerIdT timerId,
+	void **timerDataP)
+{
+	struct tmrInstance *tmrInstance;
+	SaAisErrorT error = SA_AIS_OK;
+
+	struct req_lib_tmr_timercancel req_lib_tmr_timercancel;
+	struct res_lib_tmr_timercancel res_lib_tmr_timercancel;
+
+	/* DEBUG */
+	printf ("[DEBUG]: saTmrTimerCancel\n");
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, tmrHandle, (void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		goto error_exit;
+	}
+
+	req_lib_tmr_timercancel.header.size =
+		sizeof (struct req_lib_tmr_timercancel);
+	req_lib_tmr_timercancel.header.id =
+		MESSAGE_REQ_TMR_TIMERCANCEL;
+
+	req_lib_tmr_timercancel.timer_id = timerId;
+
+	pthread_mutex_lock (&tmrInstance->response_mutex);
+
+	error = saSendReceiveReply (tmrInstance->response_fd,
+		&req_lib_tmr_timercancel,
+		sizeof (struct req_lib_tmr_timercancel),
+		&res_lib_tmr_timercancel,
+		sizeof (struct res_lib_tmr_timercancel));
+
+	pthread_mutex_unlock (&tmrInstance->response_mutex);
+
+	if (res_lib_tmr_timercancel.header.error != SA_AIS_OK) {
+		error = res_lib_tmr_timercancel.header.error;
+	}
+
+error_exit:
+	return (error);
+}
+
+SaAisErrorT
+saTmrPeriodicTimerSkip (
+	SaTmrHandleT tmrHandle,
+	SaTmrTimerIdT timerId)
+{
+	struct tmrInstance *tmrInstance;
+	SaAisErrorT error = SA_AIS_OK;
+
+	struct req_lib_tmr_periodictimerskip req_lib_tmr_periodictimerskip;
+	struct res_lib_tmr_periodictimerskip res_lib_tmr_periodictimerskip;
+
+	/* DEBUG */
+	printf ("[DEBUG]: saTmrPeriodicTimerSkip\n");
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, tmrHandle, (void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		return (error);
+	}
+
+	req_lib_tmr_periodictimerskip.header.size =
+		sizeof (struct req_lib_tmr_periodictimerskip);
+	req_lib_tmr_periodictimerskip.header.id =
+		MESSAGE_REQ_TMR_PERIODICTIMERSKIP;
+
+	req_lib_tmr_periodictimerskip.timer_id = timerId;
+
+	pthread_mutex_lock (&tmrInstance->response_mutex);
+
+	error = saSendReceiveReply (tmrInstance->response_fd,
+		&req_lib_tmr_periodictimerskip,
+		sizeof (struct req_lib_tmr_periodictimerskip),
+		&res_lib_tmr_periodictimerskip,
+		sizeof (struct res_lib_tmr_periodictimerskip));
+
+	pthread_mutex_unlock (&tmrInstance->response_mutex);
+
+	if (res_lib_tmr_periodictimerskip.header.error != SA_AIS_OK) {
+		error = res_lib_tmr_periodictimerskip.header.error;
+	}
+
+	return (error);
+}
+
+SaAisErrorT
+saTmrTimerRemainingTimeGet (
+	SaTmrHandleT tmrHandle,
+	SaTmrTimerIdT timerId,
+	SaTimeT *remainingTime)
+{
+	struct tmrInstance *tmrInstance;
+	SaAisErrorT error = SA_AIS_OK;
+
+	struct req_lib_tmr_timerremainingtimeget req_lib_tmr_timerremainingtimeget;
+	struct res_lib_tmr_timerremainingtimeget res_lib_tmr_timerremainingtimeget;
+
+	/* DEBUG */
+	printf ("[DEBUG]: saTimerRemainingTimeGet\n");
+
+	if (remainingTime == NULL) {
+		error = SA_AIS_ERR_INVALID_PARAM;
+		goto error_exit;
+	}
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, tmrHandle, (void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		return (error);
+	}
+
+	req_lib_tmr_timerremainingtimeget.header.size =
+		sizeof (struct req_lib_tmr_timerremainingtimeget);
+	req_lib_tmr_timerremainingtimeget.header.id =
+		MESSAGE_REQ_TMR_TIMERREMAININGTIMEGET;
+
+	req_lib_tmr_timerremainingtimeget.timer_id = timerId;
+
+	pthread_mutex_lock (&tmrInstance->response_mutex);
+
+	error = saSendReceiveReply (tmrInstance->response_fd,
+		&req_lib_tmr_timerremainingtimeget,
+		sizeof (struct req_lib_tmr_timerremainingtimeget),
+		&res_lib_tmr_timerremainingtimeget,
+		sizeof (struct res_lib_tmr_timerremainingtimeget));
+
+	pthread_mutex_unlock (&tmrInstance->response_mutex);
+
+	if (res_lib_tmr_timerremainingtimeget.header.error != SA_AIS_OK) {
+		error = res_lib_tmr_timerremainingtimeget.header.error;
+	}
+
+	return (error);
+}
+
+SaAisErrorT
+saTmrTimerAttributesGet (
+	SaTmrHandleT tmrHandle,
+	SaTmrTimerIdT timerId,
+	SaTmrTimerAttributesT *timerAttributes)
+{
+	struct tmrInstance *tmrInstance;
+	SaAisErrorT error = SA_AIS_OK;
+
+	struct req_lib_tmr_timerattributesget req_lib_tmr_timerattributesget;
+	struct res_lib_tmr_timerattributesget res_lib_tmr_timerattributesget;
+
+	/* DEBUG */
+	printf ("[DEBUG]: saTmrTimerAttributesGet\n");
+
+	if (timerAttributes == NULL) {
+		error = SA_AIS_ERR_INVALID_PARAM;
+		goto error_exit;
+	}
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, tmrHandle, (void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		goto error_exit;
+	}
+
+	req_lib_tmr_timerattributesget.header.size =
+		sizeof (struct req_lib_tmr_timerattributesget);
+	req_lib_tmr_timerattributesget.header.id =
+		MESSAGE_REQ_TMR_TIMERATTRIBUTESGET;
+
+	req_lib_tmr_timerattributesget.timer_id = timerId;
+
+	pthread_mutex_lock (&tmrInstance->response_mutex);
+
+	error = saSendReceiveReply (tmrInstance->response_fd,
+		&req_lib_tmr_timerattributesget,
+		sizeof (struct req_lib_tmr_timerattributesget),
+		&res_lib_tmr_timerattributesget,
+		sizeof (struct res_lib_tmr_timerattributesget));
+
+	pthread_mutex_unlock (&tmrInstance->response_mutex);
+
+	if (res_lib_tmr_timerattributesget.header.error != SA_AIS_OK) {
+		error = res_lib_tmr_timerattributesget.header.error;
+	}
+
+	memcpy (timerAttributes, &res_lib_tmr_timerattributesget.timer_attributes,
+		sizeof (SaTmrTimerAttributesT));
+
+error_exit:
+	return (error);
+}
+
+SaAisErrorT
+saTmrTimeGet (
+	SaTmrHandleT tmrHandle,
+	SaTimeT *currentTime)
+{
+	struct tmrInstance *tmrInstance;
+	SaAisErrorT error = SA_AIS_OK;
+
+	struct req_lib_tmr_timeget req_lib_tmr_timeget;
+	struct res_lib_tmr_timeget res_lib_tmr_timeget;
+
+	/* DEBUG */
+	printf ("[DEBUG]: saTmrTimeGet\n");
+
+	if (currentTime == NULL) {
+		error = SA_AIS_ERR_INVALID_PARAM;
+		goto error_exit;
+	}
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, tmrHandle, (void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		goto error_exit;
+	}
+
+	req_lib_tmr_timeget.header.size =
+		sizeof (struct req_lib_tmr_timeget);
+	req_lib_tmr_timeget.header.id =
+		MESSAGE_REQ_TMR_TIMEGET;
+
+	pthread_mutex_lock (&tmrInstance->response_mutex);
+
+	error = saSendReceiveReply (tmrInstance->response_fd,
+		&req_lib_tmr_timeget,
+		sizeof (struct req_lib_tmr_timeget),
+		&res_lib_tmr_timeget,
+		sizeof (struct res_lib_tmr_timeget));
+
+	pthread_mutex_unlock (&tmrInstance->response_mutex);
+
+	if (res_lib_tmr_timeget.header.error != SA_AIS_OK) {
+		error = res_lib_tmr_timeget.header.error;
+	}
+
+	memcpy (currentTime, &res_lib_tmr_timeget.current_time,
+		sizeof (SaTimeT));
+
+error_exit:
+	return (error);
+}
+
+SaAisErrorT
+saTmrClockTickGet (
+	SaTmrHandleT tmrHandle,
+	SaTimeT *clockTick)
+{
+	struct tmrInstance *tmrInstance;
+	SaAisErrorT error = SA_AIS_OK;
+
+	struct req_lib_tmr_clocktickget req_lib_tmr_clocktickget;
+	struct res_lib_tmr_clocktickget res_lib_tmr_clocktickget;
+
+	/* DEBUG */
+	printf ("[DEBUG]: saTmrClockTickGet\n");
+
+	if (clockTick == NULL) {
+		error = SA_AIS_ERR_INVALID_PARAM;
+		goto error_exit;
+	}
+
+	error = saHandleInstanceGet (&tmrHandleDatabase, tmrHandle, (void *)&tmrInstance);
+	if (error != SA_AIS_OK) {
+		goto error_exit;
+	}
+
+	req_lib_tmr_clocktickget.header.size =
+		sizeof (struct req_lib_tmr_clocktickget);
+	req_lib_tmr_clocktickget.header.id =
+		MESSAGE_REQ_TMR_CLOCKTICKGET;
+
+	pthread_mutex_lock (&tmrInstance->response_mutex);
+
+	error = saSendReceiveReply (tmrInstance->response_fd,
+		&req_lib_tmr_clocktickget,
+		sizeof (struct req_lib_tmr_clocktickget),
+		&res_lib_tmr_clocktickget,
+		sizeof (struct res_lib_tmr_clocktickget));
+
+	pthread_mutex_unlock (&tmrInstance->response_mutex);
+
+	if (res_lib_tmr_clocktickget.header.error != SA_AIS_OK) {
+		error = res_lib_tmr_clocktickget.header.error;
+	}
+
+	memcpy (clockTick, &res_lib_tmr_clocktickget.clock_tick,
+		sizeof (SaTimeT));
+
+error_exit:
+	return (error);
+}
+
+static SaTimeT tmrGetTimeNow (void)
+{
+	struct timeval tv;
+	SaTimeT time;
+
+	if (gettimeofday (&tv, 0)) {
+		return (0ULL);
+	}
+
+	time = (SaTimeT)(tv.tv_sec) *  1000000000ULL;
+	time += (SaTimeT)(tv.tv_usec) * 1000ULL;
+
+	return (time);
+}
Index: lib/libSaTmr.versions
===================================================================
--- lib/libSaTmr.versions	(revision 0)
+++ lib/libSaTmr.versions	(revision 0)
@@ -0,0 +1,32 @@
+# Version and symbol export for libSaTmr.so
+
+OPENAIS_TMR_A.01.01 {
+	global:
+		saTmrInitialize;
+		saTmrSelectionObjectGet;
+		saTmrDispatch;
+		saLckFinalize;
+		saTmrTimerStart;
+		saTmrTimerReschedule;
+		saTmrTimerCancel;
+		saTmrPeriodicTimerSkip;
+		saTmrTimerRemainingTimeGet;
+		saTmrTimerAttributesGet;
+		saTmrTimeGet;
+		saTmrClockTickGet;
+
+	local:
+		saHandleCreate;
+		saHandleDestroy;
+		saHandleInstanceGet;
+		saHandleInstancePut;
+		saPollRetry;
+		saRecvRetry;
+		saSendMsgReceiveReply;
+		saSendMsgRetry;
+		saSendReceiveReply;
+		saSendRetry;
+		saServiceConnect;
+		saVersionVerify;
+		clustTimeNow;
+};
Index: lib/Makefile
===================================================================
--- lib/Makefile	(revision 1694)
+++ lib/Makefile	(working copy)
@@ -43,7 +43,8 @@
 	libSaAmf.a libSaAmf.so.2.0.0 \
 	libSaEvt.a libSaEvt.so.2.0.0 \
 	libSaMsg.a libSaMsg.so.2.0.0  \
-	libSaLck.a libSaLck.so.2.0.0 
+	libSaLck.a libSaLck.so.2.0.0 \
+	libSaTmr.a libSaTmr.so.2.0.0
 
 libcoroutil.a: util.o
 	$(AR) -rc libcoroutil.a util.o
@@ -70,6 +71,9 @@
 libSaLck.so.2.0.0: util.o lck.o
 	$(CC) $(DARWIN_OPTS) util.o lck.o -o $@
 
+libSaTmr.so.2.0.0: util.o tmr.o
+	$(CC) $(DARWIN_OPTS) util.o tmr.o -o $@
+
 else
 
 libSaAmf.so.2.0.0: util.o amf.o
@@ -90,6 +94,9 @@
 libSaLck.so.2.0.0: util.o lck.o
 	$(CC) $(LDFLAGS) -shared -Wl,-soname,libSaLck.so.2,-version-script=$(srcdir)$(subdir)libSaLck.versions util.o lck.o -o $@
 
+libSaTmr.so.2.0.0: util.o tmr.o
+	$(CC) $(LDFLAGS) -shared -Wl,-soname,libSaTmr.so.2,-version-script=$(srcdir)$(subdir)libSaTmr.versions util.o tmr.o -o $@
+
 endif
 
 libSaAmf.a: util.o amf.o
@@ -110,9 +117,12 @@
 libSaLck.a: util.o lck.o
 	$(AR) -rc libSaLck.a util.o lck.o
 
+libSaTmr.a: util.o tmr.o
+	$(AR) -rc libSaTmr.a util.o tmr.o
+
 clean:
 	rm -f *.o libSa*.so* libSa*.a *.da *.bb *.bbg
- 
+
 # -fPIC rules required for all libraries
 %.o: %.c
 	$(CC) $(CFLAGS) $(CPPFLAGS) -fPIC -c -o $@ $<
Index: Makefile
===================================================================
--- Makefile	(revision 1694)
+++ Makefile	(working copy)
@@ -92,10 +92,10 @@
 	(cd $(builddir)pkgconfig; echo ==== `pwd` ===; $(call sub_make,pkgconfig,clean));
 	rm -rf $(builddir)doc/api
 
-AIS_LIBS	= SaAmf SaClm SaCkpt SaEvt SaLck SaMsg
+AIS_LIBS	= SaAmf SaClm SaCkpt SaEvt SaLck SaMsg SaTmr
 
 AIS_HEADERS	= saAis.h saAmf.h saClm.h saCkpt.h saEvt.h saEvt.h saLck.h \
-		  saMsg.h
+		  saMsg.h saTmr.h
 
 install: all
 	mkdir -p $(DESTDIR)$(SBINDIR)


More information about the Openais mailing list