summaryrefslogtreecommitdiffstats
path: root/tinySAK/src/tsk_condwait.c
blob: 08f9c89f1d30c5f2532415e9058583d1cc8ea854 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/*
* Copyright (C) 2009-2010 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
*	
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*	
* DOUBANGO 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.  See the
* GNU General Public License for more details.
*	
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/

/**@file tsk_condwait.c
 * @brief Pthread/Windows functions for waiting an signaling on condition variables.
 *
 * @author Mamadou Diop <diopmamadou(at)doubango.org>
 *
 * @date Created: Sat Nov 8 16:54:58 2009 mdiop
 */

#include "tsk_condwait.h"
#include "tsk_memory.h"
#include "tsk_debug.h"
#include "tsk_time.h"
#include <time.h>

#if TSK_UNDER_WINDOWS
#	include <windows.h>
#	include "tsk_errno.h"
#	define CONDWAIT_S void
	typedef HANDLE	CONDWAIT_T;
#	define TIMED_OUT	WAIT_TIMEOUT
#else
#	include <sys/time.h>
#	include <pthread.h>
#	define CONDWAIT_S pthread_cond_t
	typedef CONDWAIT_S* CONDWAIT_T;
#	define TIMED_OUT	ETIMEDOUT
#endif

#if defined(__GNUC__) || defined (__SYMBIAN32__)
#	include <errno.h>
#endif

/**@defgroup tsk_condwait_group Pthread/Windows functions for waiting and signaling on condition variables (conwait).
* @code
* tsk_condwait_handle_t *condwait = tsk_condwait_create();
* @endcode
*
* In thread-1:
* @code
* // Bock the current thread until the condition is opened or until 1000ms have passed.
* int ret = tsk_condwait_timedwait(condwait, 1000);
* @endcode
*
* In thread-2:
* @code
* // Wakes up
* int ret = tsk_condwait_signal(condwait);
* // or tsk_condwait_broadcast(condwait)
* @endcode
*
* To free the condwait object:
* @code
* tsk_condwait_destroy(&condwait);
* @endcode
*/

/**@ingroup tsk_condwait_group
* Represents both PThread an Windows condwait object.
*/
typedef struct tsk_condwait_s
{
	CONDWAIT_T pcond; /**< Pthread handle pointing to the internal condwait object. */
#if !TSK_UNDER_WINDOWS
	tsk_mutex_handle_t* mutex;  /**< Locker. */
#endif
}
tsk_condwait_t;

/**@ingroup tsk_condwait_group
* Creates new conwait handle. You MUST call @ref tsk_condwait_destroy to free the handle.
* @retval New condwait handle.
* @sa @ref tsk_condwait_destroy.
*/
tsk_condwait_handle_t* tsk_condwait_create()
{
	tsk_condwait_t *condwait = tsk_calloc(1, sizeof(tsk_condwait_t));

	if(condwait)
	{
#if TSK_UNDER_WINDOWS
		condwait->pcond = CreateEvent(NULL, TRUE, FALSE, NULL);
		if(!condwait->pcond)
		{
			TSK_FREE(condwait);
		}
#else
		condwait->pcond = (CONDWAIT_T)tsk_calloc(1, sizeof(CONDWAIT_S));
		if(pthread_cond_init(condwait->pcond, 0))
		{
			TSK_DEBUG_ERROR("Failed to initialize the new conwait.");
		}

		if(!(condwait->mutex = tsk_mutex_create()))
		{
			pthread_cond_destroy(condwait->pcond);

			TSK_FREE(condwait);
			TSK_DEBUG_ERROR("Failed to initialize the internal mutex.");
		}
#endif
	}

	if(!condwait)
	{
		TSK_DEBUG_ERROR("Failed to create new conwait.");
	}
	return condwait;
}

/**@ingroup tsk_condwait_group
* Block the current thread until the condition is opened. 
* @param handle CondWait handle created using @ref tsk_condwait_create.
* @retval Zero if succeed and non-zero otherwise.
* @sa @ref tsk_condwait_timedwait.
*/
int tsk_condwait_wait(tsk_condwait_handle_t* handle)
{
	int ret = EINVAL;
	tsk_condwait_t *condwait = (tsk_condwait_t*)handle;

#if TSK_UNDER_WINDOWS
	if((ret = (WaitForSingleObject(condwait->pcond, INFINITE) == WAIT_FAILED) ? -1 : 0)){
		TSK_DEBUG_ERROR("WaitForSingleObject function failed: %d", ret);
	}
#else
	if(condwait && condwait->mutex){
		tsk_mutex_lock(condwait->mutex);
		if(ret = pthread_cond_wait(condwait->pcond, (pthread_mutex_t*)condwait->mutex))
		{
			TSK_DEBUG_ERROR("pthread_cond_wait function failed: %d", ret);
		}
		tsk_mutex_unlock(condwait->mutex);
	}
#endif
	return ret;
}

