Hi Anders. Ack with minor comments.
* Its very likely that user may pass some invalid value (say.. fd value) for which the current implementation is doing osaf_abort(). Why it can't be considered error with return of "EINVAL". * Better to document that the following function won't allow periodic expiration notifications. Otherwise, there is a possibility of user setting /utmr->it_interval.tv_(n)sec/ to non-zero. /void osaf_timerfd_settime(int ufd, int flags,// // const struct itimerspec* utmr,// // struct itimerspec* otmr)// //{// // if ((flags != 0 && flags != OSAF_TFD_TIMER_ABSTIME) ||// // utmr->it_interval.tv_sec != 0 ||// // utmr->it_interval.tv_nsec != 0) {/ Thanks and Regards. Ramesh. On 12/22/2015 9:33 PM, Anders Widell wrote: > osaf/libs/core/common/Makefile.am | 1 + > osaf/libs/core/common/include/Makefile.am | 1 + > osaf/libs/core/common/include/osaf_timerfd.h | 131 +++++++++++++ > osaf/libs/core/common/osaf_timerfd.c | 264 > +++++++++++++++++++++++++++ > 4 files changed, 397 insertions(+), 0 deletions(-) > > > Add the utility functions osaf_timerfd_create(), osaf_timerfd_settime(), > osaf_timerfd_gettime() and osaf_timerfd_close(), which have functionality > corresponding to the Linux functions timerfd_create(), timerfd_settime(), > timerfd_gettime() and close(), respectively. > > The main reason for implementing these functions here is that they are missing > in LSB (Linux Standard Base), which means that the use these Linux functions > are > currently prohibited in OpenSAF. As an additional benefit, the variants > implemented here can never fail (they will abort() on failure), which means > that > the user does not have implement code for error handling. > > diff --git a/osaf/libs/core/common/Makefile.am > b/osaf/libs/core/common/Makefile.am > --- a/osaf/libs/core/common/Makefile.am > +++ b/osaf/libs/core/common/Makefile.am > @@ -34,6 +34,7 @@ libopensaf_common_la_SOURCES = \ > osaf_utility.c \ > osaf_poll.c \ > osaf_time.c \ > + osaf_timerfd.c \ > osaf_extended_name.c \ > nid_start_util.c \ > saf_edu.c \ > diff --git a/osaf/libs/core/common/include/Makefile.am > b/osaf/libs/core/common/include/Makefile.am > --- a/osaf/libs/core/common/include/Makefile.am > +++ b/osaf/libs/core/common/include/Makefile.am > @@ -34,5 +34,6 @@ noinst_HEADERS = \ > osaf_unicode.h \ > osaf_poll.h \ > osaf_time.h \ > + osaf_timerfd.h \ > saf_error.h \ > osaf_secutil.h > diff --git a/osaf/libs/core/common/include/osaf_timerfd.h > b/osaf/libs/core/common/include/osaf_timerfd.h > new file mode 100644 > --- /dev/null > +++ b/osaf/libs/core/common/include/osaf_timerfd.h > @@ -0,0 +1,131 @@ > +/* -*- OpenSAF -*- > + * > + * (C) Copyright 2015 The OpenSAF Foundation > + * > + * 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. This file and program are licensed > + * under the GNU Lesser General Public License Version 2.1, February 1999. > + * The complete license can be accessed from the following location: > + * http://opensource.org/licenses/lgpl-license.php > + * See the Copying file included with the OpenSAF distribution for full > + * licensing terms. > + * > + * Author(s): Ericsson AB > + * > + */ > + > +/** @file > + * > + * This file contains an OpenSAF replacement of the Linux functions > + * timerfd_create(), timerfd_settime() and timerfd_gettime(). The main reason > + * for implementing these functions here is that they are missing in LSB > (Linux > + * Standard Base), which means that the use these Linux functions are > currently > + * prohibited in OpenSAF. As an additional benefit, the variants implemented > + * here can never fail (they will abort() on failure), which means that the > user > + * does not have implement code for error handling. > + * > + * When using the functions declared in this header file, you must be > consistent > + * and always use them (i.e. not mix the usage of these functions with the > Linux > + * conuterparts). Also, when using the flags parameter to > osaf_timerfd_create() > + * and osaf_timerfd_settime(), you must add the OSAF_ prefix to any flag > value > + * that you use, e.g. use OSAF_TFD_TIMER_ABSTIME instead of > TFD_TIMER_ABSTIME. > + * > + * Note that these functions are currently implemented using a linear search > + * algorithm to find the internal timer data structures. Therefore, they will > + * not be able to handle a large number of timers efficiently. The > + * reccomendation is to use a maximum of around ten timers per Linux > process. If > + * more timers are needed, then these functions should be improved to use a > more > + * efficient data structure. > + * > + * The definitions in this file are for internal use within OpenSAF only. > + */ > + > +#ifndef OPENSAF_BASE_OSAF_TIMERFD_H_ > +#define OPENSAF_BASE_OSAF_TIMERFD_H_ > + > +#include <time.h> > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +enum { > + OSAF_TFD_CLOEXEC = 0x80000, > + OSAF_TFD_NONBLOCK = 0x800 > +}; > + > +enum { > + OSAF_TFD_TIMER_ABSTIME = 0x1 > +}; > + > +#define OSAF_TFD_CLOEXEC OSAF_TFD_CLOEXEC > +#define OSAF_TFD_NONBLOCK OSAF_TFD_NONBLOCK > +#define OSAF_TFD_TIMER_ABSTIME OSAF_TFD_TIMER_ABSTIME > + > +/** > + * @brief Create a timer. > + * > + * This is a convenience function that behaves exactly like the Linux > function > + * timerfd_create(2), except that it will abort the process instead of > returning > + * an error code in case of a failure. Note that you must use the > + * osaf_timerfd_settime(), osaf_timerfd_gettime() and osaf_timerfd_close() > + * functions instead of the standard Linux functions timerfd_settime(), > + * timerfd_gettime() and close() for manipulating timers created using this > + * function. > + * > + * The @a flags parameter can be a bitwise OR of the two flags > OSAF_TFD_CLOEXEC > + * and OSAF_TFD_NONBLOCK, which have the meaning corresponding to the > + * TFD_CLOEXEC and TFD_NONBLOCK flags of timerfd_create(), respectively. > + * > + * The return value will always be greater than or equal to zero (i.e. this > + * function will never fail). To free the allocated timer, call > + * osaf_timerfd_close(). > + */ > +extern int osaf_timerfd_create(clockid_t clock_id, int flags) > + __attribute__ ((__nothrow__, __leaf__)); > + > +/** > + * @brief Start, stop or change the time-out value of a timer. > + * > + * This is a convenience function that behaves exactly like the Linux > function > + * timerfd_settime(2), except that it will abort the process instead of > + * returning an error code in case of a failure. Aslo, it currently has the > + * restriction that utmr->it_interval must be zero. This function can only be > + * used with timers created using the osaf_timerfd_create() function. > + * > + * The @a flags parameter can be either zero or have the value > + * OSAF_TFD_TIMER_ABSTIME, which has the meaning corresponding to the > + * TFD_TIMER_ABSTIME flag of the timerfd_settime() function. > + */ > +extern void osaf_timerfd_settime(int ufd, int flags, > + const struct itimerspec* utmr, > + struct itimerspec* otmr) > + __attribute__ ((__nothrow__, __leaf__)); > + > +/** > + * @brief Start, stop or change the time-out value of a timer. > + * > + * This is a convenience function that behaves exactly like the Linux > function > + * timerfd_gettime(2), except that it will abort the process instead of > + * returning an error code in case of a failure. This function can only be > used > + * with timers created using the osaf_timerfd_create() function. > + */ > +extern void osaf_timerfd_gettime(int ufd, struct itimerspec* otmr) > + __attribute__ ((__nothrow__, __leaf__)); > + > +/** > + * @brief Delete a timer. > + * > + * This function is a convenience function that behaves exactly like the > Linux > + * function close(2), except that it will abort the process instead of > returning > + * an error code in case of a failure. This function can only be used with > + * timers created using the osaf_timerfd_create() function. > + */ > +extern void osaf_timerfd_close(int ufd); > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif /* OPENSAF_BASE_OSAF_TIMERFD_H_ */ > diff --git a/osaf/libs/core/common/osaf_timerfd.c > b/osaf/libs/core/common/osaf_timerfd.c > new file mode 100644 > --- /dev/null > +++ b/osaf/libs/core/common/osaf_timerfd.c > @@ -0,0 +1,264 @@ > +/* -*- OpenSAF -*- > + * > + * (C) Copyright 2015 The OpenSAF Foundation > + * > + * 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. This file and program are licensed > + * under the GNU Lesser General Public License Version 2.1, February 1999. > + * The complete license can be accessed from the following location: > + * http://opensource.org/licenses/lgpl-license.php > + * See the Copying file included with the OpenSAF distribution for full > + * licensing terms. > + * > + * Author(s): Ericsson AB > + * > + */ > + > +#ifndef _GNU_SOURCE > +#define _GNU_SOURCE > +#endif > +#include "osaf_timerfd.h" > +#include <stdlib.h> > +#include <errno.h> > +#include <pthread.h> > +#include <stdint.h> > +#include <time.h> > +#include <signal.h> > +#include <unistd.h> > +#include <sys/types.h> > +#include <sys/socket.h> > +#include "osaf_utility.h" > +#include "osaf_time.h" > + > +// Define a few constants that are missing in the LSB compiler > +#ifndef MSG_DONTWAIT > +enum { > + MSG_DONTWAIT = 0x40 > +}; > +#define MSG_DONTWAIT MSG_DONTWAIT > +#endif > +#ifndef SOCK_CLOEXEC > +enum { > + SOCK_CLOEXEC = 0x80000 > +}; > +#define SOCK_CLOEXEC SOCK_CLOEXEC > +#endif > +#ifndef SOCK_NONBLOCK > +enum { > + SOCK_NONBLOCK = 0x800 > +}; > +#define SOCK_NONBLOCK SOCK_NONBLOCK > +#endif > +#ifndef sigev_notify_function > +#define sigev_notify_function _sigev_un._sigev_thread._function > +#endif > +#ifndef sigev_notify_attributes > +#define sigev_notify_attributes _sigev_un._sigev_thread._attribute > +#endif > + > +/* > + * A data structure that keeps information about allocated timers. > + */ > +struct Timer { > + /* > + * Seqence number of this timer. Used by the event handler as a key to > + * find the timer related to the event. If the timer has already been > + * deleted when the event handler is called, then the event is ignored. > + */ > + int sequence_no; > + /* > + * The file descriptor that we return to the user. Part of a socket > + * pair together with write_socket. > + */ > + int read_socket; > + /* > + * The opposite file descriptor to read_socket, which both form a > + * socket pair. We use this file descriptor for writing, to signal that > + * a timer has expired. > + */ > + int write_socket; > + /* > + * The timer that we have allocated using timer_create(). > + */ > + timer_t timer_id; > + /* > + * Next timer in the linked list pointed to by the variable timer_list. > + */ > + struct Timer* next_timer; > +}; > + > +static struct Timer* FindTimer(int read_socket); > +static void EventHandler(union sigval value); > + > +/* > + * Mutex that protects all the shared data (i.e. all other static variables > in > + * this file). > + */ > +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; > + > +/* > + * Pointer to the first entry in a single linked list of allocated Timer > + * structures. The member next_timer in the Timer structure points to the > next > + * Timer in the list. > + */ > +static struct Timer* timer_list = NULL; > + > +/* > + * Next seqence number to be used when allocating a new timer. The sequence > + * number will be stored in the member sequence_no and is used by the event > + * handler as a key for finding the timer related to the event. This > variable > + * is initialized to a randomly chosen integer. > + */ > +static int next_sequence_no = 2124226019; > + > +/* > + * Return a pointer to the timer whose read socket (the socket we return to > the > + * user) equals @a read_socket. > + */ > +static struct Timer* FindTimer(int read_socket) > +{ > + struct Timer* timer; > + for (timer = timer_list; timer != NULL; timer = timer->next_timer) { > + if (timer->read_socket == read_socket) return timer; > + } > + // Not found > + osaf_abort(read_socket); > +} > + > +/* > + * Event handler for the timers. This function will be called in a separate > + * thread when a timer has expired. The sequence number of the expired timer > is > + * found in the parameter value.sival_int, and is used as a key for looking > up > + * the timer in the single linked list timer_list. If the timer is not found > in > + * the list (since it has already been deleted), then the event will be > ignored. > + */ > +static void EventHandler(union sigval value) > +{ > + osaf_mutex_lock_ordie(&mutex); > + struct Timer* timer = timer_list; > + while (timer != NULL && timer->sequence_no != value.sival_int) { > + timer = timer->next_timer; > + } > + if (timer != NULL) { > + uint64_t expirations = 1; > + ssize_t result; > + do { > + result = send(timer->write_socket, > + &expirations, sizeof(expirations), > + MSG_DONTWAIT | MSG_NOSIGNAL); > + } while (result == -1 && errno == EINTR); > + if (result != sizeof(expirations)) osaf_abort(result); > + } > + osaf_mutex_unlock_ordie(&mutex); > +} > + > +int osaf_timerfd_create(clockid_t clock_id, int flags) > +{ > + // Validate input parameters, abort on error > + if ((clock_id != CLOCK_REALTIME && clock_id != CLOCK_MONOTONIC) || > + (flags & ~(int) (OSAF_TFD_NONBLOCK | OSAF_TFD_CLOEXEC)) != 0) { > + osaf_abort(flags); > + } > + > + int sockflags = SOCK_DGRAM; > + if ((flags & OSAF_TFD_NONBLOCK) == OSAF_TFD_NONBLOCK) { > + sockflags |= SOCK_NONBLOCK; > + } > + if ((flags & OSAF_TFD_CLOEXEC) == OSAF_TFD_CLOEXEC) { > + sockflags |= SOCK_CLOEXEC; > + } > + > + int sfd[2]; > + if (socketpair(AF_UNIX, sockflags, 0, sfd) != 0) osaf_abort(0); > + > + struct Timer* timer = (struct Timer*) malloc(sizeof(struct Timer)); > + if (timer == NULL) osaf_abort(0); > + osaf_mutex_lock_ordie(&mutex); > + timer->sequence_no = next_sequence_no++; > + osaf_mutex_unlock_ordie(&mutex); > + timer->read_socket = sfd[0]; > + timer->write_socket = sfd[1]; > + > + struct sigevent event; > + event.sigev_notify = SIGEV_THREAD; > + event.sigev_value.sival_int = timer->sequence_no; > + event.sigev_notify_function = EventHandler; > + event.sigev_notify_attributes = NULL; > + int result; > + for (;;) { > + result = timer_create(clock_id, &event, &timer->timer_id); > + if (result != -1 || errno != EAGAIN) break; > + static const struct timespec sleep_time = { 0, 10000000 }; > + osaf_nanosleep(&sleep_time); > + } > + if (result != 0) osaf_abort(clock_id); > + > + osaf_mutex_lock_ordie(&mutex); > + timer->next_timer = timer_list; > + timer_list = timer; > + osaf_mutex_unlock_ordie(&mutex); > + return sfd[0]; > +} > + > +void osaf_timerfd_settime(int ufd, int flags, > + const struct itimerspec* utmr, > + struct itimerspec* otmr) > +{ > + if ((flags != 0 && flags != OSAF_TFD_TIMER_ABSTIME) || > + utmr->it_interval.tv_sec != 0 || > + utmr->it_interval.tv_nsec != 0) { > + osaf_abort(flags); > + } > + > + osaf_mutex_lock_ordie(&mutex); > + struct Timer* timer = FindTimer(ufd); > + for (;;) { > + uint64_t expirations; > + if (recv(timer->read_socket, &expirations, > + sizeof(expirations), MSG_DONTWAIT) < 0) { > + if (errno == EINTR) continue; > + if (errno == EAGAIN || errno == EWOULDBLOCK) break; > + osaf_abort(timer->read_socket); > + } > + } > + if (timer_settime(timer->timer_id, > + flags == OSAF_TFD_TIMER_ABSTIME ? TIMER_ABSTIME : 0, > + utmr, > + otmr) != 0) { > + osaf_abort(flags); > + } > + osaf_mutex_unlock_ordie(&mutex); > +} > + > +void osaf_timerfd_gettime(int ufd, struct itimerspec* otmr) > +{ > + osaf_mutex_lock_ordie(&mutex); > + struct Timer* timer = FindTimer(ufd); > + if (timer_gettime(timer->timer_id, otmr) != 0) osaf_abort(ufd); > + osaf_mutex_unlock_ordie(&mutex); > +} > + > +void osaf_timerfd_close(int ufd) > +{ > + osaf_mutex_lock_ordie(&mutex); > + struct Timer** prev = &timer_list; > + struct Timer* timer; > + for (;;) { > + timer = *prev; > + if (timer == NULL) osaf_abort(ufd); > + if (timer->read_socket == ufd) { > + *prev = timer->next_timer; > + break; > + } > + prev = &timer->next_timer; > + } > + osaf_mutex_unlock_ordie(&mutex); > + > + if (timer_delete(timer->timer_id) != 0 || > + (close(timer->write_socket) != 0 && errno != EINTR) || > + (close(timer->read_socket) != 0 && errno != EINTR)) { > + osaf_abort(ufd); > + } > + free(timer); > +} ------------------------------------------------------------------------------ Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now! http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140 _______________________________________________ Opensaf-devel mailing list Opensaf-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/opensaf-devel