1 /*----------------------------------------------------------------------------
  2  Name:      xmlBlaster/src/c/util/Timeout.c
  3  Project:   xmlBlaster.org
  4  Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
  5  Comment:   C Timer with POSIX threads (XB_USE_PTHREADS)
  6  Compile:   gcc -Wall -g -o Timeout Timeout.c -DTIMEOUT_UTIL_MAIN -I..
  7  Testsuite: xmlBlaster/testsuite/src/c/TestUtil.c
  8  Author:    "Marcel Ruff" <xmlBlaster@marcelruff.info>
  9  -----------------------------------------------------------------------------*/
 10 
 11 #include <stdio.h>
 12 #include <stdlib.h>
 13 #include <stdarg.h>
 14 #include <string.h>
 15 #include <ctype.h>
 16 #include <time.h>
 17 #include "helper.h"
 18 #include "Timeout.h"
 19 #include "Timestampc.h"
 20 
 21 #ifdef _ENABLE_STACK_TRACE_
 22 # include <execinfo.h>
 23 #endif
 24 
 25 #ifdef _WINDOWS
 26 #    include <pthreads/pthread.h> /* Our pthreads.h: For logging output of thread ID, for Windows and WinCE downloaded from http://sources.redhat.com/pthreads-win32 */
 27 #else
 28 #    include <pthread.h>      /* The original pthreads.h from the OS */
 29 #endif
 30 
 31 static void *timeoutMainLoop(void *ptr);
 32 static int setTimeoutListener(Timeout * const timeout, TimeoutCbFp timeoutCbFp, const long int delay,
 33       void * const userData, void * const userData2);
 34 
 35 /* Local helper function */
 36 static bool _isMyThread(Timeout *timeout) {
 37     if (timeout == 0/* ||  timeout->threadId == 0*/)
 38        return false;
 39         return pthread_equal(pthread_self(), timeout->threadId) != 0;
 40 }
 41 
 42 /* Local helper function */
 43 static bool _isNull(pthread_t *threadId) {
 44         if (threadId == 0) {
 45                 return true;
 46         }
 47 #if defined(_WINDOWS)
 48    if (threadId->p == 0)
 49                 return true;
 50 #else
 51    if (*threadId == 0)
 52                 return true;
 53 #endif
 54         return false;
 55 }
 56 
 57 static void initTimeout(Timeout *timeout) {
 58    if (timeout == 0)
 59       return;
 60    memset(&timeout->threadId, 0, sizeof(pthread_t));
 61    timeout->ready = false;
 62    timeout->running = true;
 63    timeout->selfCleanup = false;
 64    pthread_mutex_init(&timeout->condition_mutex, NULL); /* int rc return is always 0 */
 65    pthread_cond_init(&timeout->condition_cond, NULL);
 66 }
 67 
 68 /**
 69  * Create an instance of a property struct.
 70  * @param name Thread name, a clone is kept
 71  */
 72 Timeout *createTimeout(const char* const name) {
 73    Timeout *timeout = (Timeout *) calloc(1, sizeof(Timeout));
 74    timeout->verbose = false;
 75    timeout->name = (name != 0) ? strcpyAlloc(name) : 0;
 76    timeout->setTimeoutListener = setTimeoutListener;
 77    initTimeout(timeout);
 78    return timeout;
 79 }
 80 
 81 static void stopThread(Timeout *timeout) {
 82    pthread_t threadId;
 83    if (timeout == 0)
 84       return;
 85    threadId = timeout->threadId;
 86    if (timeout == 0 || _isNull(&threadId))
 87       return;
 88    if (_isMyThread(timeout)) {
 89       /* to avoid memory leak on needs to call pthread_join() or pthread_detach() */
 90       pthread_detach(threadId);
 91       timeout->running = false;
 92       return;
 93    }
 94    pthread_mutex_lock(&timeout->condition_mutex);
 95    timeout->running = false;
 96    pthread_cond_broadcast(&timeout->condition_cond);
 97    pthread_mutex_unlock(&timeout->condition_mutex);
 98    if (!_isNull(&threadId))
 99       pthread_join(threadId, NULL);
100    if (timeout->verbose) printf("Timeout.c Joined threadId=%lud\n", get_pthread_id(threadId));
101    initTimeout(timeout);
102 }
103 
104 void freeTimeout(Timeout *timeout) {
105    if (timeout == 0)
106       return;
107    stopThread(timeout);
108    free((char *) timeout->name);
109    free(timeout);
110 }
111 
112 /**
113  * See header Timeout.h for documentation
114  * May not be call from within a timeout as this would destroy the thread during this call
115  */
116 static int setTimeoutListener(Timeout * const timeout, TimeoutCbFp timeoutCbFp,
117       const long int delay, void * const userData, void * const userData2) {
118    int iret;
119    int i;
120 
121    if (timeout == 0)
122       return -1;
123 
124    timeout->timeoutContainer.timeoutCbFp = timeoutCbFp;
125    timeout->timeoutContainer.delay = delay;
126    timeout->timeoutContainer.userData = userData;
127    timeout->timeoutContainer.userData2 = userData2;
128 
129    /* delay==0: cancel timer */
130    if (delay < 1) {
131       if (_isMyThread(timeout)) {
132          if (timeout->verbose)
133             printf("Timeout.c Calling setTimeoutListener from timer thread callback\n");
134          /*
135           The timeoutMainLoop called us here: it is at the end of the while loop
136           and like this the thread will die as soon as the user cb returns
137          */
138          timeout->selfCleanup = true;
139          timeout->running = false;
140       }
141       else {
142          /* Another thread called us, so clean up immediately */
143          if (timeout->verbose)
144             printf("Timeout.c Stopping timer %s threadId=%lud, callingThreadId=%lud\n", timeout->name, get_pthread_id(timeout->threadId), get_pthread_id(pthread_self()));
145          stopThread(timeout);
146       }
147       return 0;
148    }
149 
150    if (!_isNull(&timeout->threadId)) {
151       if (timeout->verbose)
152          printf("Timeout.c Warning: Calling setTimeoutListener twice is not reinitializing immediately the timer timeout time\n");
153       return -1;
154    }
155 
156    if (timeout->timeoutContainer.timeoutCbFp == 0) {
157       printf("Timeout.c Warning: calling setTimeoutListener with 0 callback pointer\n");
158    }
159 
160    /* pthread_attr.name before calling pthread_create() ? pthread_setname(timeout->name) pthread_attr_setname() */
161    iret = pthread_create(&timeout->threadId, NULL, timeoutMainLoop, (void*) timeout);
162 
163    /* Block until timer thread is ready */
164    for (i=0; i<50; i++) {
165       if (timeout->ready)
166          break;
167       sleepMillis(1);
168    }
169    if (i >= 50)
170       printf("Timeout.c Warning: calling setTimeoutListener is not getting ready\n");
171 
172    return iret; /* 0 == success */
173 }
174 
175 /**
176  * Run by the new created thread, calls the clients update method.
177  * Leaving this pthread start routine does an implicit pthread_exit().
178  * @param ptr Is Timeout * holding all necessary informations
179  * @return 0 on success, 1 on error. The return value is the exit value returned by pthread_join()
180  */
181 static void *timeoutMainLoop(void *ptr) {
182    Timeout *timeout = (Timeout *) ptr;
183    while (timeout->running) {
184       int64_t startNanos = getTimestamp();
185       struct timespec abstime;
186 
187       pthread_mutex_lock(&timeout->condition_mutex);
188 
189       timeout->ready = true;
190 
191       /* calculate absolute time from relative delay millis */
192       getAbsoluteTime(timeout->timeoutContainer.delay, &abstime);
193 
194       /* protect against spurious wake ups */
195       while (timeout->running) {
196          bool timeElapsed = false;
197          /*int ret
198                = */pthread_cond_timedwait(&timeout->condition_cond, &timeout->condition_mutex,
199                      &abstime);
200          /* check if delay reached */
201          timeElapsed = (getTimestamp() - startNanos) >= timeout->timeoutContainer.delay * 1000000L;
202          /* ret == 110 for timed wake up on Linux */
203          /* ret == 0 for signal wake up on Linux */
204          /*if (ret == ETIMEDOUT) { Not found on my Linux box?! */
205          if (timeElapsed)
206             break;
207       }
208 
209       pthread_mutex_unlock(&timeout->condition_mutex);
210 
211       if (!timeout->running)
212          break;
213 
214       /*sleepMillis(timeout->timeoutContainer.delay);*/
215 
216       if (timeout->timeoutContainer.timeoutCbFp != 0) {
217          /* Callback client, has registered to receive callback */
218          timeout->timeoutContainer.timeoutCbFp(timeout, timeout->timeoutContainer.userData,
219                timeout->timeoutContainer.userData2);
220       }
221    }
222 
223    if (timeout->selfCleanup) {
224       /* to avoid memory leak one needs to call pthread_join() or pthread_detach() */
225       pthread_detach(timeout->threadId);
226       initTimeout(timeout); /* Thread dies, reset timeout struct */
227    }
228    return 0;
229 }
230 
231 # ifdef TIMEOUT_UTIL_MAIN
232 /*
233  * gcc -g -Wall -pedantic -DTIMEOUT_UTIL_MAIN=1 -lpthread -I.. -o Timeout Timeout.c helper.c
234  */
235 static void onTimeout(Timeout *timeout, void *userData, void *userData2) {
236    const char *data = (char *) userData;
237    char timeStr[64];
238    printf("%s Timeout occurred, timer=%s delay=%ld userData=%s\n",
239          getCurrentTimeStr(timeStr, 64), timeout->name,
240          timeout->timeoutContainer.delay, data);
241 }
242 int main()
243 {
244    const long millisecs = 1000;
245    printf("millisec=%ld\n", millisecs);
246    {
247       Timeout *timeout = createTimeout("TestTimer");
248       timeout->setTimeoutListener(timeout, onTimeout, millisecs, "dummyData", 0);
249       while (getInputKey("Hit 'q' to quit") != 'q');
250       freeTimeout(timeout);
251       printf("Bye\n");
252    }
253    return 0;
254 }
255 # endif


syntax highlighted by Code2HTML, v. 0.9.1