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