/**@ingroup tsk_condwait_group
* Block the current thread until the condition is opened or until @a ms milliseconds have passed. 
* @param handle condwait handle created using @ref tsk_condwait_create.
* @param ms The number of milliseconds to wait for a given condition.
* @retval Zero if succeed and non-zero error code otherwise.
* @sa @ref tsk_condwait_wait.
*/
int tsk_condwait_timedwait(tsk_condwait_handle_t* handle, uint64_t ms)
{
#if TSK_UNDER_WINDOWS
	DWORD ret = WAIT_FAILED;
#else
	int ret = EINVAL;
#endif
	tsk_condwait_t *condwait = (tsk_condwait_t*)handle;

#if TSK_UNDER_WINDOWS
	if((ret = WaitForSingleObject(condwait->pcond, (DWORD)ms)) != WAIT_OBJECT_0)
	{
		if(ret == TIMED_OUT){
			/* TSK_DEBUG_INFO("WaitForSingleObject function timedout: %d", ret); */
		}
		else{
			TSK_DEBUG_ERROR("WaitForSingleObject function failed: %d", ret);
		}
		return ((ret == TIMED_OUT) ? 0 : ret);
	}
#else
	if(condwait && condwait->mutex)
	{
		struct timespec   ts = {0, 0};
		struct timeval    tv = {0, 0};
		/*int rc =*/  tsk_gettimeofday(&tv, 0);

		ts.tv_sec  = ( tv.tv_sec + ((long)ms/1000) );
		ts.tv_nsec += ( (tv.tv_usec * 1000) + ((long)ms % 1000 * 1000000) );
		if(ts.tv_nsec > 999999999) ts.tv_sec+=1, ts.tv_nsec = ts.tv_nsec % 1000000000;
		
		tsk_mutex_lock(condwait->mutex);
		if(ret = pthread_cond_timedwait(condwait->pcond, (pthread_mutex_t*)condwait->mutex, &ts)){
			if(ret == TIMED_OUT){
				/* TSK_DEBUG_INFO("pthread_cond_timedwait function timedout: %d", ret); */
			}
			else{
				TSK_DEBUG_ERROR("pthread_cond_timedwait function failed: %d", ret);
			}
		}

		tsk_mutex_unlock(condwait->mutex);

		return ((ret == TIMED_OUT) ? 0 : ret);
	}
#endif

	return ret;
}

/**@ingroup tsk_condwait_group
* Wakes up at least one thread that is currently waiting.
* @param handle CondWait handle created using @ref tsk_condwait_create.
* @retval Zero if succeed and non-zero otherwise.
* @sa @ref tsk_condwait_broadcast.
*/
int tsk_condwait_signal(tsk_condwait_handle_t* handle)
{
	int ret = EINVAL;
	tsk_condwait_t *condwait = (tsk_condwait_t*)handle;

#if TSK_UNDER_WINDOWS
	if(ret = ((SetEvent(condwait->pcond) && ResetEvent(condwait->pcond)) ? 0 : -1)){
		ret = GetLastError();
		TSK_DEBUG_ERROR("SetEvent/ResetEvent function failed: %d", ret);
	}
#else
	if(condwait && condwait->mutex){
		tsk_mutex_lock(condwait->mutex);

		if(ret = pthread_cond_signal(condwait->pcond))
		{
			TSK_DEBUG_ERROR("pthread_cond_signal function failed: %d", ret);
		}
		tsk_mutex_unlock(condwait->mutex);
	}
#endif
	return ret;
}


/**@ingroup tsk_condwait_group
* Wakes up all threads that are currently waiting for the condition.
* @param handle CondWait handle created using @ref tsk_condwait_create.
* @retval Zero if succeed and non-zero otherwise.
* @sa @ref tsk_condwait_signal.
*/
int tsk_condwait_broadcast(tsk_condwait_handle_t* handle)
{
	int ret = EINVAL;
	tsk_condwait_t *condwait = (tsk_condwait_t*)handle;

#if TSK_UNDER_WINDOWS
	if(ret = ((SetEvent(condwait->pcond) && ResetEvent(condwait->pcond)) ? 0 : -1))
	{
		ret = GetLastError();
		TSK_DEBUG_ERROR("SetEvent function failed: %d", ret);
	}
#else
	if(condwait && condwait->mutex)
	{
		tsk_mutex_lock(condwait->mutex);
		if(ret = pthread_cond_broadcast(condwait->pcond))
		{
			TSK_DEBUG_ERROR("pthread_cond_broadcast function failed: %d", ret);
		}
		tsk_mutex_unlock(condwait->mutex);
	}
#endif

	return ret;
}

/**@ingroup tsk_condwait_group
* Safely free a condwait variable previously created using @ref tsk_condwait_create.
* @param handle The condwait handle to free.
* @sa @ref tsk_condwait_create
*/
void tsk_condwait_destroy(tsk_condwait_handle_t** handle)
{
	tsk_condwait_t **condwait = (tsk_condwait_t**)handle;
	
	if(condwait && *condwait){
#if TSK_UNDER_WINDOWS
		CloseHandle((*condwait)->pcond);
#else
		tsk_mutex_destroy(&((*condwait)->mutex));
		pthread_cond_destroy((*condwait)->pcond);
		TSK_FREE((*condwait)->pcond);
#endif
		tsk_free((void**)condwait);
	}
	else{
		TSK_DEBUG_WARN("Cannot free an uninitialized condwait object");
	}
}

OpenPOWER on IntegriCloud